{"id":1607,"date":"2023-12-18T14:51:53","date_gmt":"2023-12-18T13:51:53","guid":{"rendered":"https:\/\/intsight.com\/?p=1607"},"modified":"2023-12-25T16:28:20","modified_gmt":"2023-12-25T15:28:20","slug":"list-comprehensions-in-austra","status":"publish","type":"post","link":"https:\/\/intsight.com\/index.php\/2023\/12\/18\/list-comprehensions-in-austra\/","title":{"rendered":"List comprehensions in Austra"},"content":{"rendered":"<p><span style=\"font-variant: small-caps; font-size: 107%\">Hay quien traduce<\/span> el t\u00e9rmino ingl\u00e9s <em>list comprehension<\/em> literalmente como \u00abcomprensi\u00f3n de listas\u00bb o, a\u00fan peor, \u00ablistas de comprensi\u00f3n\u00bb. Lo interesante es que <a href=\"https:\/\/en.wikipedia.org\/wiki\/David_Turner_(computer_scientist)\" rel=\"noopener\" target=\"_blank\">David Turner<\/a>, el autor del lenguaje funcional <a href=\"https:\/\/en.wikipedia.org\/wiki\/Miranda_(programming_language)\" rel=\"noopener\" target=\"_blank\">Miranda<\/a>, llam\u00f3 inicialmente a estas expresiones <em>Zermelo-Frankel expressions<\/em>, pero alguien lo convenci\u00f3 para llamarlas <em>list comprehensions<\/em>, que en ingl\u00e9s no suena tan mal. Yo, porque soy un tipo caprichoso, cuando traduzca el t\u00e9rmino en el contexto de Austra, las llamar\u00e9 <em>constructores de secuencias<\/em>, a secas. Menos pretencioso, y m\u00e1s comprensible, creo.<\/p>\n<h4>Un truco para escribir menos<\/h4>\n<p>\u00bfQue es un <em>constructor de secuencias<\/em>? Pues es un truco sencillo para eso mismo: construir secuencias, pero usando menos c\u00f3digo, con el a\u00f1adido de que el resultado es normalmente m\u00e1s sencillo de leer (no siempre). Imagine que tenemos un vector de n\u00fameros reales, y queremos quedarnos con los que son enteros divisibles por dos, para elevarlos al cuadrado. En Austra, hasta ahora, har\u00edamos esto, suponiendo que tenemos el vector ya almacenado en una variable global <code>v<\/code>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-linenumbers=\"false\">\nv.filter(x =&gt; x % 2 = 0).map(x =&gt; x^2)\n<\/pre>\n<p>Con el mecanismo nuevo de construcci\u00f3n de secuencias, la expresi\u00f3n anterior se reducir\u00eda a esto:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-linenumbers=\"false\">\n[x in v : x % 2 = 0 =&gt; x^2]\n<\/pre>\n<p>No hace falta contar caracteres para ver que realmente hemos escrito menos. Nos hemos ahorrado los par\u00e9ntesis de los m\u00e9todos, y los par\u00e1metros de las funciones lambdas se han reducido a uno solo, reflejando el hecho de que los valores que fluyen por el constructor son casi siempre (casi) del mismo tipo. El resultado del constructor, en este caso, es un vector, y puedo seguir aplicando m\u00e9todos y operadores tras el corchete de cierre. Por ejemplo, puedo hacer algo algo est\u00fapido como a\u00f1adir uno a cada valor (lo pod\u00eda haber hecho al elevar al cuadrado):<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-linenumbers=\"false\">\n([x in v : x % 2 = 0 =&gt; x^2] + 1).plot\n<\/pre>\n<p>Pero tambi\u00e9n pod\u00eda haber transformado el vector resultante con una matriz, o cualquier otra cosa posible con un vector.<\/p>\n<p>Tambi\u00e9n pod\u00eda haber creado una secuencia en el constructor, aunque los datos viniesen de un vector, o de una matriz:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-linenumbers=\"false\">\n[x in seq(v) : x % 2 = 0 =&gt; x^2];\n[x in seq(v1^v2) : x % 2 = 0 =&gt; x^2];\n[x in iseq(1, 100) : x % 2 = 0 =&gt; x^2];\n[x in 1..100 : x % 2 = 0 =&gt; x^2];\n<\/pre>\n<p>El primer ejemplo usa una secuencia basada en un vector. El segundo, una secuencia basada en una matriz que se genera a partir de dos vectores. En el tercero, simplemente uso una secuencia de enteros construida a partir de un range. Y en el \u00faltimo ejemplo uso m\u00e1s \u00absyntatic sugar\u00bb para generar la secuencia directamente a partir de un rango.<\/p>\n<h4>Ojo con las series<\/h4>\n<p>Con las series, hay que tener un poco de cuidado, porque el m\u00e9todo que filtra una serie recibe como par\u00e1metro una lambda del tipo <code>Func&lt;Point&lt;Date&gt; bool&gt;<\/code>, mientras que el m\u00e9todo <code>Map<\/code> usa una de tipo <code>Func&lt;double, double&gt;<\/code>. El constructor de secuencias lo tiene en cuenta, pero tenemos que recordarlo:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-linenumbers=\"false\">\nlet mean = MSFT.mean in\n    [x in MSFT : x.date &gt;= jan2020 and x.value &gt= mean];\n<\/pre>\n<p>Observe que el filtro utiliza tanto la fecha como el valor de los puntos de la serie. Adem\u00e1s, he omitido la transformaci\u00f3n. Podemos omitir el filtro, la transformaci\u00f3n (o proyecci\u00f3n, en terminolog\u00eda SQL y C#) o incluso ambos.<\/p>\n<h4>Cuantificadores l\u00f3gicos<\/h4>\n<p>De todas maneras podemos ir un poco m\u00e1s lejos que Python y Haskell. Vamos a comenzar por algo sencillo. \u00bfEs el 97 un n\u00famero primo? Vamos a preguntarlo usando constructores de listas:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-linenumbers=\"false\">\n[all x in 2..96: 97 % x != 0]\n<\/pre>\n<p>La expresi\u00f3n anterior no devuelve una lista, sino un valor de tipo l\u00f3gico. Ser\u00e1 verdadero si alguno de los n\u00fameros entre 2 y 96 divide al n\u00famero 97. Podr\u00edamos haber usado una cota superior m\u00e1s baja, por supuesto, pero no quiero complicar la explicaci\u00f3n con detalles innecesarios.<\/p>\n<p><code>all<\/code> y <code>any<\/code>, en AUSTRA, no son palabras reservadas, pero en este caso se consideran palabras reservadas contextuales. La expresi\u00f3n anterior es equivalente a esta otra:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-linenumbers=\"false\">\niseq(2..96).all(x =&gt; 97 % x != 0)\n<\/pre>\n<p>Esto, naturalmente, aunque sea ligeramente interesante, es s\u00f3lo un rodeo hacia nuestro objetivo. \u00bfQu\u00e9 tal si quiero todos los n\u00fameros primos del 2 al 1000?<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-linenumbers=\"false\">\n[y in 2..1000 : all x in 2..96: y % x != 0]\n<\/pre>\n<p>La presencia de dos caracteres <code>:<\/code> nos est\u00e1 indicando que hay dos expresiones entre los corchetes. De hecho, estamos usando una funci\u00f3n lambda anidada dentro de otra, y la m\u00e1s interna est\u00e1 \u00abcapturando\u00bb el par\u00e1metro de la m\u00e1s externa:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-linenumbers=\"false\">\niseq(2, 1000).filter(y => iseq(2, y - 1).all(x => y % x != 0))\n<\/pre>\n<p>Y si quisi\u00e9ramos elevar cada primo al cuadrado, a\u00f1adir\u00edamos una funci\u00f3n de proyecci\u00f3n al engendro que hemos creado:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-linenumbers=\"false\">\n[y in 2..1000 : all x in 2..96: y % x != 0 => y^2]\n<\/pre>\n<p>\u00a1Ch\u00fapate esa, Python&#8230;!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hay quien traduce el t\u00e9rmino ingl\u00e9s list comprehension literalmente como \u00abcomprensi\u00f3n de listas\u00bb o, a\u00fan peor, \u00ablistas de comprensi\u00f3n\u00bb. Lo interesante es que David Turner, el autor del lenguaje funcional Miranda, llam\u00f3 inicialmente a estas expresiones Zermelo-Frankel expressions, pero alguien lo convenci\u00f3 para llamarlas list comprehensions, que en ingl\u00e9s no suena tan mal. Yo, porque [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1608,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[92],"tags":[102,90,91,101,89],"class_list":["post-1607","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-austra","tag-builders","tag-functional-programming","tag-lambda","tag-list-comprehension","tag-sequence"],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/intsight.com\/wp-content\/uploads\/2023\/12\/newtonCastle.png?fit=480%2C480&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/posts\/1607","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/comments?post=1607"}],"version-history":[{"count":18,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/posts\/1607\/revisions"}],"predecessor-version":[{"id":1626,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/posts\/1607\/revisions\/1626"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/media\/1608"}],"wp:attachment":[{"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/media?parent=1607"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/categories?post=1607"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/intsight.com\/index.php\/wp-json\/wp\/v2\/tags?post=1607"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}