Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# 这个序列表达式应该是尾部递归的吗?_F#_Stack Overflow_Tail Recursion - Fatal编程技术网

F# 这个序列表达式应该是尾部递归的吗?

F# 这个序列表达式应该是尾部递归的吗?,f#,stack-overflow,tail-recursion,F#,Stack Overflow,Tail Recursion,这个F#seq表达式在我看来是尾部递归的,但我遇到了堆栈溢出异常(启用了尾部调用)。有人知道我错过了什么吗 let buildSecondLevelExpressions expressions = let initialState = vector expressions |> randomize let rec allSeq state = seq { for partial in state do if count partia

这个F#seq表达式在我看来是尾部递归的,但我遇到了堆栈溢出异常(启用了尾部调用)。有人知道我错过了什么吗

let buildSecondLevelExpressions expressions =
    let initialState = vector expressions |> randomize
    let rec allSeq state = seq {
        for partial in state do
            if count partial = 1
            then yield Seq.head partial
            if count partial > 1 || (count partial = 1 && depth (Seq.head partial) <= MAX_DEPTH) then
                let allUns = partial
                                |> pick false 1
                                |> Seq.collect (fun (el, rr) -> (createExpUnaries el |> Seq.map (fun bn -> add rr bn)))
                let allBins = partial  // Careful: this case alone produces result recursivley only if |numbers| is even (rightly!).
                                |> pick false 2
                                |> Seq.collect (fun (el, rr) -> (createExpBinaries el |> Seq.map (fun bn -> add rr bn)))
                yield! allSeq (interleave allBins allUns)
    }
    allSeq initialState
让buildSecondLevelExpressions=
让initialState=向量表达式|>随机化
让rec allSeq state=seq{
对于部分在状态do
如果count partial=1
然后,产生如下部分
如果count partial>1 | |(count partial=1&&depth(Seq.head partial))选择false 1
|>Seq.collect(fun(el,rr)->(createExpUnaries el |>Seq.map(fun bn->add rr bn)))
让allBins=partial//小心:只有当| number |为偶数时(正确!),此案例才会产生递归结果。
|>选择错误2
|>Seq.collect(fun(el,rr)->(createxpbinaries el |>Seq.map(fun-bn->add-rr-bn)))
收益率!allSeq(交错allBins Allens)
}
allSeq初始状态

如果您想知道,
pick
用于生成序列中元素的组合,
interleave
从2个序列中交错元素。
vector
ResizeArray
的构造函数,这不是尾部递归,因为您可以调用recu重复多次。要转换为伪代码:

allSeq(state)
{
    foreach (partial in state)
    {
        if (...)
        {
            yield ...
        }
        if (...)
        {
            ...
            //this could be reached multiple times
            yield! allSeq(...)
        }
    }
}

正如Gideon指出的,这不是尾部递归,因为“状态”列表中还有其他元素要处理。使尾部递归并不简单,因为您需要一些应该处理的元素队列

以下伪代码显示了一种可能的解决方案。我添加了
work
参数,用于存储要完成的剩余工作。每次调用时,我们只处理第一个元素。所有其他元素都添加到队列中。完成后,我们从队列中选择更多工作:

let rec allSeq state work = seq { 
    match state with 
    | partial::rest -> 
        // Yield single thing to the result - this is fine
        if count partial = 1 then yield Seq.head partial 
        // Check if we need to make more recursive calls...
        if count partial > 1 || (* ... *) then 
            let allUns, allBins = // ...
            // Tail-recursive call to process the current state. We add 'rest' to 
            // the collected work to be done after the current state is processed
            yield! allSeq (interleave allBins allUns) (rest :: work)
        else
            // No more processing for current state - let's take remaining
            // work from the 'work' list and run it (tail-recursively)
            match work with 
            | state::rest -> yield! allSeq state rest
            | [] -> () //completed
    | _ -> 
        // This is the same thing as in the 'else' clause above. 
        // You could use clever pattern matching to handle both cases at once
        match work with 
        | state::rest -> yield! allSeq state rest
        | [] -> () } //completed

我找不到序列表达式中哪些调用在F#中处于尾部位置的定义,因此我强烈建议不要编写依赖于当前实现语义的代码,即这是未定义的行为

例如,尝试枚举(例如,应用
Seq.length
)以下序列会导致堆栈溢出:

let rec xs() = seq { yield! xs() }
但是,正如托马斯指出的那样,以下方法确实有效:

let rec xs n = seq { yield n; yield! xs(n+1) }
我的建议是始终使用
Seq.unfold
替换递归序列表达式。在这种情况下,您可能希望累积要完成的工作(例如,当您递归到左分支时,您将右分支推到累加器中的堆栈上)

FWIW,甚至弄错了。它给出了以下用于展平树的代码:

type Tree<'a> =
   | Tree of 'a * Tree<'a> * Tree<'a>
   | Leaf of 'a

let rec inorder tree =
    seq {
      match tree with
          | Tree(x, left, right) ->
               yield! inorder left
               yield x
               yield! inorder right
          | Leaf x -> yield x
    } 
类型树
|叶
让我们记录顺序树=
序号{
匹配树
|树(x,左,右)->
让开!按左顺序
产量x
屈服!为了正确
|叶x->产量x
} 

他们自己的代码在输入左边的深树时杀死F#与堆栈溢出交互。

yield!allSeq…
在for循环中。它不是尾部递归。@Yin:
yield!
根本不是尾部递归,例如枚举
let rec xs()=seq{yield!xs()}
还有堆栈溢出。
yield!
在尾部调用位置时是尾部递归的,但在Jon发布的退化示例中似乎不起作用(我认为这是一个bug)。然而,在所有现实的例子中,这都应该是可行的。@Tomas Petricek——这是一个延续吗?说真的,我在试图理解延续的概念,我觉得这很像。你测试过它吗?我认为这是错误的:你永远不应该在
yield
…@Jon:我没有测试它(这是一个非常不完整的片段),但我认为,
allSeq
的所有递归使用都处于尾部调用位置,并使用
yield!
返回-序列表达式编译器看起来正是这种情况。@Onorio:Continuations类似,但是一个更一般的概念-您传递的函数应该是e run(在上面的例子中,
work
将是函数,我们将在完成当前处理时执行它)。在我的书()中有一个更清楚的解释,但不幸的是,这一部分在免费的示例章节中不可用…@Tomas:如果这是真的,您肯定能够枚举
let rec xs()=seq{yield!xs()}
但是堆栈溢出了。我认为你错了:yield!、let!、do!可以是尾部递归的,但不是在这种情况下。+1表示Seq.unfold,谢谢。@Mau:Tomas给了我一个反例,其中递归调用可以位于序列表达式的尾部位置。但是,如果没有任何关于何时发生这种情况的线索,我绝对不会使用dep结束。@Jon:我认为你发布的案例可能被认为是F#中的一个bug,因为所有的
yield!
在尾部调用位置都应该优化。我会给F#团队发送一个链接。@Jon:我不明白你说的“F#语言引用出错”是指什么。它错在哪里?它并没有声称解决方案不会堆栈溢出(我认为这对任何函数式程序员来说都不奇怪)。@Jon,@Tomas-一般来说,是否是“尾部递归”正确工作取决于所使用的计算生成器。我要说,Jon的病态示例确实显示了一个编译器错误,因为使用规范对SequenceBuilder的定义而不是
seq
会导致无限循环,而不是堆栈溢出。