Elm Language
Listas e iteración
Buscar..
Observaciones
La List ( lista enlazada ) brilla en el acceso secuencial :
- accediendo al primer elemento
- antepuesto al frente de la lista
- borrar de la parte frontal de la lista
Por otro lado, no es ideal para el acceso aleatorio (es decir, obtener el elemento nth) y el desplazamiento en orden inverso , y es posible que tenga más suerte (y rendimiento) con la estructura de datos de Array .
Creando una lista por rango
Antes de 0.18.0 puedes crear rangos como este:
> range = [1..5]
[1,2,3,4,5] : List number
>
> negative = [-5..3]
[-5,-4,-3,-2,-1,0,1,2,3] : List number
En 0.18.0 La sintaxis [1..5] ha sido eliminada .
> range = List.range 1 5
[1,2,3,4,5] : List number
>
> negative = List.range -5 3
[-5,-4,-3,-2,-1,0,1,2,3] : List number
Los rangos creados por esta sintaxis son siempre inclusivos y el paso es siempre 1 .
Creando una lista
> listOfNumbers = [1,4,99]
[1,4,99] : List number
>
> listOfStrings = ["Hello","World"]
["Hello","World"] : List String
>
> emptyList = [] -- can be anything, we don't know yet
[] : List a
>
Bajo el capó, la List ( lista enlazada ) se construye mediante la función :: (llamada "contras"), que toma dos argumentos: un elemento, conocido como la cabecera, y una lista (posiblemente vacía) a la cual se añade la cabecera.
> withoutSyntaxSugar = 1 :: []
[1] : List number
>
> longerOne = 1 :: 2 :: 3 :: []
[1,2,3] : List number
>
> nonemptyTail = 1 :: [2]
[1,2] : List number
>
List solo puede tomar valores de un tipo, por lo que algo como [1,"abc"] no es posible. Si necesitas esto, usa tuplas.
> notAllowed = [1,"abc"]
==================================== ERRORS ====================================
-- TYPE MISMATCH --------------------------------------------- repl-temp-000.elm
The 1st and 2nd elements are different types of values.
8| [1,"abc"]
^^^^^
The 1st element has this type:
number
But the 2nd is:
String
Hint: All elements should be the same type of value so that we can iterate
through the list without running into unexpected values.
>
Obteniendo elementos
> ourList = [1,2,3,4,5]
[1,2,3,4,5] : List number
>
> firstElement = List.head ourList
Just 1 : Maybe Int
>
> allButFirst = List.tail ourList
Just [2,3,4,5] : Maybe (List Int)
Este ajuste en el tipo Maybe ocurre debido al siguiente escenario:
¿Qué debería devolver List.head por una lista vacía? (Recuerda, Elm no tiene excepciones o nulos).
> headOfEmpty = List.head []
Nothing : Maybe Int
>
> tailOfEmpty = List.tail []
Nothing : Maybe (List Int)
>
> tailOfAlmostEmpty = List.tail [1] -- warning ... List is a *linked list* :)
Just [] : Maybe (List Int)
Transformando cada elemento de una lista.
List.map : (a -> b) -> List a -> List b es una función de orden superior que aplica una función de un parámetro a cada elemento de una lista, devolviendo una nueva lista con los valores modificados.
import String
ourList : List String
ourList =
["wubba", "lubba", "dub", "dub"]
lengths : List Int
lengths =
List.map String.length ourList
-- [5,5,3,3]
slices : List String
slices =
List.map (String.slice 1 3) ourList
-- ["ub", "ub", "ub", "ub"]
Si necesita conocer el índice de los elementos, puede usar List.indexedMap : (Int -> a -> b) -> List a -> List b :
newList : List String
newList =
List.indexedMap (\index element -> String.concat [toString index, ": ", element]) ourList
-- ["0: wubba","1: lubba","2: dub","3: dub"]
Filtrando una lista
List.filter : (a -> Bool) -> List a -> List a es una función de orden superior que lleva una función de un parámetro de cualquier valor a un booleano, y aplica esa función a cada elemento de una lista dada, manteniendo solo aquellos elementos para los que la función devuelve True . La función que List.filter toma como su primer parámetro a menudo se denomina predicado .
import String
catStory : List String
catStory =
["a", "crazy", "cat", "walked", "into", "a", "bar"]
-- Any word with more than 3 characters is so long!
isLongWord : String -> Bool
isLongWord string =
String.length string > 3
longWordsFromCatStory : List String
longWordsFromCatStory =
List.filter isLongWord catStory
Evalúa esto en elm-repl :
> longWordsFromCatStory
["crazy", "walked", "into"] : List String
>
> List.filter (String.startsWith "w") longWordsFromCatStory
["walked"] : List String
Coincidencia de patrones en una lista
Podemos coincidir en listas como cualquier otro tipo de datos, aunque son un tanto únicos, ya que el constructor para construir listas es la función de infijo :: . (Consulte el ejemplo Creación de una lista para obtener más información sobre cómo funciona).
matchMyList : List SomeType -> SomeOtherType
matchMyList myList =
case myList of
[] ->
emptyCase
(theHead :: theRest) ->
doSomethingWith theHead theRest
Podemos hacer coincidir tantos elementos en la lista como queramos:
hasAtLeast2Elems : List a -> Bool
hasAtLeast2Elems myList =
case myList of
(e1 :: e2 :: rest) ->
True
_ ->
False
hasAtLeast3Elems : List a -> Bool
hasAtLeast3Elems myList =
case myList of
(e1 :: e2 :: e3 :: rest) ->
True
_ ->
False
Obteniendo el elemento nth de la lista
List no admite el "acceso aleatorio", lo que significa que se necesita más trabajo para obtener, digamos, el quinto elemento de la lista que el primer elemento, y como resultado no hay List.get nth list función de List.get nth list . Uno tiene que ir desde el principio ( 1 -> 2 -> 3 -> 4 -> 5 ).
Si necesita acceso aleatorio, puede obtener mejores resultados (y rendimiento) con estructuras de datos de acceso aleatorio, como Array , donde tomar el primer elemento requiere la misma cantidad de trabajo que tomar, por ejemplo, el 1000. (complejidad O (1)).
Sin embargo, es posible (pero desanimado) obtener el elemento nth:
get : Int -> List a -> Maybe a
get nth list =
list
|> List.drop (nth - 1)
|> List.head
fifth : Maybe Int
fifth = get 5 [1..10]
-- = Just 5
nonexistent : Maybe Int
nonexistent = get 5 [1..3]
-- = Nothing
De nuevo, esto requiere mucho más trabajo cuanto más grande sea el nth argumento.
Reduciendo una lista a un solo valor
En Elm, las funciones de reducción se denominan "pliegues", y existen dos métodos estándar para "plegar" los valores: desde la izquierda, foldl , y desde la derecha, foldr .
> List.foldl (+) 0 [1,2,3]
6 : number
Los argumentos para foldl y foldr son:
- función reductora :
newValue -> accumulator -> accumulator - valor de arranque del acumulador
- lista para reducir
Un ejemplo más con función personalizada:
type alias Counts =
{ odd : Int
, even : Int
}
addCount : Int -> Counts -> Counts
addCount num counts =
let
(incOdd, incEven) =
if num `rem` 2 == 0
then (0,1)
else (1,0)
in
{ counts
| odd = counts.odd + incOdd
, even = counts.even + incEven
}
> List.foldl
addCount
{ odd = 0, even = 0 }
[1,2,3,4,5]
{ odd = 3, even = 2 } : Counts
En el primer ejemplo anterior, el programa es el siguiente:
List.foldl (+) 0 [1,2,3]
3 + (2 + (1 + 0))
3 + (2 + 1)
3 + 3
6
List.foldr (+) 0 [1,2,3]
1 + (2 + (3 + 0))
1 + (2 + 3)
1 + 5
6
En el caso de una función conmutativa como (+) no hay realmente una diferencia.
Pero mira que pasa con (::) :
List.foldl (::) [] [1,2,3]
3 :: (2 :: (1 :: []))
3 :: (2 :: [1])
3 :: [2,1]
[3,2,1]
List.foldr (::) [] [1,2,3]
1 :: (2 :: (3 :: []))
1 :: (2 :: [3])
1 :: [2,3]
[1,2,3]
Creando una lista repitiendo un valor
> List.repeat 3 "abc"
["abc","abc","abc"] : List String
Puedes darle a List.repeat cualquier valor:
> List.repeat 2 {a = 1, b = (2,True)}
[{a = 1, b = (2,True)}, {a = 1, b = (2,True)}]
: List {a : Int, b : (Int, Bool)}
Ordenar una lista
Por defecto, List.sort ordena en orden ascendente.
> List.sort [3,1,5]
[1,3,5] : List number
List.sort necesita que los elementos de la lista sean comparable . Eso significa: String , Char , number ( Int y Float ), List de comparable o tupla de comparable .
> List.sort [(5,"ddd"),(4,"zzz"),(5,"aaa")]
[(4,"zzz"),(5,"aaa"),(5,"ddd")] : List ( number, String )
> List.sort [[3,4],[2,3],[4,5],[1,2]]
[[1,2],[2,3],[3,4],[4,5]] : List (List number)
No puede ordenar listas de Bool u objetos con List.sort . Para eso vea Ordenar una lista con un comparador personalizado.
> List.sort [True, False]
-- error, can't compare Bools
Ordenar una lista con comparador personalizado
List.sortWith permite ordenar listas con datos de cualquier forma; usted le proporciona una función de comparación.
compareBools : Bool -> Bool -> Order
compareBools a b =
case (a,b) of
(False, True) ->
LT
(True, False) ->
GT
_ ->
EQ
> List.sortWith compareBools [False, True, False, True]
[False, False, True, True] : List Bool
Invertir una lista
Nota: esto no es muy eficiente debido a la naturaleza de la List (ver Comentarios a continuación). Será mejor construir la lista de la manera "correcta" desde el principio que construirla y luego revertirla.
> List.reverse [1,3,5,7,9]
[9,7,5,3,1] : List number
Ordenar una lista en orden descendente
Por defecto, List.sort ordena en orden ascendente, con la función de compare .
Hay dos formas de clasificar en orden descendente: una eficiente y otra ineficiente.
- La forma eficiente :
List.sortWithy una función de comparación descendente.
descending a b =
case compare a b of
LT -> GT
EQ -> EQ
GT -> LT
> List.sortWith descending [1,5,9,7,3]
[9,7,5,3,1] : List number
- El modo ineficiente (¡desalentado!) :
List.sorty luegoList.reverse.
> List.reverse (List.sort [1,5,9,7,3])
[9,7,5,3,1] : List number
Ordenar una lista por un valor derivado
List.sortBy permite usar una función en los elementos y usar su resultado para la comparación.
> List.sortBy String.length ["longest","short","medium"]
["short","medium","longest"] : List String
-- because the lengths are: [7,5,6]
También funciona muy bien con los registros de acceso:
people =
[ { name = "John", age = 43 }
, { name = "Alice", age = 30 }
, { name = "Rupert", age = 12 }
]
> List.sortBy .age people
[ { name = "Rupert", age = 12 }
, { name = "Alice", age = 30 }
, { name = "John", age = 43 }
] : List {name: String, age: number}
> List.sortBy .name people
[ { name = "Alice", age = 30 }
, { name = "John", age = 43 }
, { name = "Rupert", age = 12 }
] : List {name: String, age: number}