F#XLinq遍历 - curried版本的函数抛出StackOverflowException

时间:2015-11-24 07:12:24

标签: f# linq-to-xml currying

我有两个类似的函数nestednestedCurried,它们使用XLinq遍历XML树。他们没有做任何有用的事情 - 它只是一个"缩小"从更复杂的代码中摘录。

我对这两个函数的期望是以相同的方式运行,对我来说它看起来是相同的,唯一的区别是nestedCurried没有明确声明{{1 }}}参数 - 使用e: XElement函数和函数组合elements

来解决它

同时,>>函数在任何nestedCurried

上调用时会抛出StackOverflowException

在FSI评估:

XElement

为什么#r "System.Xml.Linq" open System.Xml.Linq let inline elements (e: XElement) = e.Elements() |> Seq.toList let rec nested () e = elements e |> List.collect (nested ()) let rec nestedCurried () = elements >> List.collect (nestedCurried ()) let x = XDocument.Parse """<a></a>""" let ok : XElement list = nested () (x.Root) // Stack Overflow below let boom : XElement list = nestedCurried () (x.Root) 出现,这两个函数之间的技术差异是什么,如何在不明确指定StackOverflowException参数的情况下声明nested函数?

2 个答案:

答案 0 :(得分:2)

查看:每次拨打nestedCurried时,都会立即无条件地再次致电nestedCurried

为了使事情更清楚,请考虑表达式List.collect f等同于let x = f; List.collect x。这意味着您对nestedCurried的定义与此相同:

let nestedCurried () =
  let x = nestedCurried()
  elements >> List.collect x

现在更清楚为什么会导致无限递归?

答案 1 :(得分:1)

不需要你的()参数,这让人感到困惑。你已经使用X.Root部分应用了元素,因此你反复调用带有X.Root的nestedCurried - 因此Stack Overflow。要声明嵌套而不明确指定参数,您可以执行以下操作:

let nested = 
  let rec inner e = elements e |> List.collect (inner)
  inner

如果您将nestedCurried声明为

let rec nestedCurried = elements >> List.collect (nestedCurried)

你会得到一个编译错误,&#34; nestedCurried被评估为自己定义的一部分&#34;。