将 seq 尾部递归复制到 F# 中的列表
我试图通过将序列的第一个元素递归地附加到列表来从序列构建列表:
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 ([])
两个问题:
。出现堆栈溢出,这意味着我的尾部递归策略很
。
糟糕 为什么当我输入 |> 时,代码速度快了 100 倍? Seq.cache 此处
let newS = s |>序列跳过(1)|> Seq.cache
。
(注意这只是一个小练习,我知道你可以做 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
I am trying to build a list from a sequence by recursively appending the first element of the sequence to the list:
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 ([])
Two problems:
. getting a Stack overflow which means my tail recusive ploy sucks
and
. why is the code 100x faster when I put |> Seq.cache
here let newS = s |> Seq.skip(1)|> Seq.cache
.
(Note this is just a little exercise, I understand you can do Seq.toList etc.. )
Thanks a lot
One way that works is ( the two points still remain a bit weird to me ):
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
你说这只是一个练习,但指出我对
F# 中的 while 或尾递归,何时使用什么?
并重申您应该尽可能支持更多应用性/声明性结构。 Eg
是表达特定功能的一种很好且高效的方式。
也就是说,如果我不说“永远不要创建那么大的列表”,我会感到失职。对于海量数据,您需要数组或序列。
You say it's just an exercise, but it's useful to point to my answer to
While or Tail Recursion in F#, what to use when?
and reiterate that you should favor more applicative/declarative constructs when possible. E.g.
is a nice and performant way to express your particular function.
That said, I'd feel remiss if I didn't also say "never create a list that big". For huge data, you want either array or seq.
根据我使用 F# 的短暂经验,像使用尾部列表一样使用
Seq.skip 1
并不是一个好主意。Seq.skip
创建一个新的IEnumerable/sequence
而不仅仅是跳过 n。因此,您的函数将比 List.toSeq 慢很多。您应该正确地执行命令并迭代序列并保存一个包含每个元素的列表。
在这个问题中
从序列中取出 N 个元素 N F# 中的不同索引
我开始做与您类似的事情,但发现它非常慢。请参阅我的方法以获取如何做到这一点的灵感。
补充:我写了这个:
并发现内置方法实际上做了一些非常相似的事情(包括相反的部分)。但是,它会检查您提供的序列实际上是列表还是数组。
您将能够使代码完全发挥作用:(我现在也这样做了 - 无法抗拒;-))
In my short experience with F# it is not a good idea to use
Seq.skip 1
like you would with lists with tail.Seq.skip
creates a newIEnumerable/sequence
and not just skips n. Therefore your function will be A LOT slower thanList.toSeq
. You should properly do it imperative withand iterates through the sequence and hold a list which you cons every single element.
In this question
Take N elements from sequence with N different indexes in F#
I started to do something similar to what you do but found out it is very slow. See my method for inspiration for how to do it.
Addition: I have written this:
And found out that the build in method actually does something very similar (including the reverse part). It do, however, checks whether the sequence you have supplied is in fact a list or an array.
You will be able to make the code entirely functional: (which I also did now - could'nt resist ;-))
您的函数是正确的尾递归,因此递归调用本身并不是堆栈溢出的原因。相反,问题在于
Seq.skip
在递归使用时表现不佳,正如其他人指出的那样。例如,这段代码在我的机器上溢出了堆栈:也许您可以看到与您自己的代码的模糊连接,该代码最终也占据了由于重复应用
Seq.skip 1
所产生的序列的头部你的初始序列。Your function is properly tail recursive, so the recursive calls themselves are not what is overflowing the stack. Instead, the problem is that
Seq.skip
is poorly behaved when used recursively, as others have pointed out. For instance, this code overflows the stack on my machine:Perhaps you can see the vague connection to your own code, which also eventually takes the head of a sequence which results from repeatedly applying
Seq.skip 1
to your initial sequence.尝试以下代码。
警告:在运行此代码之前,您需要在 Visual Studio 中启用尾调用生成。这可以通过项目属性页面上的“构建”选项卡来完成。如果未启用此功能,代码将在 StackOverflow 中处理延续。
Try the following code.
Warning: Before running this code you will need to enable tail call generation in Visual Studio. This can be done through the Build tab on the project properties page. If this is not enabled the code will StackOverflow processing the continuation.