Recursion F#树构建函数导致Xamarin Studio中的堆栈溢出
我试图在树状结构中建立一些规则,逻辑门,即and,not,or以及条件,例如属性x等于值y。我首先编写了最明显的递归函数,这很有效。然后,我尝试编写一个版本,以延续传递的方式从和中获取线索,不会导致堆栈溢出 它适用于小树(深度约为1000),但不幸的是,当使用大树时,当我使用Xamarin Studio在Mac上运行它时,它会导致堆栈溢出。有谁能告诉我,我是否误解了F#如何对待尾部递归代码,或者这段代码是否不是尾部递归代码Recursion F#树构建函数导致Xamarin Studio中的堆栈溢出,recursion,xamarin,f#,functional-programming,stack-overflow,Recursion,Xamarin,F#,Functional Programming,Stack Overflow,我试图在树状结构中建立一些规则,逻辑门,即and,not,or以及条件,例如属性x等于值y。我首先编写了最明显的递归函数,这很有效。然后,我尝试编写一个版本,以延续传递的方式从和中获取线索,不会导致堆栈溢出 它适用于小树(深度约为1000),但不幸的是,当使用大树时,当我使用Xamarin Studio在Mac上运行它时,它会导致堆栈溢出。有谁能告诉我,我是否误解了F#如何对待尾部递归代码,或者这段代码是否不是尾部递归代码 代码是尾部递归的,你说对了。但问题在于单核细胞增多症。看,Mono并不
代码是尾部递归的,你说对了。但问题在于单核细胞增多症。看,Mono并不像官方产品那样是.NET的高质量实现。特别是,它不进行尾部调用消除。一点也不 对于最简单(也是最普遍)的自递归,这并不重要,因为编译器会更早地捕获它。F#编译器足够聪明,可以发现函数正在调用自己,找出在什么条件下,并将其转换为整洁的
while
循环,这样编译后的代码就不会进行任何调用
但是,当尾部调用是作为参数传递的函数时,编译器不能这样做,因为实际调用的函数直到运行时才知道。事实上,即使两个函数的相互递归也不能可靠地转换为循环
可能的解决办法:
- 切换到.NET核心
- 不要使用递归延续,而是使用累加器(可能不可能)
- 使用自递归并传递手动维护的连续堆栈
- 如果所有其他操作都失败,请使用可变堆栈
那太令人失望了。花了相当长的时间思考问题的关键是我的实现。找到了这个,它看起来和这个匹配,最后的评论让它看起来好像不会很快解决。Mono正在消失。切换到.NET内核。@FyodorSoikin我认为Xamarin的Mono不会很快消失。
let FoldTree andF orF notF leafV t data =
let rec Loop t cont =
match t with
| AndGate (left, right)->
Loop left (fun lacc ->
Loop right (fun racc ->
cont (andF lacc racc)))
| OrGate (left, right)->
Loop left (fun lacc ->
Loop right (fun racc ->
cont (orF lacc racc)))
| NotGate exp ->
Loop exp (fun acc -> cont (notF acc))
| EqualsExpression(property,value) -> cont (leafV (property,value))
Loop t id
let evaluateContinuationPassingStyle tree data =
FoldTree (&&) (||) (not) (fun (prop,value) -> data |> Map.find prop |> ((=) value)) tree data