Любое серьезное программирование на любом языке всегда будет включать списки. Таким образом, вам будет приятно узнать, что F# очень и очень хорошо поддерживает списки с помощью своего модуля List. Список в F# — это упорядоченная, неизменная серия элементов одного типа.

Создаем список


В F# есть несколько путей, которыми Вы можете создать список.

  • Создаём пустой список
  • Создаем простой список с элементами от 1 до 10
  • Создаем список с нечетными номерами в промежутке от 1 до 10
  • Используем цикл for для создания списка

let prettyPrint desc list =
    printfn desc
    printfn "%A" list
         
 
//empty list
let listEmpty = []
//simple list
let list1 = [1 .. 10 ]
//simple list, with step value
let list2 = [1 ..2..10 ]
//using for loops to create lists
let list3 = [for i in 1 .. 10 -> i*2 ]
 
prettyPrint "let listEmpty = []" listEmpty
prettyPrint "let list1 = [ 1 .. 10 ]" list1
prettyPrint "let list2 = [ 1 .. 2..10 ]" list2
prettyPrint "[ for i in 1 .. 10 -> i*2 ]" list3

image

В последнем примере, показанном выше, показано, как использовать цикл for для создания списка, что очень круто, но в наборе инструментов F # есть что-то еще более крутое и мощное, а именно «List Comprehensions».

List Comprehensions(понимание списков?) — это мощный метод, который позволяет вам создавать списки, используя в значительной степени стандартный F#, который включает функции / циклы / условия и т.д.

Давайте продолжим смотреть на пример того, как мы могли бы создавать списки.

let is2 x = match x with
    | 2 -> "YES"
    | _ -> "NO"
 
 
 
//yield directly
let list1 = [
    yield 1;
    yield 2;
    yield 3;
]
 
//yield numbers between 1 and 20 where we use the
//Math.Pow function to return a new number
let list2 = [for i in 1.0 .. 20.0 do
                yield Math.Pow(i,2.0)
            ]
     
//yield only numbers between 1 and 20 that 
//can be divided by 5
let list3 = [
                for i in 1 .. 20 do
                    if i % 5 = 0 then
                        yield i
            ]
 
 
//yields YES/NO strings depending on
//whether source int = 2 or not
let list4 = [for i in 1 .. 5 ->
                is2 i
            ]

image

Некоторые полезные операторы списков


Оператор Cons


Мы можем использовать оператор cons «::» для добавления значений в существующий список, поэтому предположим, что у нас был этот список:

let list1 = [1;2;3;4]
let list2 = 42 :: list1

image

Оператор конкатенации


Другим весьма полезным оператором является оператор «@», который позволяет объединять списки одного типа. Так, например, если бы у нас было это:

let list1 = [1;2;3;4]
let list2 = [5;6;7;8]
let list3 = list1 @ list2

image

Модуль List


Я не думаю, что перебарщиваю, когда говорю, что модуль List является ключевым модулем в F#. Фактически, документация MSDN для него очень хороша по сравнению с другими в F#. Поэтому я не думаю, что смогу добавить много полезного к примерам, найденным на MSDN, но я приведу здесь несколько для вашего удобства, но для получения дополнительной информации вы должны изучить MSDN.

Свойства


Head
‘T
Первый элемент
Empty
‘T list
Возвращает пустой список, заданного типа
IsEmpty
bool
true — если в списке нет элементов
Item
‘T
Элемент, который находится по специфическому индексу
Length
int
Количество элементов
Tail
‘T list
Список без первого элемента:

let list1 = [ 1; 2; 3 ]
 
// Properties
printfn "list1.IsEmpty is %b" (list1.IsEmpty)
printfn "list1.Length is %d" (list1.Length)
printfn "list1.Head is %d" (list1.Head)
printfn "list1.Tail.Head is %d" (list1.Tail.Head)
printfn "list1.Tail.Tail.Head is %d" (list1.Tail.Tail.Head)
printfn "list1.Item(1) is %d" (list1.Item(1))

image

Фильтр(filter)


Возвращает новую коллекцию, содержащую только элементы коллекции, для которой данный предикат возвращает true. Вот тривиальный пример, который выбирает только четные числа из списка для создания нового списка

let evenOnlyList = List.filter (fun x -> x % 2 = 0) [1; 2; 3; 4; 5; 6]

image

Поиск(find)


Возвращает первый элемент, для которого данная функция возвращает true. В этом примере, поскольку список от 1 до 100 содержит 5, 5 — это 1-е число, которое делится на 5, поэтому это возвращаемое значение:

image

Для всех(forall)


Проверяет, все ли элементы коллекции удовлетворяют данному предикату. В этом примере весь список должен содержать 0, чтобы получить истинное возвращаемое значение.

let isAllZeroes list = List.forall (fun elem -> elem = 0.0) list
printfn "%b" (isAllZeroes [0.0; 0.0])
printfn "%b" (isAllZeroes [0.0; 1.0])

image

Применить ко всех(Iteri)


Применяет данную функцию к каждому элементу коллекции. Целое число, переданное функции, указывает на индекс элемента.

let data = ["Cats";"Dogs";"Mice";"Elephants"]
data |> List.iteri (fun i x -> printfn "item %d: %s" i x)

image

Сортировать по(SortWith)


Сортирует указанный список, используя данную функцию сравнения.

let list1 = [ ""; "&"; "&&"; "&&&"; ""; "|"; "||"; "|||" ]
printfn "Before sorting: "
list1 |> printfn "%A"
 
//custom sorting function
let sortFunction (string1:string) (string2:string) =
    if (string1.Length > string2.Length) then
        1
    else if (string1.Length  printfn "After sorting:\n%A"

image

В модуле списка есть множество полезных функций, это лишь малая часть того, что можно найти.

Небольшой взгляд на рекурсию над списками


Никакое обсуждение списков не будет полным без разговора о рекурсии.Рекурсия — это то, чему будет посвящен целый пост, но сейчас давайте рассмотрим, что нужно, чтобы написать рекурсивную функцию, которая работает над списком в F#.

let printIt desc x = 
    printfn "%A %A" desc x
 
let rec printList list =
    match list with
    | h :: t -> 
        printIt "head=" h
        printIt "tail=" t
        printList t
    | [] -> ()
 
printList [1;2;3;4;5]

Обратите внимание на использование ключевого слова rec , которое сообщает компилятору F #, что эта функция будет рекурсивной. Без этого ключевого слова рекурсивный вызов функции — Вы получите сообщение о ошибке компиляции, хотя и с не очень понятным текстом о ошибке.

image

Итак, давайте посмотрим результаты выполнения примера, показанного чуть выше со следующим списком [1; 2; 3; 4; 5]

image

Можно видеть, что все работает просто отлично, и оно прекращает работу, когда значение соответствует пустому списку, как и ожидалось.

Комментарии (3)


  1. tweekaz Автор
    18.10.2019 01:54

    <фото>

    </фото>
    Несмотря на то, что с разметкой все нормально, почему-то часть поплыла.
    Могу только извиниться за это :(


    1. TheShock
      18.10.2019 02:02

      Просто оберните инлайн куски кода в тег code:

      Head‘T — первый элемент Empty‘T list

      <code>Head‘T</code> - первый элемент <code>Empty‘T list</code></blockquote>
      


  1. Nikolai46
    18.10.2019 17:41

    Обратите внимание на использование ключевого слова rec
    Не знаю как кому, но для меня вот эти непонятные сокращения (fun/rec/elif) сильно демотивируют от использования языка.