Elm Language
Listy i iteracja
Szukaj…
Uwagi
List ( lista połączona ) świeci w sekwencyjnym dostępie :
- dostęp do pierwszego elementu
- przechodzenie na początek listy
- usuwanie z początku listy
Z drugiej strony, nie jest idealny do losowego dostępu (tj. Uzyskania n-tego elementu) i przechodzenia w odwrotnej kolejności , i możesz mieć więcej szczęścia (i wydajności) ze strukturą danych Array .
Tworzenie listy według zakresu
Przed 0.18.0 możesz tworzyć takie zakresy:
> range = [1..5]
[1,2,3,4,5] : List number
>
> negative = [-5..3]
[-5,-4,-3,-2,-1,0,1,2,3] : List number
W 0.18.0 Składnia [1..5] została usunięta .
> 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
Zakresy utworzone przez tę składnię są zawsze włącznie, a krok to zawsze 1 .
Tworzenie listy
> 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
>
Pod maską List ( lista połączona ) jest konstruowana przez funkcję :: (zwaną „przeciw”), która pobiera dwa argumenty: element zwany nagłówkiem i (ewentualnie pustą) listę, do której dodawany jest nagłówek.
> withoutSyntaxSugar = 1 :: []
[1] : List number
>
> longerOne = 1 :: 2 :: 3 :: []
[1,2,3] : List number
>
> nonemptyTail = 1 :: [2]
[1,2] : List number
>
List może przyjmować tylko wartości jednego typu, więc coś takiego jak [1,"abc"] nie jest możliwe. Jeśli potrzebujesz tego, użyj krotek.
> 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.
>
Zdobywanie elementów
> 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)
Takie zawijanie do typu Maybe zdarza z powodu następującego scenariusza:
Co List.head powinien zwrócić w przypadku pustej listy? (Pamiętaj, Wiąz nie ma wyjątków ani wartości zerowych).
> 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)
Przekształcanie każdego elementu listy
List.map : (a -> b) -> List a -> List b to funkcja wyższego rzędu, która stosuje funkcję jednego parametru do każdego elementu listy, zwracając nową listę ze zmodyfikowanymi wartościami.
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"]
Jeśli potrzebujesz znać indeks elementów, możesz użyć 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"]
Filtrowanie listy
List.filter : (a -> Bool) -> List a -> List a to funkcja wyższego rzędu, która przenosi funkcję jednego parametru z dowolnej wartości na wartość logiczną i stosuje tę funkcję do każdego elementu danej listy, zachowując tylko te elementy, dla których funkcja zwraca True . Funkcja, którą List.filter przyjmuje jako swój pierwszy parametr, jest często nazywana predykatem .
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
Oceń to w elm-repl :
> longWordsFromCatStory
["crazy", "walked", "into"] : List String
>
> List.filter (String.startsWith "w") longWordsFromCatStory
["walked"] : List String
Dopasowywanie wzorców na liście
Możemy dopasowywać na listach jak każdy inny typ danych, chociaż są one nieco unikalne, ponieważ konstruktorem do tworzenia list jest funkcja infix :: . (Zobacz przykład Tworzenie listy, aby dowiedzieć się więcej o tym, jak to działa.)
matchMyList : List SomeType -> SomeOtherType
matchMyList myList =
case myList of
[] ->
emptyCase
(theHead :: theRest) ->
doSomethingWith theHead theRest
Możemy dopasować tyle elementów na liście, ile chcemy:
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
Pobieranie n-tego elementu z listy
List nie obsługuje „losowego dostępu”, co oznacza, że potrzeba więcej pracy, aby uzyskać, powiedzmy, piąty element z listy niż pierwszy element, w wyniku czego nie ma funkcji List.get nth list . Trzeba przejść całą drogę od samego początku ( 1 -> 2 -> 3 -> 4 -> 5 ).
Jeśli potrzebujesz dostępu losowego, możesz uzyskać lepsze wyniki (i wydajność) dzięki strukturom danych dostępu swobodnego, takim jak Array , gdzie wzięcie pierwszego elementu zajmuje tyle samo pracy, co powiedzmy 1000-ty. (złożoność O (1)).
Niemniej jednak jest możliwe (ale odradzane) uzyskanie n-tego elementu:
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
Ponownie, wymaga to znacznie więcej pracy, im większy jest nth argument.
Zmniejszenie listy do jednej wartości
W Elm funkcje redukujące nazywane są „fałdami”, a istnieją dwie standardowe metody „fałdowania” wartości: z lewej strony, foldl i z prawej, foldr .
> List.foldl (+) 0 [1,2,3]
6 : number
Argumenty foldl i foldr to:
- funkcja redukująca :
newValue -> accumulator -> accumulator - wartość początkowa akumulatora
- lista do zmniejszenia
Jeszcze jeden przykład z funkcją niestandardową:
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
W pierwszym przykładzie powyżej program wygląda następująco:
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
W przypadku funkcji komutacyjnej takiej jak (+) tak naprawdę nie ma różnicy.
Ale zobacz, co się stanie z (::) :
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]
Tworzenie listy poprzez powtarzanie wartości
> List.repeat 3 "abc"
["abc","abc","abc"] : List String
Możesz podać List.repeat dowolną wartość:
> List.repeat 2 {a = 1, b = (2,True)}
[{a = 1, b = (2,True)}, {a = 1, b = (2,True)}]
: List {a : Int, b : (Int, Bool)}
Sortowanie listy
Domyślnie List.sort sortuje w kolejności rosnącej.
> List.sort [3,1,5]
[1,3,5] : List number
List.sort wymaga, aby elementy listy były comparable . Oznacza to: String , Char , number ( Int i Float ), List of comparable lub krotkę 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)
Nie można sortować list Bool ani obiektów za pomocą List.sort . W tym celu patrz Sortowanie listy za pomocą niestandardowego komparatora.
> List.sort [True, False]
-- error, can't compare Bools
Sortowanie listy za pomocą niestandardowego komparatora
List.sortWith pozwala sortować listy z danymi o dowolnym kształcie - udostępniasz funkcję porównania.
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
Odwracanie listy
Uwaga: nie jest to bardzo wydajne ze względu na charakter List (patrz uwagi poniżej). Lepiej będzie zbudować listę „właściwą” od samego początku niż zbudować ją, a następnie odwrócić.
> List.reverse [1,3,5,7,9]
[9,7,5,3,1] : List number
Sortowanie listy w kolejności malejącej
Domyślnie List.sort sortuje w kolejności rosnącej, z funkcją compare .
Istnieją dwa sposoby sortowania w kolejności malejącej: jeden wydajny i jeden nieefektywny.
- Wydajny sposób :
List.sortWithi malejąca funkcja porównania.
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
- Nieefektywny sposób (odradzany!) :
List.sorta następnieList.reverse.
> List.reverse (List.sort [1,5,9,7,3])
[9,7,5,3,1] : List number
Sortowanie listy według wartości pochodnej
List.sortBy pozwala na użycie funkcji na elementach i wykorzystanie jej wyniku do porównania.
> List.sortBy String.length ["longest","short","medium"]
["short","medium","longest"] : List String
-- because the lengths are: [7,5,6]
Świetnie współpracuje także z akcesoriami do nagrywania:
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}