Articulo de referencia

Comprensión de listas

La comprensión de listas es una construcción sintáctica disponible en algunos lenguajes de programación para crear listas a partir de listas existentes. Sigue la notación matemá...

La comprensión de listas es una construcción sintáctica disponible en algunos lenguajes de programación para crear listas a partir de listas existentes. Sigue la notación matemática de conjuntos ( comprensión de conjuntos ), a diferencia del uso de las funciones map y filter .

Descripción general

Consideremos el siguiente ejemplo en notación matemática de construcción de conjuntos .

S={2incógnitaincógnitanorte, incógnita2>3}{\displaystyle S=\{2\cdot x\mid x\in \mathbb {N} ,\ x^{2}>3\}}

o a menudo

S={2incógnita:incógnitanorte, incógnita2>3}{\displaystyle S=\{2\cdot x:x\in \mathbb {N} ,\ x^{2}>3\}}

Esto se puede leer, "S{\displaystyle S}es el conjunto de todos los números "2 veces"incógnita{\displaystyle x}"TAL COMO QUEincógnita{\displaystyle x}es un ELEMENTO o MIEMBRO del conjunto de los números naturales (norte{\displaystyle \mathbb {N} }), Yincógnita{\displaystyle x}al cuadrado es mayor que3{\displaystyle 3}"

El número natural más pequeño, x = 1, no satisface la condición x² > 3 (la condición ​​> 3 es falsa), por lo que 2 · 1 no está incluido en S. El siguiente número natural, 2, sí satisface la condición (2² > 3), al igual que todos los demás números naturales. Por lo tanto, x consta de 2, 3, 4, 5... Dado que el conjunto S consta de todos los números "2 veces x", está dado por S = {4, 6, 8, 10,...}. S es, en otras palabras, el conjunto de todos los números pares mayores que 2.

En esta versión anotada del ejemplo:

S={2incógnitaexpresión de salidaincógnitavariablenorteconjunto de entrada, incógnita2>3predicado}{\displaystyle S=\{\underbrace {2\cdot x} _{\color {Violet}{\text{expresión de salida}}}\mid \underbrace {x} _{\color {Violet}{\text{variable}}}\in \underbrace {\mathbb {N} } _{\color {Violet}{\text{conjunto de entrada}}},\ \underbrace {x^{2}>3} _{\color {Violet}{\text{predicado}}}\}}
  • incógnita{\displaystyle x}es la variable que representa a los miembros de un conjunto de entrada.
  • norte{\displaystyle \mathbb {N} }representa el conjunto de entrada, que en este ejemplo es el conjunto de números naturales.
  • incógnita2>3{\displaystyle x^{2}>3}es una expresión predicada que actúa como un filtro sobre los miembros del conjunto de entrada.
  • 2incógnita{\displaystyle 2\cdot x}es una expresión de salida que produce miembros del nuevo conjunto a partir de miembros del conjunto de entrada que satisfacen la expresión predicativa.
  • {}{\displaystyle \{\}}Las llaves indican que el resultado es un conjunto.
  • {\displaystyle \mid },{\displaystyle ,}La barra vertical se lee como "SUMAMENTE QUE". La barra y los dos puntos ":" se usan indistintamente.
  • Las comas separan los predicados y pueden leerse como "Y".

Una comprensión de lista tiene los mismos componentes sintácticos para representar la generación de una lista en orden a partir de una lista de entrada o iterador :

  • Una variable que representa los miembros de una lista de entrada.
  • Una lista de entrada (o iterador).
  • Una expresión predicada opcional.
  • Y una expresión de salida que produce miembros de la lista de salida a partir de miembros del iterable de entrada que satisfacen el predicado.

El orden de generación de los elementos de la lista de salida se basa en el orden de los elementos de la entrada.

En la sintaxis de comprensión de listas de Haskell , esta construcción de conjunto se escribiría de manera similar, como:

s = [ 2 * x | x <- [ 0 .. ], x ^ 2 > 3 ]

Aquí, la lista [0..]representanorte{\displaystyle \mathbb {N} }, x^2>3representa el predicado, y 2*xrepresenta la expresión de salida.

Las comprensiones de listas dan resultados en un orden definido (a diferencia de los miembros de los conjuntos); y pueden generar los miembros de una lista en orden, en lugar de producir la lista completa, lo que permite, por ejemplo, la definición anterior de Haskell de los miembros de una lista infinita.

Historia

La existencia de construcciones relacionadas es anterior al uso del término "comprensión de listas". El lenguaje de programación SETL (1969) tiene una construcción de formación de conjuntos que es similar a las comprensiones de listas. Por ejemplo, este código imprime todos los números primos del 2 al N :

print([n en [2..N] | ∀ m en {2..n - 1} | n mod m > 0]);

El sistema de álgebra computacional Axiom (1973) tiene una construcción similar que procesa flujos .

El primer uso del término "comprensión" para tales construcciones se encuentra en la descripción que Rod Burstall y John Darlington hicieron de su lenguaje de programación funcional NPL en 1977. En su retrospectiva "Some History of Functional Programming Languages" [ 1 ] , David Turner recuerda:

NPL fue implementado en POP2 por Burstall y utilizado para el trabajo de Darlington sobre transformación de programas (Burstall y Darlington 1977). El lenguaje era de primer orden, fuertemente tipado (pero no polimórficamente ), puramente funcional , paso por valor. También tenía "expresiones de conjuntos", por ejemplo,

setofeven (X) <= <:x : x in X & even(x):>}}

En una nota a pie de página adjunta al término "comprensión de listas", Turner también señala:

Inicialmente las llamé expresiones ZF , en referencia a la teoría de conjuntos de Zermelo-Fraenkel ; fue Phil Wadler quien acuñó el término más adecuado de comprensión de listas .

El trabajo de Burstall y Darlington con NPL influyó en muchos lenguajes de programación funcional durante la década de 1980, pero no todos incluían comprensiones de listas. Una excepción fue Miranda , el influyente lenguaje de programación funcional puro y perezoso de Turner , publicado en 1985. El lenguaje funcional puro y perezoso estándar Haskell, desarrollado posteriormente , incluye muchas de las características de Miranda, incluidas las comprensiones de listas.

Las comprensiones se propusieron como una notación de consulta para bases de datos [ 2 ] y se implementaron en el lenguaje de consulta de bases de datos Kleisli . [ 3 ]

Ejemplos en diferentes lenguajes de programación

Construcciones similares

Comprensión de mónadas

In Haskell, a monad comprehension is a generalization of the list comprehension to other monads in functional programming.

Set comprehension

The Python language introduces syntax for set comprehensions starting in version 2.7. Similar in form to list comprehensions, set comprehensions generate Python sets instead of lists.

s:set[str]={vforvin"ABCDABCD"ifvnotin"CB"}print(s)# prints {'A', 'D'}print(type(s))# prints <class 'set'>

Racket set comprehensions generate Racket sets instead of lists.

(for/set([v"ABCDABCD"]#:unless(memberv(string->list"CB")))v))

Dictionary comprehension

The Python language introduced a new syntax for dictionary comprehensions in version 2.7, similar in form to list comprehensions but which generate Python dicts instead of lists.

s:dict[str]={key:valforkey,valinenumerate("ABCD")ifvalnotin"CB"}print(s)# prints {0: 'A', 3: 'D'}

Racket hash table comprehensions generate Racket hash tables (one implementation of the Racket dictionary type).

(for/hash([(valkey)(in-indexed"ABCD")]#:unless(memberval(string->list"CB")))(valueskeyval))

Parallel list comprehension

El compilador Glasgow Haskell tiene una extensión llamada comprensión de listas paralela (también llamada comprensión zip ) que permite múltiples ramas independientes de calificadores dentro de la sintaxis de comprensión de listas. Mientras que los calificadores separados por comas son dependientes ("anidados"), las ramas de calificadores separadas por tuberías se evalúan en paralelo (esto no se refiere a ninguna forma de multihilo: simplemente significa que las ramas están combinadas ).

-- comprensión de lista regular a = [( x , y ) | x <- [ 1 .. 5 ], y <- [ 3 .. 5 ]] -- [(1,3),(1,4),(1,5),(2,3),(2,4) ...-- comprensión de lista comprimida b = [( x , y ) | ( x , y ) <- zip [ 1 .. 5 ] [ 3 .. 5 ]] -- [(1,3),(2,4),(3,5)]-- comprensión de lista paralela c = [( x , y ) | x <- [ 1 .. 5 ] | y <- [ 3 .. 5 ]] -- [(1,3),(2,4),(3,5)]

La biblioteca estándar de comprensiones de Racket contiene versiones paralelas y anidadas de sus comprensiones, que se distinguen por el uso de "for" o "for*" en el nombre. Por ejemplo, las comprensiones de vectores "for/vector" y "for*/vector" crean vectores mediante iteración paralela o anidada sobre secuencias. A continuación se muestra el código Racket para los ejemplos de comprensión de listas de Haskell.

> ( para*/lista ([ x ( en rango 1 6 )] [ y ( en rango 3 6 )]) ( lista x y )) ' (( 1 3 ) ( 1 4 ) ( 1 5 ) ( 2 3 ) ( 2 4 ) ( 2 5 ) ( 3 3 ) ( 3 4 ) ( 3 5 ) ( 4 3 ) ( 4 4 ) ( 4 5 ) ( 5 3 ) ( 5 4 ) ( 5 5 )) > ( para/lista ([ x ( en rango 1 6 )] [ y ( en rango 3 6 )]) ( lista x y )) ' (( 1 3 ) ( 2 4 ) ( 3 5 ))

En Python, podríamos hacerlo de la siguiente manera:

# Comprensión de lista regular a : lista [ tupla [ int , int ]] = [( x , y ) para x en rango ( 1 , 6 ) para y en rango ( 3 , 6 )] print ( a ) # imprime [(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), ... # Comprensión de lista paralela/comprimida b : lista [ tupla [ int , int ]] = [ x para x en zip ( rango ( 1 , 6 ), rango ( 3 , 6 ))] print ( b ) # imprime [(1, 3), (2, 4), (3, 5)]

En Julia, prácticamente se pueden obtener los mismos resultados de la siguiente manera:

# comprensión de matriz regular a :: Vector { Tuple { Int , Int }} = [( x , y ) for x in 1 : 5 for y in 3 : 5 ]# comprensión de matriz paralela/comprimida b :: Vector { Tuple { Int , Int }} = [ x for x in zip ( 1 : 3 , 3 : 5 )]

con la única diferencia de que, en lugar de listas, en Julia tenemos arreglos.

XQuery y XPath

Al igual que el uso original de NPL, estos son fundamentalmente lenguajes de acceso a bases de datos.

Esto hace que el concepto de comprensión sea más importante, porque es computacionalmente inviable recuperar la lista completa y operar sobre ella (la "lista completa" inicial puede ser una base de datos completa en lenguaje de marcado extensible ( XML )).

En XPath, la expresión:

/ biblioteca / libro // párrafo [ @style = 'first-in-chapter' ]

se evalúa conceptualmente como una serie de "pasos" donde cada paso produce una lista y el siguiente paso aplica una función de filtro a cada elemento en la salida del paso anterior. [ 4 ]

En XQuery, está disponible la expresión XPath completa, pero también se utilizan las sentencias FLWOR , que constituyen una estructura de comprensión más potente. [ 5 ]

para $ b en // libro donde $ b [ @pages < 400 ] ordenar por $ b // título devolver <shortBook> <title> { $ b // título } </title> <firstPara> {( $ libro // párrafo )[ 1 ]} </firstPara> </shortBook>

Aquí, la expresión XPath //book se evalúa para crear una secuencia (también conocida como lista); la cláusula where es un "filtro" funcional, la cláusula order by ordena el resultado y el fragmento XML es en realidad una función anónima que construye/transforma XML para cada elemento de la secuencia utilizando el enfoque de "mapa" que se encuentra en otros lenguajes funcionales.<shortBook>...</shortBook>

Así pues, en otro lenguaje funcional la instrucción FLWOR anterior puede implementarse de esta manera:

map ( newXML ( shortBook , newXML ( title , $ 1. title ), newXML ( firstPara , $ 1...)) filter ( lt ( $ 1. pages , 400 ), xpath (// book ) ) )

LINQ en C#

C# 3.0 cuenta con un conjunto de características relacionadas llamadas Language Integrated Query (LINQ), que definen un conjunto de operadores de consulta para manipular enumeraciones de objetos .

using System.Collections.Generic ; using System.Linq ;IEnumerable < int > s = Enumerable . Range ( 0 , 100 ). Where ( x => x * x > 3 ). Select ( x => x * 2 );

También ofrece una sintaxis de comprensión alternativa, que recuerda al lenguaje de consulta estructurado ( SQL ):

using System.Collections.Generic ; using System.Linq ;IEnumerable < int > s = from x in Enumerable . Range ( 0 , 100 ) where x * x > 3 select x * 2 ;

LINQ ofrece una funcionalidad superior a las implementaciones típicas de comprensión de listas. Cuando el objeto raíz de la comprensión implementa la IQueryableinterfaz, en lugar de simplemente ejecutar los métodos encadenados de la comprensión, toda la secuencia de comandos se convierte en un objeto de árbol de sintaxis abstracta (AST), que se pasa al objeto IQueryable para su interpretación y ejecución.

Esto permite muchas cosas, entre ellas que IQueryable pueda:

  • Reescribe una comprensión incompatible o ineficiente.
  • Traduzca el AST a otro lenguaje de consulta (por ejemplo, SQL) para ejecutarlo.

C++

C++ no cuenta con características de lenguaje que soporten directamente las comprensiones de listas, pero la sobrecarga de operadores (por ejemplo, sobrecargar |, >>, >>=) se ha utilizado para proporcionar una sintaxis expresiva para lenguajes específicos de dominio (DSL) de consulta "integrados". Alternativamente, las comprensiones de listas se pueden construir utilizando el patrón de borrado-eliminación para seleccionar elementos en un contenedor y el algoritmo STL for_eachpara transformarlos.

Históricamente, el <algorithm>encabezado contenía únicamente algoritmos basados ​​en iteradores sobre un rango de elementos. [ 6 ] Posteriormente, esto se amplió en C++20 con la adición de algoritmos restringidos (en el espacio de nombres std::ranges) a dicho encabezado, que en lugar de operar sobre iteradores, operan sobre un rango. [ 7 ]

importar std ;usando std :: vector ;plantilla < typename Collection , typename Pred , typename Trans > Collection comprende ( Collection && source , const Pred & predicate , const Trans & transformation ) { // inicializar destino Collection d = std :: forward < Collection > ( source );// filtrar elementos d . erase ( std :: ranges :: remove_if ( d , predicate ), d . end ());// aplicar transformación std :: ranges :: for_each ( d , transformation );devolver d ; }int main ( int argc , char * argv []) { vector < int > range ( 10 ); // range es una lista de 10 elementos, todos cero std :: ranges :: iota ( range , 1 ); // range ahora contiene 1, 2, ..., 10vector < int > resultado = comprender ( rango , []( int x ) -> bool { return x * x <= 3 ; }, []( int & x ) -> void { x *= 2 ; } ); // el resultado ahora contiene 4, 6, ..., 20 }

<ranges>En C++20, se agregaron encabezados adicionales, como , que presentan algoritmos de rango componibles y vistas evaluadas de forma diferida sobre cualquier rango. Usando la std::ranges::viewsbiblioteca (también abreviada como std::views) [ 8 ] , esto se puede escribir como:

using std :: vector ; using std :: ranges :: to ; using std :: views :: filter ; using std :: views :: transform ;vector < int > range ( 10 ); // range es una lista de 10 elementos, todos cero std :: ranges :: iota ( range , 1 ); // range ahora contiene 1, 2, ..., 10vector < int > resultado = rango | filtro ([]( int x ) -> bool { return x * x > 3 ; }) | transformar ([]( int x ) -> int { return x * 2 ; }) | a < vector > ();

Java

La API Streams introducida en Java 8 introdujo un objeto similar a un rango evaluado de forma diferida, llamado stream. Cualquier tipo que implemente la java.util.stream.Streaminterfaz puede usarse con streams. [ 9 ] Estos pueden ser recolectados por toArray()o toList(), que acumula los elementos en un Object[]o List<T>.

import java.util.List ;List < Integer > numbers = List . of ( 1 , 2 , 3 , 4 , 5 ); List < Integer > doubledEven = numbers . stream () . filter ( x -> x % 2 == 0 ) . map ( x -> x * 2 ) . toList ();System.out.println ( doubledEven ) ; // [ 4 , 8 ]

Óxido

Rust tiene iteradores, que se evalúan de forma diferida. Cualquier tipo que implemente el std::iter::Iteratorrasgo puede usarse con iteradores. [ 10 ] Al usar los métodos, se crea una cadena de adaptadores de iteradores, que finalmente se recopilan collect()en una colección real (normalmente Vec<T>).

let numbers = vec! [ 1 , 2 , 3 , 4 , 5 ]; let doubled_even : Vec < i32 > = numbers . iter () . filter ( |& x | x % 2 == 0 ) . map ( | x | x * 2 ) . collect ();println! ( "{:?}" , doubled_even ); // [4, 8]

Véase también

Notas y referencias

  1. Turner, David (2012). "Alguna historia de los lenguajes de programación funcional" (PDF) . Simposio Internacional sobre Tendencias en Programación Funcional . Berlín, Heidelberg: Springer . págs. 1–20 . 
  2. Comprensiones, una notación de consulta para DBPL
  3. El funcionamiento interno del sistema de consultas Kleisli
  4. "2.1 Pasos de ubicación" . Lenguaje de ruta XML (XPath) . W3C . 16 de noviembre de 1999. Archivado del original el 9 de diciembre de 2012. Recuperado el 24 de diciembre de 2008 .
  5. "Expresiones FLWOR de XQuery" . W3Schools . Archivado del original el 8 de octubre de 2011.
  6. cppreference.com. "Encabezado de la biblioteca estándar <algoritmo>" . cppreference.com . cppreference.com . Consultado el 9 de mayo de 2026 .
  7. cppreference.com. "Algoritmos restringidos" . cppreference.com . cppreference.com . Consultado el 9 de mayo de 2026 .
  8. cppreference.com. "Biblioteca de rangos (desde C++20)" . cppreference.com . Consultado el 9 de mayo de 2026 .
  9. Oracle Corporation. "Interface Stream<T>" . docs.oracle.com . Oracle Corporation.
  10. El equipo de Rust (14 de abril de 2026). "Trait Iterator" . El equipo de Rust.
  • Comprensión de listas en el Diccionario en línea gratuito de informática, editor Denis Howe.
  • Wadler, Philip (1990). "Comprensión de las mónadas" . Actas de la Conferencia ACM de 1990 sobre LISP y programación funcional . Excelente.
  • Operaciones de conjuntos similares a SQL con comprensiones de listas en una sola línea en el libro de recetas de Python.
  • Discusión sobre comprensiones de listas en Scheme y construcciones relacionadas.
  • Comprensión de listas en diferentes idiomas

Axioma

  • Ejemplos de flujos de Axiom

Clojure

  • Documentación de la API de Clojure - para macro

Lisp común

  • Implementación de una macro de comprensión de Lisp por Guy Lapalme

Haskell

  • El informe Haskell 98, capítulo 3.11 Comprensión de listas .
  • Guía del usuario del sistema de compilación Haskell Glorious Glasgow, capítulo 7.3.4 Comprensiones de listas paralelas .
  • Guía del usuario de Hugs 98, capítulo 5.1.2 Comprensiones de listas paralelas (también conocidas como comprensiones zip) .

OCaml

  • OCaml (baterías incluidas)
  • Extensiones de lenguaje introducidas en OCaml Batteries Included Archivado el 3 de marzo de 2016 en Wayback Machine

Pitón

  • Tutorial de Python: Comprensión de listas .
  • Referencia del lenguaje Python, muestra listas .
  • Propuesta de mejora de Python PEP 202: Comprensión de listas .
  • Referencia del lenguaje Python, expresiones generadoras .
  • Propuesta de mejora de Python PEP 289: Expresiones generadoras .