Elm Language
Списки и итерация
Поиск…
замечания
List ( связанный список ) светит в последовательном доступе :
- доступ к первому элементу
- добавление к началу списка
- удаление из передней части списка
С другой стороны, он не идеален для случайного доступа (т. Е. Получения n-го элемента) и прохождения в обратном порядке , и вам может быть повезло (и производительность) с структурой данных Array .
Создание списка по диапазону
До 0.18.0 вы можете создавать такие диапазоны:
> range = [1..5]
[1,2,3,4,5] : List number
>
> negative = [-5..3]
[-5,-4,-3,-2,-1,0,1,2,3] : List number
В 0.18.0 Синтаксис [1..5] удален .
> 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
Диапазоны , созданные этот синтаксис всегда включают и шаг всегда 1.
Создание списка
> 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
>
Под капотом List ( связанный список ) строится функцией :: (так называемые «cons»), которая принимает два аргумента: элемент, известный как глава, и (возможно, пустой) список, к которому добавляется голова.
> withoutSyntaxSugar = 1 :: []
[1] : List number
>
> longerOne = 1 :: 2 :: 3 :: []
[1,2,3] : List number
>
> nonemptyTail = 1 :: [2]
[1,2] : List number
>
List может принимать только значения одного типа, поэтому нечто вроде [1,"abc"] невозможно. Если вам это нужно, используйте кортежи.
> 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.
>
Получение элементов
> 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)
Эта упаковка в тип Maybe происходит из-за следующего сценария:
Что должно List.head возвращать для пустого списка? (Помните, что Elm не имеет исключений или нулей.)
> 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)
Преобразование каждого элемента списка
List.map : (a -> b) -> List a -> List b - это функция более высокого порядка, которая применяет однопараметрическую функцию к каждому элементу списка, возвращая новый список с измененными значениями.
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"]
Если вам нужно знать индекс элементов, вы можете использовать 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"]
Фильтрация списка
List.filter : (a -> Bool) -> List a -> List a - это функция более высокого порядка, которая принимает однопараметрическую функцию от любого значения до логического и применяет эту функцию к каждому элементу данного списка, сохраняя только те элементы, для которых функция возвращает True . Функция, которую List.filter принимает в качестве своего первого параметра, часто упоминается как предикат .
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
Оцените это в elm-repl :
> longWordsFromCatStory
["crazy", "walked", "into"] : List String
>
> List.filter (String.startsWith "w") longWordsFromCatStory
["walked"] : List String
Соответствие шаблону в списке
Мы можем сопоставлять списки, как и любой другой тип данных, хотя они несколько уникальны, поскольку конструктор для создания списков - это функция infix :: . (См. Пример Создание списка для более подробного описания того, как это работает.)
matchMyList : List SomeType -> SomeOtherType
matchMyList myList =
case myList of
[] ->
emptyCase
(theHead :: theRest) ->
doSomethingWith theHead theRest
Мы можем сопоставить столько элементов в списке, сколько хотим:
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
Получение n-го элемента из списка
List не поддерживает «произвольный доступ», что означает, что требуется больше работы, чтобы получить, скажем, пятый элемент из списка, чем первый элемент, и, как следствие, функции List.get nth list . Нужно пройти весь путь с самого начала ( 1 -> 2 -> 3 -> 4 -> 5 ).
Если вам нужен произвольный доступ, вы можете получить лучшие результаты (и производительность) со структурами данных с произвольным доступом, такими как Array , где взятие первого элемента требует такой же работы, как принятие, скажем, 1000-го. (сложность O (1)).
Тем не менее, возможно (но обескураженно) получить n-й элемент:
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
Опять же, это занимает значительно больше работы, чем больше nth аргумент.
Уменьшение списка до одного значения
В Elm функции сокращения называются «сгибами», и есть два стандартных метода для «складывания» значений: слева, foldl и справа, foldr .
> List.foldl (+) 0 [1,2,3]
6 : number
Аргументами foldl и foldr являются:
- функция уменьшения :
newValue -> accumulator -> accumulator - начальное значение аккумулятора
- список для сокращения
Еще один пример с пользовательской функцией:
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
В первом примере выше программа выглядит следующим образом:
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
В случае коммутативной функции, такой как (+) на самом деле нет разницы.
Но посмотрите, что происходит с (::) :
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]
Создание списка путем повторения значения
> List.repeat 3 "abc"
["abc","abc","abc"] : List String
Вы можете дать List.repeat любое значение:
> List.repeat 2 {a = 1, b = (2,True)}
[{a = 1, b = (2,True)}, {a = 1, b = (2,True)}]
: List {a : Int, b : (Int, Bool)}
Сортировка списка
По умолчанию List.sort сортируется в порядке возрастания.
> List.sort [3,1,5]
[1,3,5] : List number
List.sort требует, чтобы элементы списка были comparable . Это означает: String , Char , number ( Int и Float ), List comparable или кортежей 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)
Вы не можете сортировать списки Bool или объектов с помощью List.sort . Для этого см. Раздел Сортировка списка с пользовательским компаратором.
> List.sort [True, False]
-- error, can't compare Bools
Сортировка списка с помощью пользовательского компаратора
List.sortWith позволяет сортировать списки с данными любой формы - вы предоставляете ее с помощью функции сравнения.
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
Перемещение списка
Примечание: это не очень эффективно из-за характера List (см. Примечания ниже). Лучше построить список «правильным» способом с самого начала, чем построить его, а затем отменить его.
> List.reverse [1,3,5,7,9]
[9,7,5,3,1] : List number
Сортировка списка в порядке убывания
По умолчанию List.sort сортирует в порядке возрастания, с функцией compare .
Существует два способа сортировки по убыванию: один эффективный и один неэффективный.
- Эффективный способ :
List.sortWithи нисходящая функция сравнения.
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
- Неэффективный способ (обескуражен!) :
List.sortа затемList.reverse.
> List.reverse (List.sort [1,5,9,7,3])
[9,7,5,3,1] : List number
Сортировка списка по производному значению
List.sortBy позволяет использовать функцию для элементов и использовать ее результат для сравнения.
> List.sortBy String.length ["longest","short","medium"]
["short","medium","longest"] : List String
-- because the lengths are: [7,5,6]
Он также прекрасно работает с записывающими устройствами:
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}