Quantcast
Channel: Planeta Código
Viewing all articles
Browse latest Browse all 2734

Variable not found: El operador virgulilla "~" en C#

$
0
0
.NET CoreDesde que apareció Roslyn, C# ha ido evolucionando a pasos agigantados. Tanto es así que es frecuente encontrar desarrolladores que, aunque lo usan a diario, desconocen todo su potencial porque la velocidad de introducción de cambios en el lenguaje es mayor que la de asimilación de novedades por parte de los profesionales que nos dedicamos a esto.

Por ejemplo, en las consultorías técnicas que realizo en empresas es frecuente encontrar equipos de trabajo en los que aún no está generalizado el uso de construcciones tan útiles como el null coalescing operator (fullName ?? "Anonymous"), safe navigation operator (person?.Address?.Street), el at object operator (Address@person), o características tan potentes como las funciones locales, interpolación de cadenas, tuplas o muchas otras.

Sin embargo, creo que el rey de los desconocidos es el operador virgulilla "~" de C#. Introducido con C#7 es probablemente uno de los operadores menos utilizados y, sin embargo, de los más potentes ofrecidos por el lenguaje.
Nota de traducción: el nombre original del operador es "tilde operator", y como he encontrado poca literatura al respecto en nuestro idioma, me he tomado la libertad de traducirlo como operador virgulilla (¡sí, esa palabra existe!). También, en entornos más informales lo encontraréis con el nombre "wormy operator" (operador gusanillo) o como "soft similarity operator" (que podríamos traducir como operador de similitud relajada).

El operador virgulilla

Originalmente el carácter "~" ya era utilizado desde los comienzos de C# para realizar la operación de complemento bit a bit, pero, dado su poco uso, en C#7 reescribieron el operador para alinear el lenguaje con las nuevas corrientes de popularización de los sistemas basados en inteligencia artificial.

Básicamente, el operador virgulilla retorna un valor booleano resultado de evaluar una comparación entre dos objetos o valores, pero, a diferencia de las comparaciones "tradicionales", utilizando criterios basados en lógica borrosa y aprendizaje automático o machine learning.

Creo que la mejor forma de entender rápidamente en qué consiste es viendo código, así que vamos con el primer ejemplo:
var n1 = 987654320;
var n2 = 987654321;

Console.WriteLine(n1 == n2); // n1 equals n2? --> false
Console.WriteLine(n1 ~ n2); // n1 is similar to n2? --> true!
Efectivamente, como podéis suponer, el operador ~ comprueba si los dos operandos son similares, retornando true en caso afirmativo.

Para ajustar en cada escenario el nivel de precisión que deseamos aplicar a las operaciones de comparación, C# permite encadenar hasta tres virgulillas para indicar que queremos aplicar un grado mayor de precisión en la evaluación de similitud:
var n1 = 987654320;
var n2 = 987654321;

Console.WriteLine(n1 == n2); // n1 is equals to n2? --> false
Console.WriteLine(n1 ~ n2); // n1 is similar to n2? --> true
Console.WriteLine(n1 ~~ n2); // n1 is very similar to n2? --> true
Console.WriteLine(n1 ~~~ n2); // n1 is very very similar to n2? --> false!
Observad que en muchos escenarios puede librarnos del típico off by one, uno de los errores más habituales en el mundo de la programación.
Los ejemplos anteriores pueden verse más o menos claros porque estamos comparando números. Pero la cosa va más allá; veamos otro ejemplo, usando la sobrecarga del operador para tipos string:
string s1 = "Hello, what is your name?";
string s2 = "Hello, wat is your name?";

Console.WriteLine(s1 == s2); // s1 equals s2? --> false
Console.WriteLine(s1 ~ s2); // s1 is similar to s2? --> true
Empleando bien esta característica podemos simplificar bastante las comparaciones de cadenas y, sobre todo, el tratamiento de valores nulos, vacíos o blancos, como en el siguiente ejemplo:
string strNull = null;
string strEmpty = "";
string strSpaces = "";

Console.WriteLine(strNull == strEmpty); // false
Console.WriteLine(strNull ~ strEmpty); // true
Console.WriteLine(strNull ~ strSpaces); // true
Console.WriteLine(strSpaces ~ null); // true: Bye bye, string.IsNullOrEmpty()!
Y como operador completo que es, también podemos utilizar composición de gusanillos para expresar comprobaciones complejas de forma más concisa:
Console.WriteLine(strNull ~ strEmpty ~ strSpaces);   // true

Comparaciones AI-Based

Este operador también tiene la capacidad de comparar valores de distinto tipo, y es donde encontramos probablemente su utilidad más espectacular porque con ello se ponen en marcha los mecanismos de inteligencia artificial integrados en el compilador. Fijaos en esto:
string strRed = "red";
string strRgbRed = "ff0000";
Color colorRed = Color.Red;
int intRgbRed = 0xff0000;

Console.WriteLine(strRed ~ strRgbRed ~ colorRed ~ intRgbRed); // true!!
Internamente, el sistema es capaz de detectar la similitud gracias a la siguiente cadena de decisiones:
  • Sabe que Color.Red es un miembro de un enum con un valor entero equivalente a 0xff0000.
  • Por esta razón, sabe que colorRed ~ intRgbRed.
  • También sabe que "ff0000" es la representación textual de 0xff0000, y teniendo en cuenta los pasos anteriores, deduce que strRgbRed ~ colorRed ~ intRgbRed.
  • El texto "red" se encuentra en el nombre de la constante Color.Red, por lo que ya puede concluir que strRed ~ strRgbRed ~ colorRed ~ intRgbRed.
Brutal, ¿eh?

Pues subamos la apuesta ;) La comparación de similitud ofrecida por el operador también está integrada con los sistemas NLP (procesamiento de lenguaje natural) incluidos en el compilador. Observad en el siguiente ejemplo, donde evaluamos la similitud de cadenas de caracteres y enteros utilizando comparación semántica:
using Microsoft.Extensions.Operators.Tilde.Nlp;
...
int numberTen = 10;
string strDiez = "diez";
string strDecena = "una decena";
string strDecimoDeCien = "la décima parte de cien";

Console.WriteLine(strDiez ~ numberTen ~ strDecena ~ strDecimoDeCien); // true!
Nota: para que esto funcione correctamente es necesario incluir el espacio de nombres Microsoft.Extensions.Operators.Tilde.Nlp.
Incluso podemos sacar partido fácilmente a la traducción en tiempo de compilación a distintos locales. Fijaos que en este caso son dos los namespaces los que debemos utilizar:
using Microsoft.Extensions.Operators.Tilde.Nlp;
using Microsoft.Extensions.Operators.Tilde.Nlp.I18N;
...
string strTen = "ten";
string strDiez = "diez";

Console.WriteLine(strTen ~ strDiez); // true; usando auto-locale
Console.WriteLine(strDiez ~en-us~ strTen); // true; forzando el locale en-us
Console.WriteLine(strDiez ~fr-fr~ strTen); // false (el locale forzado es incorrecto)

Activación en Visual Studio 2017 u otros IDE

Sin duda, este operador es uno de los grandes desconocidos por la controvertida decisión de Microsoft de hacer que éste sólo esté disponible cuando es habilitado explícitamente en el entorno de desarrollo. Es decir, no podemos utilizarlo en nuestro código hasta que lo activemos expresamente en el proyecto.

Para conseguirlo en Visual Studio 2017, basta con acudir a las propiedades del proyecto, pestaña "Build", clicar el botón "Avanzado" y asegurarse de que está marcado Enable similarity operator, como se muestra en la siguiente captura de pantalla:


Activación del operador virgulilla en Visual Studio 2017
Si no utilizamos Visual Studio, también podemos acudir al archivo .csproj del proyecto e insertar manualmente las siguientes líneas (es posible que requiera reiniciar VS tras hacerlo):
<PropertyGroup>
<LanguageVersion>Latest</AssemblyName>
<EnableSimilarityOperator>true</EnableSimilarityOperator>
</PropertyGroup>

¿Y está disponible este operador en Visual Basic .NET?

Nota: Este punto no estaba inicialmente en el post, pero lo he actualizado para responder a la pregunta que me hacía @visualbutnotbasic vía Twitter nada más publicarlo.
Pues aún no, pero lo estará pronto. Según Victor Bellino, principal program manager en Redmond, se está trabajando duro para llevar este operador a Visual Basic, aunque probablemente bajo otra denominación para alinearlo sintácticamente con las construcciones habituales de este lenguaje:
If x Is Similar To y And Similar To z Then
...
End If

En conclusión

Desconozco las razones que han llevado a Microsoft a mantener el operador virgulilla semioculto tras opciones de compilación, y a no haberlo publicitado más. Probablemente existan reticencias por que, como todo gran poder, su uso exige una gran responsabilidad y es posible que piensen que los desarrolladores no estamos aún preparados para asumirla.

Pero en cualquier caso, sin duda, el operador virgulilla es una de las incorporaciones al lenguaje C# más interesantes de los últimos años, pues nos abre la puerta a la construcción de sistemas más fiables, inteligentes y flexibles.

El operador virgulilla es a la inteligencia artificial lo que LINQ fue en su momento a las bases de datos: una integración dentro del lenguaje de funcionalidades que tradicionalmente pertenecían a un mundo aparte. De ahí que podamos considerarlo como una herramienta totalmente disruptiva para los desarrolladores.

Publicado en ~Varible not fund.

Viewing all articles
Browse latest Browse all 2734