2010-04-13 4 views
13

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

ответ

15

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

+0

Это похоже на то, что я ищу. Тестирование сейчас. :) – McMuttons

+13

На самом деле, действие haskell действует как Seq.truncate, а не Seq.take. – sepp2k

+2

Поскольку список наследуется от IEnumerable, вам не нужно сначала преобразовывать его в последовательность, чем я могу видеть. Использование Seq.take в списке отлично работало. – McMuttons

39

Чтобы прояснить несколько вещей, разница между 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 (но это только моя догадка ...)

+0

Nice разъяснений. – McMuttons

+0

@McMuttons: Спасибо! Я не был уверен, должен ли я опубликовать его или нет, потому что большинство вещей уже упоминалось в комментариях. Итак, я рад, что он используется! –

1

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

Более примечательно, что, например, Seq.concat в списке занимает намного больше времени, чем List.concat. Я предполагаю, что это означает, что вы не просто получаете доступ к списку как seq при вызове функции Seq.xxx, но и о том, что список скопирован/преобразован в Seq за кулисами.

редактировать: Причина, я сделал вывод, выше, была эта скамья с помощью 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 сек. (кратчайшее время нескольких повторных тестов только для линий длины, исключая линию генерации списка).

+3

Я не думаю, что это правильно. 'list <'t>' реализует интерфейс 'seq <'t>', поэтому нет необходимости в преобразовании и нет причин ожидать, что копия будет выполнена. Кроме того, 'Seq.take' работает лениво, тогда как' List.take' не может, поэтому в длинном списке операции последовательности почти наверняка будут быстрее, если потребуется только фронт результирующей последовательности. Однако может оказаться правдой, что реализация функции «List.take» с помощью сопоставления с образцом будет работать лучше, чем реализация «Seq.take», которая перечисляет список, если вы хотите с нетерпением получить доступ ко всем элементам результирующего списка. – kvb

+1

Мое заключение о конверсиях, вероятно, было неправильным, но что-то происходит, и требуется дополнительное время. Я редактировал свой пост и добавил некоторые тайминги. – Batibix

Смежные вопросы