我有两个类似的函数 nested
和 nestedCurried
,它们使用 XLinq 遍历 XML 树。它们没有做任何有用的事情 - 它只是从更复杂的代码中“缩小”的摘录。
我期望这两个函数以相同的方式运行,对我来说它们看起来是相同的,唯一的区别是 nestedCurried
没有显式声明 e: XElement
参数 - 通过使用 elements
函数和函数组合 >>
同时,nestedCurried
函数在任何 XElement
上调用时抛出 StackOverflowException
在 FSI 中评估:
#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
,这两个函数之间的技术区别是什么,以及如何在不指定 XElement
的情况下声明 nested
函数明确论证?
最佳答案
看:每次调用 nestedCurried
时,都会立即无条件地再次调用 nestedCurried
。
为了让事情更清楚一点,考虑表达式 List.collect f
等同于 let x = f; List.collect x
.这意味着您对 nestedCurried
的定义等同于:
let nestedCurried () =
let x = nestedCurried()
elements >> List.collect x
现在是否更清楚为什么这会导致无限递归?
关于F# XLinq 遍历 - 柯里化(Currying)版本的函数抛出 StackOverflowException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33887455/