如何使重试函数尾递归?
How can I make a retry function tail recursive?
我有一个与 Scott's Railway Oriented Programming 中使用的 Result
类型相似的可区分联合。为了简单起见,这里稍微简化一下:
type ErrorMessage = ErrorMessage of string
type ValidationResult<'a> =
| Success of 'a
| Error of ErrorMessage
我有一个相应的模块 ValidationResult
,其中包含作用于这些 ValidationResult
的函数,其中一个是允许参数 [=16= 的递归 retryable
函数],如果 ValidationResult
是 Error
:
,则再次调用(例如从 stdin
读取)
module ValidationResult
let doubleMap success error = function
| Success x -> success x
| Error e -> error e
let rec retryable errorHandler f =
let result = f ()
let retry e =
errorHandler e
retryable errorHandler f
doubleMap id retry result
但它不是尾递归的,我想将其转换为尾递归。我该怎么做?
只需删除对 doubleMap
的调用即可:
let rec retryable errorHandler f =
match f() with
| Success x -> x
| Error e ->
errorHandler e
retryable errorHandler f
F# 编译器以两种不同的方式编译尾递归函数。
- 如果函数简单(直接调用自身),则编译成循环
- 如果尾递归涉及多个不同的函数(甚至函数值),则编译器使用
.tail
IL 指令进行尾调用。这也是尾调用,但由 .NET 运行时处理,而不是由 F# 编译器消除。
在你的例子中,retryable
函数 是 已经是尾递归的,但它是第二种。大牛的回答很简单,所以就变成了第一种。
但是,您可以保留该函数,因为它是尾递归的。唯一需要注意的是,编译器在 Debug 模式下默认不会生成 .tail
指令(因为它会弄乱调用堆栈),因此您需要显式启用它(在项目选项中,勾选 "Generate tail calls").
我有一个与 Scott's Railway Oriented Programming 中使用的 Result
类型相似的可区分联合。为了简单起见,这里稍微简化一下:
type ErrorMessage = ErrorMessage of string
type ValidationResult<'a> =
| Success of 'a
| Error of ErrorMessage
我有一个相应的模块 ValidationResult
,其中包含作用于这些 ValidationResult
的函数,其中一个是允许参数 [=16= 的递归 retryable
函数],如果 ValidationResult
是 Error
:
stdin
读取)
module ValidationResult
let doubleMap success error = function
| Success x -> success x
| Error e -> error e
let rec retryable errorHandler f =
let result = f ()
let retry e =
errorHandler e
retryable errorHandler f
doubleMap id retry result
但它不是尾递归的,我想将其转换为尾递归。我该怎么做?
只需删除对 doubleMap
的调用即可:
let rec retryable errorHandler f =
match f() with
| Success x -> x
| Error e ->
errorHandler e
retryable errorHandler f
F# 编译器以两种不同的方式编译尾递归函数。
- 如果函数简单(直接调用自身),则编译成循环
- 如果尾递归涉及多个不同的函数(甚至函数值),则编译器使用
.tail
IL 指令进行尾调用。这也是尾调用,但由 .NET 运行时处理,而不是由 F# 编译器消除。
在你的例子中,retryable
函数 是 已经是尾递归的,但它是第二种。大牛的回答很简单,所以就变成了第一种。
但是,您可以保留该函数,因为它是尾递归的。唯一需要注意的是,编译器在 Debug 模式下默认不会生成 .tail
指令(因为它会弄乱调用堆栈),因此您需要显式启用它(在项目选项中,勾选 "Generate tail calls").