F# XLinq 遍历 - 柯里化版本的函数抛出 StackOverflowException
F# XLinq traversal - curried version of function throws StackOverflowException
我有两个类似的函数 nested
和 nestedCurried
,它们使用 XLinq 遍历 XML 树。它们没有做任何有用的事情 - 它只是从更复杂的代码中摘录的 "shrinked"。
我对这两个函数的期望是以相同的方式运行,对我来说它们看起来是相同的,唯一的区别是 nestedCurried
没有明确声明 e: XElement
参数 - 通过使用 elements
函数和函数组合 >>
同时,nestedCurried
函数在调用任何 XElement
时抛出 WhosebugException
在 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)
为什么会出现 WhosebugException
,这两个函数之间的技术区别是什么,以及如何在不明确指定 XElement
参数的情况下声明 nested
函数?
您的 () 参数不是必需的,而且会造成混淆。您使用 X.Root 部分应用了元素,因此您一遍又一遍地使用 X.Root 调用 nestedCurried - 因此堆栈溢出。要在不明确指定参数的情况下声明嵌套,您可以这样做:
let nested =
let rec inner e = elements e |> List.collect (inner)
inner
如果您将 nestedCurried 声明为
let rec nestedCurried = elements >> List.collect (nestedCurried)
你会得到一个编译器错误 "nestedCurried is evaluated as part of its own definition"。
看:每次调用 nestedCurried
时,都会立即无条件地再次调用 nestedCurried
。
为了让事情更清楚一点,考虑表达式 List.collect f
等同于 let x = f; List.collect x
。这意味着您对 nestedCurried
的定义等同于:
let nestedCurried () =
let x = nestedCurried()
elements >> List.collect x
现在是否更清楚为什么这会导致无限递归?
我有两个类似的函数 nested
和 nestedCurried
,它们使用 XLinq 遍历 XML 树。它们没有做任何有用的事情 - 它只是从更复杂的代码中摘录的 "shrinked"。
我对这两个函数的期望是以相同的方式运行,对我来说它们看起来是相同的,唯一的区别是 nestedCurried
没有明确声明 e: XElement
参数 - 通过使用 elements
函数和函数组合 >>
同时,nestedCurried
函数在调用任何 XElement
WhosebugException
在 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)
为什么会出现 WhosebugException
,这两个函数之间的技术区别是什么,以及如何在不明确指定 XElement
参数的情况下声明 nested
函数?
您的 () 参数不是必需的,而且会造成混淆。您使用 X.Root 部分应用了元素,因此您一遍又一遍地使用 X.Root 调用 nestedCurried - 因此堆栈溢出。要在不明确指定参数的情况下声明嵌套,您可以这样做:
let nested =
let rec inner e = elements e |> List.collect (inner)
inner
如果您将 nestedCurried 声明为
let rec nestedCurried = elements >> List.collect (nestedCurried)
你会得到一个编译器错误 "nestedCurried is evaluated as part of its own definition"。
看:每次调用 nestedCurried
时,都会立即无条件地再次调用 nestedCurried
。
为了让事情更清楚一点,考虑表达式 List.collect f
等同于 let x = f; List.collect x
。这意味着您对 nestedCurried
的定义等同于:
let nestedCurried () =
let x = nestedCurried()
elements >> List.collect x
现在是否更清楚为什么这会导致无限递归?