将函数作为参数传递但获得意外结果

时间:2011-01-19 10:08:43

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

我正在使用具有“高级学生”语言设置的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

1 个答案:

答案 0 :(得分:5)

让我们来看看你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)应用程序的结果值递归调用自身,但它不正确,因为您要收集过程应用程序的计时信息,而不是值(在我们的案例中(add1 41)的值)。只需在true的定义中用fn替换many

(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 )作为第二个参数传递给很多({{1} })。如您所知,Scheme中的函数应用程序表达为fn表达式:

()

示例输出:

(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)(thunk)结果的应用,(lambda () (fact 10000)正是你想要传递它(表达式)并显示正确的时间信息。

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