Почему Seq.tail не вариант
Мой вопрос при входе Seq.
почему нет Seq.tail
функционировать?
В этом коде, который не преобразует последовательность в список, нет Seq.tail
функция доступна в рекурсивной функции. Это потому что Seq.initInfinte
был использован для создания последовательности, или есть другая причина?
open System
let readList() =
Seq.initInfinite (fun _ -> Console.ReadLine())
|> Seq.takeWhile (fun s -> (s <> ""))
|> Seq.map (fun x -> Int32.Parse(x))
let rec listLen list1 acc =
if Seq.isEmpty list1 then
acc
else
(* There is no Seq.tail available. Compile error. *)
listLen (Seq.tail list1) (acc + 1)
[<EntryPoint>]
let main argv =
let inList = (readList())
let inListLen = listLen inList 0
printfn "%A" inListLen
0 // return an integer exit code
Тем не менее, это работает просто отлично. Я не понимаю, почему Seq.tail
недоступен, но List.tail
доступен.
open System
let readList() =
Seq.initInfinite (fun _ -> Console.ReadLine())
|> Seq.takeWhile (fun s -> (s <> ""))
|> Seq.map (fun x -> Int32.Parse(x))
let rec listLen list1 acc =
if List.isEmpty list1 then
acc
else
listLen (List.tail list1) (acc + 1)
[<EntryPoint>]
let main argv =
let inList = Seq.toList (readList())
let inListLen = listLen inList 0
printfn "%A" inListLen
0 // return an integer exit code
2 ответа
Конкретной причины нет, просто она никогда не добавлялась, вот и все. Хотя он доступен в более поздней версии F# (в 4.0 была предпринята большая работа по упорядочению API-интерфейсов сбора).
Тем не менее, можно привести здравый смысл аргумент о том, почему Seq.tail
будет незначительно полезным, а может быть, даже опасным. Что может быть причиной того, что вы не добавили ее изначально, но я точно не знаю.
Видите ли, списки и последовательности имеют очень разные представления за кулисами.
Список - это структура данных, которая имеет два поля: первый элемент (называемый "заголовок") и остальные элементы, которые сами по себе являются просто другим списком (называемым "хвостом"). Так зовет List.tail
означает просто взять второе поле структуры данных. Никакой сложной обработки, просто взяв одно из полей структуры данных.
Последовательность, с другой стороны, в основном является функцией (называемой IEnumerable.GetEnumerator
), который возвращает изменчивую структуру данных (называется IEnumerator
), который можно многократно "пнуть" (вызывая IEnumerator.MoveNext
), производя следующий элемент при каждом ударе и изменяя его внутреннее состояние.
Это означает, что для того, чтобы "отбросить" первый элемент последовательности, нужно взять исходную последовательность и обернуть ее в другую функцию, которая, когда ее просят произвести IEnumerator
, получит внутреннюю последовательность IEnumerator
, затем пните его один раз, а затем вернитесь к абоненту. Что-то вроде этого (псевдокод):
Tail(inner).GetEnumerator =
let innerE = inner.GetEnumerator()
innerE.MoveNext()
innerE
Это означает, что, хотя со списком каждый вызов tail
делает структуру данных менее сложной (на один элемент меньше, остается только хвост) с последовательностью каждого вызова tail
сделает его более сложным (еще одна функция-обертка). Более того, если вы возьмете tail
последовательности несколько раз подряд, а затем перебирая результат, вы все равно будете перебирать всю оригинальную последовательность, хотя логически она выглядит короче для вас.
Применяя это к вашему конкретному случаю, ваш listLen
реализация на основе Seq.tail
будет иметь квадратичную сложность (в отличие от линейного списка), потому что каждый раз, когда вы звоните Seq.isEmpty
, это будет эффективно вызывать итерацию до первого не пропущенного элемента, и каждый рекурсивный вызов listLen
добавит еще один пропущенный элемент для повторения.
Для чего стоит стандартная.NET LINQ действительно имеет эквивалентную операцию - называется .Skip
и вы можете полностью использовать его из F#:
open System.Linq
let seqTail (s: _ seq) = s.Skip(1)
Или, как отмечает в комментариях Роберт Нильсен, Seq.skip
даже в стандартной библиотеке F# (я писал с телефона, не мог проверить это в то время):
let seqTail s = Seq.skip 1 s
Seq.tail не доступен в F# 3.x. Это доступно в F# 4.x. Я проверил, что твой код компилируется в 4.0, но не в 3.1.
В F# 4.0 было добавлено множество функций для "регуляризации" List, Array и Seq.
https://github.com/fsharp/fslang-design/blob/master/FSharp-4.0/ListSeqArrayAdditions.md
(Я заметил, что Option был оставлен в этой проблеме, но подозреваю, что в какой-то момент он тоже получил больше функций.)
Что касается того, почему все эти функции отсутствовали, им просто не хватало времени, чтобы заполнить эти пробелы в более ранних версиях.