【问题标题】:why am I getting "application not a procedure"?为什么我收到“申请不是程序”?
【发布时间】:2014-11-22 16:34:05
【问题描述】:

我正在尝试编写一个通过迭代过程计算 f 的过程。函数 f 由以下规则定义:

f(n) = n,如果 n

f(n) = f(n - 1) + 2f(n - 2) + 3f(n - 3) + 4f(n - 4),如果 n >= 4。

这是我的程序:

(define (f n)
  (define (newF temp n)
    (letrec ((first (- n 1))
             (second (- n 2))
             (third/fourth (- n 3))
             (fifth (- n 4)))
      (define (d)
        ((if (< first 4) (set! temp (+ temp first)) (newF temp first))
         (if (< second 4) (set! temp (+ temp (* second 2))) (newF temp second))
         (if (< third/fourth 4) (set! temp (+ temp (* third/fourth 3) (* third/fourth 4))) (newF temp third/fourth))
         (if (< fifth 4) (set! temp (+ temp (* fifth 4)))(newF temp fifth))))
      (d))
    temp)
  (newF 0 n))

不幸的是,当我运行 (f 7) 时,我得到了这个错误(它引用了 if 语句体):

application: not a procedure;
 expected a procedure that can be applied to arguments
  given: #<void>
  arguments...:
   #<void>
   #<void>
   #<void>

有人知道为什么以及如何解决它吗?

【问题讨论】:

标签: recursion functional-programming scheme racket tail-recursion


【解决方案1】:

出现报告的错误是因为在 d 辅助过程中的表达式周围有几个不必要的 ()。您不必这样做,过程中的所有表达式都隐含在begin 中,没有必要使用() 来表示它们都是代码块的一部分——此外,当我们用() 包围一个表达式,解释器会尝试应用该表达式,就好像它是一个过程一样 - 因此出现application: not a procedure 错误。

除此之外,一些关于风格的问题是有序的。在 Scheme 中,我们尽量避免使用set!,这不是解决这种编程语言问题的惯用方式。此外,您正在覆盖 Racket 中的一些内置过程:firstsecondfifth 是已在使用的名称,您不应将它们用于您自己的变量。

【讨论】:

    【解决方案2】:

    基于your previous question,您将以一种完全命令式的方式进行处理(当然,这是不正确的,否则您不会问这个问题),这不是Scheme 喜欢的工作方式。这是编写函数的一种函数式(但不是迭代式)方式:

    (define (f n)
      (if (< n 4)
          n
          (+ (f (- n 1)) (* 2 (f (- n 2))) (* 3 (f (- n 3))) (* 4 (f (- n 4))))))
    

    现在,让我们看看如何迭代地编写它。首先,让我们看看迭代斐波那契函数是如何编写的:

    (define (fib n)
      (let loop ((i 0) (a 0) (b 1))
        (if (>= i n)
            a
            (loop (+ i 1) b (+ a b)))))
    

    这和下面的 JavaScript 做同样的事情:

    fib = function (n) {
      return (function loop(i, a, b) {
                return i >= n ? a : loop(i + 1, b, a + b);
              })(0, 0, 1);
    };
    

    注意iab 是如何实际更新的。我们使用尾递归来更新值,而不是通过重新分配/变异(即,在 JS 中不使用 = 或在 Scheme 中不使用 set!)。我最近写了一个关于why tail-recursion is so important in Scheme的答案。

    所以,你会用你的函数做类似的事情:

    (define (f n)
      (let loop ((i 0) (a 0) (b 1) (c 2) (d 3))
        (if (>= i n)
            a
            (loop (+ i 1) b c d (+ d c c b b b a a a a)))))
    

    【讨论】:

    • 我从来不知道可以使用 let 创建一个过程。有趣
    • 确实!我也写了一篇关于 named let 的帖子,解释了它们在幕后是如何工作的。
    猜你喜欢
    • 2017-06-18
    • 2014-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多