我有两个类似的函数nested
和nestedCurried
,它们使用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
函数?
答案 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;。