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
F# 包装递归函数以计算函数调用的数量_F# - Fatal编程技术网

F# 包装递归函数以计算函数调用的数量

F# 包装递归函数以计算函数调用的数量,f#,F#,假设我有一个递归函数,我想知道每个输入值函数调用自己多少次。与其放置printf表达式或更改返回类型以包含调用数,是否可以用另一个函数“包装”函数来实现这一点?我希望包装函数返回函数调用的数量和原始函数结果。它应该可以跨不同的功能重用 这是我所拥有的,但它不起作用 open System open System.IO open System.Collections.Generic /// example recursive function let rec getfilenames dir =

假设我有一个递归函数,我想知道每个输入值函数调用自己多少次。与其放置printf表达式或更改返回类型以包含调用数,是否可以用另一个函数“包装”函数来实现这一点?我希望包装函数返回函数调用的数量和原始函数结果。它应该可以跨不同的功能重用

这是我所拥有的,但它不起作用

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
,因为它