Есть ли у F # эквивалент взятию Хаскелла? - PullRequest
14 голосов
/ 14 апреля 2010

В Haskell есть функция «взять n список», которая возвращает первые n элементов из списка. Например, «sum (take 3 xs)» суммирует первые три элемента в списке xs. Есть ли у F # эквивалент? Я ожидал, что это будет одна из функций List, но я не могу найти ничего, что кажется подходящим.

Ответы [ 3 ]

44 голосов
/ 14 апреля 2010

Чтобы прояснить некоторые вещи, разница между Seq.take и Seq.truncate (как указано @ sepp2k) заключается в том, что второй даст вам последовательность, которая возвращает самое большее число элементы, которые вы указали (но если длина последовательности меньше, это даст вам меньше элементов).

Сгенерированная последовательность функция Seq.take сгенерирует исключение, если вы попытаетесь получить доступ к элементу за пределами длины исходного списка (обратите внимание, что функция Seq.take не выдает исключение сразу, потому что результат генерируется лениво последовательность).

Кроме того, вам не нужно явно преобразовывать список в последовательность. Под обложкой list<'a> - это класс .NET, который наследуется от типа seq<'a>, который является интерфейсом. Тип seq<'a> на самом деле является просто псевдонимом типа для IEnumerable<'a>, поэтому он реализуется всеми другими коллекциями (включая массивы, изменяемые списки и т. Д.). Следующий код будет работать нормально:

let list = [ 1 .. 10 ]
let res = list |> Seq.take 5

Однако, если вы хотите получить результат типа list<int>, вам нужно преобразовать последовательность обратно в список (поскольку список является более конкретным типом, чем последовательность):

let resList = res |> List.ofSeq

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

15 голосов
/ 14 апреля 2010

Да, это называется Seq.take. Использование похоже на то, что использует Haskell: Seq.take count count . Чтобы использовать его в списке, сначала используйте List.toSeq. (Обновление: как видно из комментариев, это необязательно.)

1 голос
/ 19 июня 2010

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

Примечательно, что, например, Seq.concat в списке занимает намного больше времени, чем List.concat. Полагаю, это подразумевает, что вы не просто получаете доступ к списку как к seq при вызове функции Seq.xxx, но и к тому, что список копируется / преобразуется в Seq за кулисами. * edit: Причиной, по которой я сделал вывод выше, была эта скамья, использующая интерактив F #:

#time "on";;
let lists = [for i in 0..5000000 -> [i..i+1]];;
Seq.length (Seq.concat lists);;
List.length (List.concat lists);;

На моем компьютере версия List.length занимает около 1,9 с , тогда как версия Seq.length занимает около 3,8 с (самое короткое время из нескольких повторных тестов длины только строки, исключая строку генерации списка).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...