我试图通过递归地将序列的第一个元素附加到列表来构建序列中的列表:
open System
let s = seq[for i in 2..4350 -> i,2*i]
let rec copy s res =
if (s|>Seq.isEmpty) then
res
else
let (a,b) = s |> Seq.head
Console.WriteLine(string a)
let newS = s |> Seq.skip(1)|> Seq.cache
let newRes = List.append res ([(a,b)])
copy newS newRes
copy s ([])
两个问题:
。得到一个堆栈溢出,这意味着我的尾巴重复伎俩糟透了
和
。当我将|> Seq.cache
放在let newS = s |> Seq.skip(1)|> Seq.cache
时,为什么代码会快100倍。
(注意这只是一个小练习,我知道你可以做Seq.toList等。)
非常感谢
一种有效的方法是(这两点对我来说仍然有点奇怪):
let toList (s:seq<_>) =
let rec copyRev res (enum:Collections.Generic.IEnumerator<_*_>) =
let somethingLeft = enum.MoveNext()
if not(somethingLeft) then
res
else
let curr = enum.Current
Console.WriteLine(string curr)
let newRes = curr::res
copyRev newRes enum
let enumerator = s.GetEnumerator()
(copyRev ([]) (enumerator)) |>List.rev
答案 0 :(得分:3)
你说这只是一个练习,但指出我对
的回答是有用的While or Tail Recursion in F#, what to use when?
并重申,如果可能,您应该支持更多的应用/声明性构造。 E.g。
let rec copy2 s = [
for tuple in s do
System.Console.WriteLine(string(fst tuple))
yield tuple
]
是表达特定功能的一种不错且高效的方式。
那就是说,如果我不说“永远不会创建那么大的名单”,我会感到疏忽。对于海量数据,您需要数组或seq。
答案 1 :(得分:1)
在我使用F#的短暂体验中,使用Seq.skip 1
就像使用带有尾部的列表一样不是一个好主意。 Seq.skip
创建一个新的IEnumerable/sequence
而不只是跳过n。因此,您的函数将比List.toSeq
慢很多。你应该用
s.GetEnumerator()
并遍历序列并保存一个列表,其中包含每个元素。
在这个问题中
Take N elements from sequence with N different indexes in F#
我开始做类似你做的事情,但发现它很慢。请参阅我的方法,了解如何做到这一点。
添加:我写过:
let seqToList (xs : seq<'a>) =
let e = xs.GetEnumerator()
let mutable res = []
while e.MoveNext() do
res <- e.Current :: res
List.rev res
并且发现构建方法实际上做了非常相似的事情(包括反向部分)。但是,它会检查您提供的序列实际上是列表还是数组。
你将能够使代码完全正常运行:(我现在也做了 - 无法抗拒; - )
let seqToList (xs : seq<'a>) =
Seq.fold (fun state t -> t :: state) [] xs |> List.rev
答案 2 :(得分:1)
你的函数是正确的尾递归,所以递归调用本身不是溢出堆栈的东西。相反,问题是Seq.skip
在递归使用时表现不佳,正如其他人所指出的那样。例如,这段代码溢出了我机器上的堆栈:
let mutable s = seq { 1 .. 20001 }
for i in 1 .. 20000 do
s <- Seq.skip 1 s
let v = Seq.head s
也许你可以看到与你自己的代码的模糊联系,这也最终成为序列的头部,这是因为重复将Seq.skip 1
应用到你的初始序列。
答案 3 :(得分:0)
请尝试以下代码。
警告:在运行此代码之前,您需要在Visual Studio中启用尾调用生成。这可以通过项目属性页面上的“构建”选项卡完成。如果未启用此代码,则代码将StackOverflow处理延续。
open System
open System.Collections.Generic
let s = seq[for i in 2..1000000 -> i,2*i]
let rec copy (s : (int * int) seq) =
use e = s.GetEnumerator()
let rec inner cont =
if e.MoveNext() then
let (a,b) = e.Current
printfn "%d" b
inner (fun l -> cont (b :: l))
else cont []
inner (fun x -> x)
let res = copy s
printfn "Done"