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
Optimization 在函数内部多次调用递归函数的情况下,这个F#函数尾部是递归的吗?_Optimization_F#_Tail Recursion_Tail Call Optimization - Fatal编程技术网

Optimization 在函数内部多次调用递归函数的情况下,这个F#函数尾部是递归的吗?

Optimization 在函数内部多次调用递归函数的情况下,这个F#函数尾部是递归的吗?,optimization,f#,tail-recursion,tail-call-optimization,Optimization,F#,Tail Recursion,Tail Call Optimization,关于尾部递归函数有几个问题,例如and,但找不到与以下类似的问题 我的理解是,尾部调用优化函数应该在其最后一次调用中返回一个累积值,而无需进行任何进一步计算。例如,使用阶乘函数很容易理解,它可以优化成循环。但在其他情况下,例如在以下情况下,最后一次呼叫是什么并不总是显而易见的?当函数在主体中被多次递归调用时,会有很多这样的函数 Brian建议了一种方法来找出答案,但我不确定如何使尾部调用优化。我可以将--tailcalls标志传递给编译器自动执行,但它总是成功吗 f和g返回相同的类型 type

关于尾部递归函数有几个问题,例如and,但找不到与以下类似的问题

我的理解是,尾部调用优化函数应该在其最后一次调用中返回一个累积值,而无需进行任何进一步计算。例如,使用阶乘函数很容易理解,它可以优化成循环。但在其他情况下,例如在以下情况下,最后一次呼叫是什么并不总是显而易见的?当函数在主体中被多次递归调用时,会有很多这样的函数

Brian建议了一种方法来找出答案,但我不确定如何使尾部调用优化。我可以将
--tailcalls
标志传递给编译器自动执行,但它总是成功吗

f
g
返回相同的类型

type T = T of int * T list

let rec myfunc f (T (x,xs)) =
    if (List.isEmpty xs) then f x
    else 
        List.fold g acc (List.map (fun xxs -> myfunc f xxs) xs)
如果您能帮助我们优化上述代码,我们将不胜感激

我的理解是,尾部调用优化函数应该在最后一次调用中返回一个累积值

差不多。尾部递归是当递归调用都出现在尾部位置时。尾部位置表示调用者直接从被调用者返回结果

在下面,最后一个电话是什么

在尾部位置有两个呼叫。首先,调用
f
。其次,调用
List.fold
。递归调用不在尾部位置,因为其返回值不是由调用方直接返回的

另外,使用模式匹配,而不是
isEmpty
和friends

如果您能帮助我们优化上述代码,我们将不胜感激

在任何人能够帮助您编写尾部递归版本之前,您必须发布工作代码或至少一个规范。一般来说,最简单的解决方案是以连续传递样式或命令式样式编写

我的理解是,尾部调用优化函数应该在最后一次调用中返回一个累积值

差不多。尾部递归是当递归调用都出现在尾部位置时。尾部位置表示调用者直接从被调用者返回结果

在下面,最后一个电话是什么

在尾部位置有两个呼叫。首先,调用
f
。其次,调用
List.fold
。递归调用不在尾部位置,因为其返回值不是由调用方直接返回的

另外,使用模式匹配,而不是
isEmpty
和friends

如果您能帮助我们优化上述代码,我们将不胜感激


在任何人能够帮助您编写尾部递归版本之前,您必须发布工作代码或至少一个规范。一般来说,最简单的解决方案是以连续传递样式或命令式样式编写。

正如Jon已经说过的,您的函数不是尾部递归的。基本问题是它需要多次递归地调用自己(对于
xs
列表中的每个元素调用一次,这是在传递给
list.map
的lambda函数中完成的)

在实际需要进行多个递归调用的情况下,使用延续传递样式或命令堆栈可能是唯一的选项。continuations背后的思想是,每个函数都将接受另一个函数(作为最后一个参数),该函数应该在结果可用时执行

以下示例显示了正常版本(左侧)和基于延续的版本(右侧)

要以延续传递样式编写函数,还需要使用基于延续的
fold
函数。通过将
map
中的操作移动到
fold
的lambda函数中,可以首先避免使用
map

List.fold g acc (List.map (fun xxs -> myfunc f xxs) xs)
变成:

List.fold (fun state xxs -> g state (myfunc f xxs)) acc xs
然后,您可以按如下方式重写代码(请注意,您在问题中未显示的
f
g
现在都是基于延续的函数,因此它们采用了表示延续的附加参数):

List.foldcontent
函数是基于延续的
fold
版本,可以编写如下内容:

module List = 
  let rec foldCont f (state:'TState) (list:'T list) cont =
    match list with
    | [] -> cont state
    | x::xs -> foldCont f state xs (fun state ->
        f x state cont)

由于您没有发布完整的工作示例,我无法真正测试代码,但我认为它应该可以工作。

正如Jon所说,您的函数不是尾部递归的。基本问题是它需要多次递归地调用自己(对于
xs
列表中的每个元素调用一次,这是在传递给
list.map
的lambda函数中完成的)

在实际需要进行多个递归调用的情况下,使用延续传递样式或命令堆栈可能是唯一的选项。continuations背后的思想是,每个函数都将接受另一个函数(作为最后一个参数),该函数应该在结果可用时执行

以下示例显示了正常版本(左侧)和基于延续的版本(右侧)

要以延续传递样式编写函数,还需要使用基于延续的
fold
函数。通过将
map
中的操作移动到
fold
的lambda函数中,可以首先避免使用
map

List.fold g acc (List.map (fun xxs -> myfunc f xxs) xs)
变成:

List.fold (fun state xxs -> g state (myfunc f xxs)) acc xs
然后,您可以按如下方式重写代码(请注意,您在问题中未显示的
f
g
现在都是基于延续的函数,因此它们采用了表示延续的附加参数):

List.foldcontent
函数是基于延续的
fold
版本,可以编写如下内容:

module List = 
  let rec foldCont f (state:'TState) (list:'T list) cont =
    match list with
    | [] -> cont state
    | x::xs -> foldCont f state xs (fun state ->
        f x state cont)

由于您没有发布完整的工作示例,我无法真正测试代码,但我认为它应该可以工作。

您的答案中不需要
mapCont
或cons吗?我花了一段时间才完成