F# 包装递归函数以计算函数调用的数量
假设我有一个递归函数,我想知道每个输入值函数调用自己多少次。与其放置printf表达式或更改返回类型以包含调用数,是否可以用另一个函数“包装”函数来实现这一点?我希望包装函数返回函数调用的数量和原始函数结果。它应该可以跨不同的功能重用 这是我所拥有的,但它不起作用F# 包装递归函数以计算函数调用的数量,f#,F#,假设我有一个递归函数,我想知道每个输入值函数调用自己多少次。与其放置printf表达式或更改返回类型以包含调用数,是否可以用另一个函数“包装”函数来实现这一点?我希望包装函数返回函数调用的数量和原始函数结果。它应该可以跨不同的功能重用 这是我所拥有的,但它不起作用 open System open System.IO open System.Collections.Generic /// example recursive function let rec getfilenames dir =
open System
open System.IO
open System.Collections.Generic
/// example recursive function
let rec getfilenames dir =
seq {
yield Directory.GetFiles dir
for x in Directory.GetDirectories dir do yield! getfilenames x}
/// function to count the number of calls a recursive function makes to itself
let wrapped (f: 'a -> 'b) =
let d = new Dictionary<'a, int>()
fun x ->
let ok, res = d.TryGetValue(x)
if ok then d.[x] <- d.[x] + 1
else
d.Add(x, 1)
d, f x
> let f = wrapped getfilenames
let calls, res = f "c:\\temp";;
val f : (string -> Dictionary<string,int> * seq<string []>)
val res : seq<string []>
val calls : Dictionary<string,int> = dict [("c:\temp", 1)]
开放系统
开放系统
open System.Collections.Generic
///递归函数示例
让rec getfilenames dir=
序号{
yield Directory.GetFiles目录
对于Directory.GetDirectories目录中的x,不产生!getfilenames x}
///函数来计算递归函数对自身进行的调用数
让包装(f:'a->'b)=
让d=new Dictionary这是行不通的,因为getfilenames
被定义为调用getfilenames
,而不是任何其他函数,尤其是之后定义的函数。因此,一旦包装器调用函数,函数就会忽略包装器并开始调用自身
您需要做的是通过提供要作为参数递归调用的函数,将递归从getfilenames
函数移到另一个函数中
let body recfun dir =
seq {
yield Directory.GetFiles dir
for x in Directory.GetDirectories dir do yield! recfun x}
let rec getfilenames dir = body getfilenames dir
现在,您可以在插入递归函数之前包装body
:
let wrap f =
let d = (* ... *) in
d, fun recfun x ->
let ok, res = d.TryGetValue(x)
if ok then d.[x] <- d.[x] + 1
else d.Add(x, 1)
f recfun x
let calls, counted_body = wrap body
let getfilenames dir = counted_body getfilenames dir
let wrap f=
设d=(*…*)在
d、 fun recfun x->
设ok,res=d.TryGetValue(x)
如果确定,那么d.[x]将不起作用,因为getfilenames
被定义为调用getfilenames
,而不是任何其他函数,尤其是之后定义的函数。因此,一旦包装器调用函数,函数就会忽略包装器并开始调用自身
您需要做的是通过提供要作为参数递归调用的函数,将递归从getfilenames
函数移到另一个函数中
let body recfun dir =
seq {
yield Directory.GetFiles dir
for x in Directory.GetDirectories dir do yield! recfun x}
let rec getfilenames dir = body getfilenames dir
现在,您可以在插入递归函数之前包装body
:
let wrap f =
let d = (* ... *) in
d, fun recfun x ->
let ok, res = d.TryGetValue(x)
if ok then d.[x] <- d.[x] + 1
else d.Add(x, 1)
f recfun x
let calls, counted_body = wrap body
let getfilenames dir = counted_body getfilenames dir
let wrap f=
设d=(*…*)在
d、 fun recfun x->
设ok,res=d.TryGetValue(x)
如果ok,那么正如Victor所指出的,d[x],您不能接受递归函数并将某些行为“注入”到发生递归调用的地方(因为函数已经完成)。您需要为此提供一些扩展点。在Victor的解决方案中,这是通过将要递归调用的函数作为参数来实现的,这是最通用的解决方案
一个更简单的选择是使用F#值递归,它允许您创建函数值并在其声明中使用它。通过调用另一个向函数添加某些行为(例如计数)的函数,可以使用此函数创建递归函数:
在lambda函数内部,我们可以直接访问我们定义的函数,因此不需要将传递的函数作为附加参数递归调用。函数counted
仅包装给定函数f
,并添加一些功能:
let counted f =
let count = ref 0
(fun x ->
count := !count + 1;
printfn "call: %d" (!count)
f x)
由于值递归,功能将被添加到阶乘
函数中(因此,当它调用自身时,它将调用具有附加计数支持的版本)。正如Victor指出的,您不能使用递归函数并“注入”一些行为到递归调用发生的地方(因为函数已经完成了)。您需要为此提供一些扩展点。在Victor的解决方案中,这是通过将要递归调用的函数作为参数来实现的,这是最通用的解决方案
一个更简单的选项是使用F#值递归,它允许您创建函数值并在其声明中使用它。您可以使用它通过调用另一个函数来创建递归函数,该函数为函数添加了一些行为(例如计数):
在lambda函数内部,我们可以直接访问正在定义的函数,因此无需将传递函数作为附加参数递归调用。函数counted
只需包装给定函数f
,并添加一些功能:
let counted f =
let count = ref 0
(fun x ->
count := !count + 1;
printfn "call: %d" (!count)
f x)
由于值递归,该功能将添加到阶乘
函数中(因此,当它调用自身时,它将调用具有附加计数支持的版本).我有两个问题,注释字典,现在应该使用什么类型?在wrap函数中,最后一行f recfun x我不明白它是如何工作的:(字典:和以前一样(唯一的区别是现在f的类型是('a->'b)->'a->'b
,因为它需要recfun
).recfun
表示子目录上的f
应该调用的函数(因为f
不再是递归函数)。这使您可以使用counted函数处理子目录let调用,counted_body=wrap body;;错误FS0030:值限制。已推断值“counted_body”的泛型类型为val counted_body:((字符串->'\u a)->string->seq)当“_a:>seq听起来像F”不能推断出('a->'b)->'a->'b
中的类型是相同的(只是第一个可以转换为第二个)。你必须手动将F
注释为('a->'b>)-'a->'b
和recfun
注释为'a->'b
。这种技术被称为“解开递归结”。我被困在两点上,评论字典,现在应该有什么类型?在wrap函数的最后一行f recfun x中,我不明白它是如何工作的:(字典:和以前一样(唯一的区别是现在f的类型是('a->'b)->'a->'b
,因为它