Elm Language
Lijsten en Iteratie
Zoeken…
Opmerkingen
De List ( gekoppelde lijst ) schittert in opeenvolgende toegang :
- toegang tot het eerste element
- vooraan in de lijst
- verwijderen vanaf de voorkant van de lijst
Aan de andere kant is het niet ideaal voor willekeurige toegang (bijv. Het verkrijgen van het nde element) en verplaatsing in omgekeerde volgorde , en u hebt misschien meer geluk (en prestaties) met de gegevensstructuur van de Array .
Een lijst op bereik maken
Voorafgaand aan 0.18.0 kunt u bereiken als deze maken:
> range = [1..5]
[1,2,3,4,5] : List number
>
> negative = [-5..3]
[-5,-4,-3,-2,-1,0,1,2,3] : List number
In 0.18.0 De syntaxis [1..5] is verwijderd .
> 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
Bereiken die door deze syntaxis zijn gemaakt, zijn altijd inclusief en de stap is altijd 1 .
Een lijst maken
> 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
>
Onder de motorkap wordt List ( gekoppelde lijst ) samengesteld door de :: functie ("cons" genoemd), waarvoor twee argumenten nodig zijn: een element, bekend als de kop, en een (mogelijk lege) lijst waaraan de kop is voorafgegaan.
> withoutSyntaxSugar = 1 :: []
[1] : List number
>
> longerOne = 1 :: 2 :: 3 :: []
[1,2,3] : List number
>
> nonemptyTail = 1 :: [2]
[1,2] : List number
>
List kan slechts waarden van één type aannemen, dus zoiets als [1,"abc"] is niet mogelijk. Gebruik tupels als je dit nodig hebt.
> 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.
>
Elementen ophalen
> 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)
Deze verpakking in het type Maybe gebeurt vanwege het volgende scenario:
Wat moet List.head opleveren voor een lege lijst? (Vergeet niet dat Elm geen uitzonderingen of nulwaarden heeft.)
> 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)
Elk element van een lijst transformeren
List.map : (a -> b) -> List a -> List b is een functie van hogere orde die een functie met één parameter toepast op elk element van een lijst en een nieuwe lijst met de gewijzigde waarden retourneert.
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"]
Als u de index van de elementen moet weten, kunt u 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"]
Een lijst filteren
List.filter : (a -> Bool) -> List a -> List a is een functie van een hogere orde die een functie met één parameter van een willekeurige waarde naar een Boolean neemt en die functie op elk element van een gegeven lijst toepast, alleen die elementen behouden waarvoor de functie True aan retourneert. De functie die List.filter als eerste parameter aanneemt, wordt vaak een predicaat genoemd .
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
Evalueer dit in elm-repl :
> longWordsFromCatStory
["crazy", "walked", "into"] : List String
>
> List.filter (String.startsWith "w") longWordsFromCatStory
["walked"] : List String
Patroonovereenkomst op een lijst
We kunnen op lijsten matchen als elk ander gegevenstype, hoewel ze enigszins uniek zijn, omdat de constructor voor het samenstellen van lijsten de infixfunctie is :: . (Zie het voorbeeld Een lijst maken voor meer informatie over hoe dat werkt.)
matchMyList : List SomeType -> SomeOtherType
matchMyList myList =
case myList of
[] ->
emptyCase
(theHead :: theRest) ->
doSomethingWith theHead theRest
We kunnen zoveel elementen in de lijst matchen als we willen:
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
Nde element uit de lijst krijgen
List ondersteunt geen "willekeurige toegang", wat betekent dat er meer werk nodig is om bijvoorbeeld het vijfde element uit de lijst te krijgen dan het eerste element, en bijgevolg is er geen List.get nth list . Men moet helemaal vanaf het begin gaan ( 1 -> 2 -> 3 -> 4 -> 5 ).
Als u willekeurige toegang nodig hebt, krijgt u mogelijk betere resultaten (en prestaties) met datastructuren voor willekeurige toegang, zoals Array , waarbij het nemen van het eerste element evenveel werk kost als het nemen van bijvoorbeeld de 1000e. (complexiteit O (1)).
Desondanks is het mogelijk (maar ontmoedigd) om het nde element te krijgen:
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
Nogmaals, dit kost aanzienlijk meer werk naarmate het nth argument groter is.
Een lijst terugbrengen tot één waarde
In Elm worden verkleiningsfuncties "vouwen" genoemd, en er zijn twee standaardmethoden om waarden omhoog te "vouwen": van links, foldl en van rechts, foldr .
> List.foldl (+) 0 [1,2,3]
6 : number
De argumenten voor foldl en foldr zijn:
- reducerende functie :
newValue -> accumulator -> accumulator - startwaarde accumulator
- lijst om te verkleinen
Nog een voorbeeld met aangepaste functie:
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
In het eerste voorbeeld hierboven ziet het programma er als volgt uit:
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
In het geval van een commutatieve functie zoals (+) er niet echt een verschil.
Maar kijk wat er gebeurt met (::) :
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]
Een lijst maken door een waarde te herhalen
> List.repeat 3 "abc"
["abc","abc","abc"] : List String
U kunt List.repeat elke waarde geven:
> List.repeat 2 {a = 1, b = (2,True)}
[{a = 1, b = (2,True)}, {a = 1, b = (2,True)}]
: List {a : Int, b : (Int, Bool)}
Een lijst sorteren
Standaard sorteert List.sort in oplopende volgorde.
> List.sort [3,1,5]
[1,3,5] : List number
List.sort heeft de List.sort nodig om comparable . Dat betekent: String , Char , number ( Int en Float ), List met comparable of tuple met 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)
U kunt lijsten met Bool of objecten niet sorteren met List.sort . Zie daarvoor Een lijst sorteren met aangepaste comparator.
> List.sort [True, False]
-- error, can't compare Bools
Een lijst sorteren met aangepaste vergelijker
List.sortWith kunt u lijsten sorteren met gegevens van elke vorm - u voorziet deze van een vergelijkingsfunctie.
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
Een lijst omkeren
Opmerking: dit is niet erg efficiënt vanwege de aard van List (zie Opmerkingen hieronder). Het is beter om de lijst vanaf het begin op de "juiste" manier samen te stellen dan deze op te bouwen en vervolgens om te keren.
> List.reverse [1,3,5,7,9]
[9,7,5,3,1] : List number
Een lijst in aflopende volgorde sorteren
Standaard sorteert List.sort in oplopende volgorde, met de compare .
Er zijn twee manieren om in aflopende volgorde te sorteren: een efficiënte en een inefficiënte.
- De efficiënte manier :
List.sortWithen een dalende vergelijkingsfunctie.
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
- De inefficiënte manier (ontmoedigd!) :
List.sorten vervolgensList.reverse.
> List.reverse (List.sort [1,5,9,7,3])
[9,7,5,3,1] : List number
Een lijst sorteren op een afgeleide waarde
List.sortBy maakt het mogelijk om een functie op de elementen te gebruiken en het resultaat ervan te gebruiken voor de vergelijking.
> List.sortBy String.length ["longest","short","medium"]
["short","medium","longest"] : List String
-- because the lengths are: [7,5,6]
Het werkt ook goed met record accessors:
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}