В F # есть много разных списков / типов коллекций.
F # list
type.Как сказал Крис, вы не можете инициализировать рекурсивное значение этого типа, потому что тип не ленивый и не изменяемый (неизменность означает, что вы должны создать его сразу, а тот факт, что он не ленивый, означает, что вы не можете использовать F # рекурсивныйзначения с использованием let rec
).Как сказал ssp, вы можете использовать Reflection для взлома, но это, вероятно, случай, который мы не хотим обсуждать.
Другой тип - seq
(который на самом деле IEnumerable
) или тип LazyList
от PowerPack.Они ленивы, поэтому вы можете использовать let rec
для создания циклического значения.Однако (насколько я знаю) ни одна из функций, работающих с ними, не учитывает циклические списки - если вы создаете циклический список, это просто означает, что вы создаете бесконечный список, поэтому результатиз (например) map
будет потенциально бесконечным списком.
Вот пример для LazyList
типа:
#r "FSharp.PowerPack.dll"
// Valid use of value recursion
let rec ones = LazyList.consDelayed 1 (fun () -> ones)
Seq.take 5 l // Gives [1; 1; 1; 1; 1]
Вопрос в том, какие данныетипы вы можете определить сами.Крис показывает изменяемый список, и если вы пишете операции, которые его изменяют, они влияют на весь список (если вы интерпретируете его как бесконечную структуру данных).
Вы также можете определить ленивый (потенциально циклический) тип данныхи реализуйте операции, которые обрабатывают циклы, поэтому, когда вы создаете циклический список и проецируете его в другой список, в результате будет создан циклический список (а не потенциально бесконечная структура данных).
Объявление типа может выглядеть следующим образом (я использую тип объекта, поэтому мы можем использовать равенство ссылок при проверке циклов):
type CyclicListValue<'a> =
Nil | Cons of 'a * Lazy<CyclicList<'a>>
and CyclicList<'a>(value:CyclicListValue<'a>) =
member x.Value = value
Следующие map
дескрипторы функциициклы - если вы зададите ему циклический список, он вернет вновь созданный список с такой же циклической структурой:
let map f (cl:CyclicList<_>) =
// 'start' is the first element of the list (used for cycle checking)
// 'l' is the list we're processing
// 'lazyRes' is a function that returns the first cell of the resulting list
// (which is not available on the first call, but can be accessed
// later, because the list is constructed lazily)
let rec mapAux start (l:CyclicList<_>) lazyRes =
match l.Value with
| Nil -> new CyclicList<_>(Nil)
| Cons(v, rest) when rest.Value = start -> lazyRes()
| Cons(v, rest) ->
let value = Cons(f v, lazy mapAux start rest.Value lazyRes)
new CyclicList<_>(value)
let rec res = mapAux cl cl (fun () -> res)
res