【问题标题】:Passing a function as a parameter but getting unexpected results将函数作为参数传递但得到意外结果
【发布时间】:2011-06-11 15:53:44
【问题描述】:

我正在使用带有“高级学生”语言设置的 Racket,我在尝试编写一个接受一个函数、执行 n 次并报告每次运行所用时间的函数时遇到了困难。这就是我目前所得到的。

(define (many n fn)
  (cond
    [(= n 0) true]
    [else (many (sub1 n) (local ((define k (time fn))) k))]))

我有一个名为 fact 的函数,它计算一个数字的阶乘。

(define (fact n)
  (cond
    [(= 0 n) 1]
    [else (* n (fact (- n 1)))]))

如果我评估(time (fact 10000)),我会得到合理的 cpu、real 和 gc 时间以及大量结果。一切顺利。

但是,当我尝试评估 (many 3 (fact 10000)) 时,我得到:

cpu time: 0 real time: 0 gc time: 0
cpu time: 0 real time: 0 gc time: 0
cpu time: 0 real time: 0 gc time: 0
true

尽管作为参数传递,为什么函数没有评估 fact

【问题讨论】:

  • 您传递的不是函数,而是() 中表达式的求值结果。

标签: lambda functional-programming scheme racket operator-precedence


【解决方案1】:

让我们来看看你many 做了什么。首先你定义它:

(define (many n fn)
  (cond
    [(= n 0) true]
    [else (many (sub1 n) 
                (local ((define k (time fn))) 
                       k))]))

然后调用它:

> (many 3 (add1 41))
cpu time: 0 real time: 0 gc time: 0
cpu time: 0 real time: 0 gc time: 0
cpu time: 0 real time: 0 gc time: 0
#t
> 

many 递归调用自身时,每次迭代会发生什么:

(define (many 3 42)
  (cond
    [(= 3 0) true]
    [else (many (sub1 3) 
                (local ((define k (time 42))) 
                       42))]))

(define (many 2 42)
  (cond
    [(= 2 0) true]
    [else (many (sub1 2) 
                (local ((define k (time 42))) 
                       42))]))

(define (many 1 42)
  (cond
    [(= 1 0) true]
    [else (many (sub1 1) 
                (local ((define k (time 42))) 
                       42))]))

(define (many 0 42)
  (cond
    [(= 0 0) true]
    [else (many (sub1 0) 
                (local ((define k (time 42))) 
                       42))]))

你对many的定义递归调用了第一个(time fn)应用程序的结果值,但是不正确,因为你想为你的程序应用程序收集计时信息,而不是值(@987654329的值@ 在我们的例子中)。只需在 many 的定义中将 true 替换为 fn

(define (many n fn)
  (cond
    [(= n 0) fn]
    [else (many (sub1 n) 
                (local ((define k (time fn))) 
                 k))]))

你会得到以下结果:

> (many 3 (add1 41))
cpu time: 0 real time: 0 gc time: 0
cpu time: 0 real time: 0 gc time: 0
cpu time: 0 real time: 0 gc time: 0
cpu time: 0 real time: 0 gc time: 0
42
>

您会看到,每次递归调用中的fn 都等于42。发生这种情况是因为许多(如果不是全部)FP 语言使用Applicative order 进行评估,并且(add1 41) 在第一次调用many 之前被评估。

因此我们必须使用lambda 来确保函数(在我们的例子中为thunk)作为第二个参数(fn)传递给许多人。如您所知,Scheme 中的函数应用表示为 () 周围的表达式:

(define (many n fn)  
  (time (fn))  
  (if (= n 0) 
      true
      (many (sub1 n) fn)))

示例输出:

> (many 3 (lambda () (fact 10000)))
cpu time: 2734 real time: 2828 gc time: 1922
cpu time: 906 real time: 953 gc time: 171
cpu time: 891 real time: 953 gc time: 204
cpu time: 938 real time: 984 gc time: 251
#t
>

正如您在上面看到的(fn) 执行函数(lambda () (fact 10000)(thunk)的结果的应用,time 得到您想要传递的内容(一个表达式)并显示正确的时间信息。

希望对您有所帮助。如果我错了,请纠正我。

【讨论】:

  • @leppie:谢谢!我喜欢 FP,我认为鼓励它的学习是有道理的。
  • 您好 Yasir,感谢您的回复。我现在理解得更好了,所以似乎评估顺序是让我绊倒的原因。但是,在“中级”和“高级”学生设置下,没有“如果”功能。因此,尽管我了解原因,但我仍然无法解决此问题。感谢您的耐心和时间。
  • @Greenhorn:如果您不特别需要“中级”和“高级”学生设置,您可能只需按键盘上的 Ctrl+L 并选择“使用源中声明的语言”单选,因为你知道 lambdas 和其他一些“高级”的东西;-)
猜你喜欢
  • 2020-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多