Коллекции и списки

Завершено XP: 100

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

Коллекции

Первый признак того, что вам нужна коллекция, заключается в том, что у вас есть несколько фрагментов данных, и кажется, что они относятся к одному и тому же типу данных. У вас может быть сценарий, в котором имеется много записей, например, как в следующем коде:

let logEntryMonday = "first entry"
let logEntryTuesDay = "second entry"
let logEntryWednesday = "third entry"

В этом коде можно продолжить добавление аналогичных записей одну за другой, но в определенный момент этот подход становится непрактичным. Отдельные записи не только связаны, но и имеют одинаковый тип. Может, создать новую переменную для каждой записи? Скорее всего, нет. Вместо этого рассмотрите возможность использования коллекции, в которой можно создать логическую, управляемую группу, будь то добавление или удаление данных или использование другой операции:

logEntriesWeek = ["first entry"; "second entry"; "third entry"]

Этот код не только требует гораздо меньше ввода, но также позволяет упорядочить данные, когда все связанные элементы группируются вместе.

Итак, чем нам может быть полезен F#? Во-первых, в нем имеется три типа коллекций, каждая из которых предназначена для конкретной ситуации.

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

  • Массивы: это изменяемые коллекции элементов данных фиксированного размера, в которых данные имеют один и тот же тип и расположены последовательно с индексом, отсчитываемым от нуля. Под изменяемыми понимается, что можно с легкостью добавлять и удалять элементы данных, однако производительность при этом снижается.

  • Последовательности: последовательность — это логический ряд элементов одного типа. Последовательность годится для JIT, поскольку подходит для больших коллекций данных, где не предполагается использовать все элементы и где элементы вычисляются по мере необходимости. Поэтому в определенных ситуациях ее производительность может быть выше, чем производительность списка.

Списки

Как упоминалось ранее, список представляет собой упорядоченный неизменяемый набор элементов. Вот несколько способов создания списка.

let cards = ["Ace"; "King"; "Queen"]

В этом коде элементы заключаются в квадратные скобки ([]) для определения списка. Элементы списка разделяются точкой с запятой (;).

Другим способом создания списка является размещение каждого элемента в новой строке, как показано ниже. Этот метод также удаляет необходимость использования с запятой.

let cards = [
  "Ace"
  "King"
  "Queen"
]

В списках элементы должны иметь одинаковый тип, поэтому следующее объявление не допускается:

let cards = [ 1; "Ace"; "King" ] // not permitted

В этом коде, поскольку объявление смешивает числа и строки, это не разрешается. Другим способом создания списка является использование оператора диапазона (..). Идея состоит в том, чтобы указать начальные и конечные элементы, разделенные оператором диапазона (..). При этом создаются все числа от начала до конца, включая промежуточные элементы. Например, можно создать числа 1 2 3 4 5 с помощью следующего кода:

let numbers = [ 1 .. 5 ]

Изменение списка

Как упоминалось, списки являются неизменяемыми, а это означает, что изменить их не удастся. Если посмотреть с другой стороны, вы можете сделать это. Например, добавив в список элемент или даже список элементов. Что это обозначает? Рассмотрим следующий пример:

let cards = ["Ace"; "King"; "Queen"]

Приведенный выше код представляет собой список, состоящий из трех строк. С помощью оператора двойного двоеточия (::) можно добавить элемент в начало списка. Результатом является новый список. Причем старый список остается без изменений:

let cards = ["Ace"; "King"; "Queen"]
let newList = "Jack" :: cards // "Jack", "Ace", "King", "Queen" 

Можно также добавить весь список с помощью оператора знака (@), как показано ниже:

let cards = ["Ace"; "King"; "Queen"]
let otherCardList = ["Jack"; "10"]
let fullList = cards @ otherCardList // "Ace", "King", "Queen", "Jack", "10"

В модуле списка имеется функция append(), которая подходит как для коллекций массивов, так и для коллекций последовательностей. Предпочтительнее использовать, например, append(), если необходимо переключить тип коллекции со списка на последовательность или не нужно изучать новый способ добавления элементов. Использование знака (@) для добавления элементов в список работает только для структуры списка. Давайте вернемся к нашим двум примерам и вместо этого будем использовать append().

let cards = ["Ace"; "King"; "Queen"]
let otherCardList = ["10"; "9"]
let fullList = cards |> List.append ["Jack"] // "Jack", "Ace", "King", "Queen"
let fullList = cards |> List.append otherCardList // "10", "9", "Ace", "King", "Queen"

Свойства

Списки в F# реализуются как связанные списки. То есть, список представляет собой структуру, в которой каждый элемент связан с другим элементом. Дополнительные термины, которые необходимо изучить, — это головной элемент, который является первым элементом в списке, и оконечный элемент, включающий элементы, которых нет в головном элементе. Например, в списке 1 2 3 4 начальным будет элемент 1, а конечным — 2 3 4.

Разделив элементы таким образом, можно быстро получить доступ к первому элементу, чтобы вы могли читать, удалять или выполнять другие действия с ним. Для доступа к конкретному элементу в списке можно использовать свойство Item, которое принимает индекс, отсчитываемый от нуля, как показано ниже:

let list = [1; 2; 3; 4]
list.Item 1 // 2

Эти функции списка описаны в следующей таблице.

Свойство Description
Head Первый элемент в списке
Нет значения Возвращает пустой список, может использоваться, если нужно создать пустой список
IsEmpty Проверяет, является ли текущий список пустым
Товар Извлекает текущий элемент на заданной позиции (индекс, отсчитываемый от нуля)
Length Возвращает число элементов в списке
Tail Возвращает все элементы списка, кроме первого

Следующий урок: Упражнение. Применение свойств списка

Предыдущий Следующая