我的代码出现了“应用程序:不是过程”的错误信号或“调用非过程”的错误信号。

7
在执行我的代码时,不同的Scheme实现会出现以下错误:
Racket:
application: not a procedure;
 expected a procedure that can be applied to arguments
  given: '(1 2 3)
  arguments...:

伊卡洛斯:

Unhandled exception
 Condition components:
   1. &assertion
   2. &who: apply
   3. &message: "not a procedure"
   4. &irritants: ((1 2 3))

鸡肉:

Error: call of non-procedure: (1 2 3)

甘比特:

*** ERROR IN (console)@2.1 -- Operator is not a PROCEDURE
((1 2 3) 4)

MIT Scheme:

;The object (1 2 3) is not applicable.
;To continue, call RESTART with an option number:
; (RESTART 2) => Specify a procedure to use in its place.
; (RESTART 1) => Return to read-eval-print level 1.

Chez Scheme:

Exception: attempt to apply non-procedure (1 2 3)
Type (debug) to enter the debugger.

Guile:

ERROR: In procedure (1 2 3):
ERROR: Wrong type to apply: (1 2 3)

Chibi: 赤壁:
ERROR in final-resumer: non procedure application: (1 2 3)

@LeifAndersen 我实际上使用了 (test 4),其中 test 绑定到 (1 2 3),但选择不包括它,因为除了它不是一个过程之外,它并不重要 :-) - Sylwester
哦,好的,我会撤销那个编辑。 :) - Leif Andersen
嘿,小猫咪!我看到你在Stack Overflow上给出了a fulsome answer here。不幸的是,问题的作者是个忘恩负义的人,已经删除了他们的帖子,也把你的答案一并删除了。我们是否应该重新发布这个问题,以便您的答案可以重新发布?我对Scheme一无所知,所以我不知道这对未来的读者是否有用。 - halfer
好的,谢谢 - 在英国这里不是假期,但明天是周末,那就很好了。祝你假期愉快,周末愉快! - halfer
1个回答

14
为什么会发生这种情况
Scheme的过程/函数调用看起来像这样:
(operator operand ...)

操作符和操作数都可以是变量,比如test+,它们会被计算为不同的值。为了使过程调用正常工作,操作符的值必须是一个过程。从错误信息来看,test似乎不是一个过程,而是一个列表(1 2 3)
操作符和操作数也可以是表达式,所以像((proc1 4) 5)这样的语法是有效的,预期调用(proc1 4)会返回一个过程,然后用5作为它唯一的参数进行调用。

导致这些错误的常见错误。

试图对表达式进行分组或创建一个块

(if (< a b)
    ((proc1)
     (proc2))
    #f)
 

当谓词/测试为真时,Scheme假设将尝试同时评估(proc1)(proc2),然后由于括号的存在,它将调用(proc1)的结果。在Scheme中创建一个块,您可以使用begin
(if (< a b)
    (begin 
      (proc1)
      (proc2))
    #f)

在这个例子中,(proc1)只是为了效果而调用,表单的结果将是最后一个表达式(proc2)的结果。 过程的遮蔽
(define (test list)
  (list (cdr list) (car list)))

这里的参数被称为list,这使得在调用期间过程list不可用。在Scheme中,一个变量只能是过程或不同的值,最近的绑定是在操作符和操作数位置上获得的绑定。这是Common Lisp程序员常犯的一个典型错误,因为在CL中他们可以在不干扰函数list的情况下使用list作为参数。 cond中包装变量
(define test #t) ; this might be result of a procedure

(cond 
  ((< 5 4) result1)
  ((test) result2)
  (else result3))

除了谓词表达式(< 5 4)外,(test)看起来是正确的,因为它是一个被检查真实性的值,但它更类似于else术语,应该这样写:
(cond 
  ((< 5 4) result1)
  (test result2)
  (else result3))

一个本应返回一个过程的过程,并不总是返回一个过程。
由于Scheme不强制返回类型,你的过程在某种情况下可以返回一个过程,而在另一种情况下返回一个非过程的值。
(define (test v)
  (if (> v 4) 
      (lambda (g) (* v g))
      '(1 2 3)))

((test 5) 10) ; ==> 50
((test 4) 10) ; ERROR! application: not a procedure

未定义的值,如#<void>#!void#<undef>#<unspecified>
通常这些是由于改变形式(如set!set-car!set-cdr!define)返回的值。
(define (test x)
  ((set! f x) 5))

(test (lambda (x) (* x x)))

这段代码的结果是不确定的,因为set!可以返回任何值,我知道一些Scheme的实现(如MIT Scheme)实际上返回绑定值或原始值,结果可能是2510,但在许多实现中,你会得到一个常量值,如#<void>,并且由于它不是一个过程,你会得到相同的错误。依赖于一个实现使用不完全规范的方法会导致你的代码不可移植。 参数传递顺序错误 想象一下你有一个这样的函数:
(define (double v f)
  (f (f v)))

(double 10 (lambda (v) (* v v))) ; ==> 10000

如果您错误地交换了参数:
(double (lambda (v) (* v v)) 10) ; ERROR: 10 is not a procedure

在高阶函数中,比如`fold`和`map`,如果没有按照正确的顺序传递参数,将会产生类似的错误。 试图像Algol派生语言一样应用 在像JavaScript和C++这样的Algol语言中,当试图用参数`arg`应用`fun`时,看起来像这样:
fun(arg)

在Scheme中,这被解释为两个独立的表达式。
fun   ; ==> valuates to a procedure object
(arg) ; ==> call arg with no arguments

正确的方式是使用funarg作为参数进行应用:
(fun arg)

多余的括号
这是一般的“捕捉所有”其他错误。在Scheme中,像((+ 4 5))这样的代码是无法工作的,因为该表达式中的每一组括号都是一个过程调用。你不能随意添加括号,因此需要保持(+ 4 5)
为什么允许这些错误发生?
操作符位置的表达式和允许将变量作为库函数调用的功能为语言提供了表达能力。当你习惯了这些功能后,你会喜欢上它们。
这是abs的一个例子:
(define (abs x)
  ((if (< x 0) - values) x))

这个代码在执行时会在(- x)(values x)之间切换(identity函数返回其参数),正如你所看到的,它调用了表达式的结果。下面是一个使用cps的copy-list的示例。
(define (copy-list lst)
  (define (helper lst k)
    (if (null? lst)
        (k '())
        (helper (cdr lst)
                (lambda (res) (k (cons (car lst) res))))))
  (helper lst values))

请注意,k是我们传递给函数的变量,并且它被作为函数调用。如果我们传递给它的不是一个函数,你将会得到相同的错误。
这是否只适用于Scheme语言?
当然不是。所有具有一个命名空间并且可以将函数作为参数传递的语言都会面临类似的挑战。下面是一些具有类似问题的JavaScript代码:
function double (f, v) {
  return f(f(v));
}

double(v => v * v, 10); // ==> 10000
double(10, v => v * v);
; TypeError: f is not a function
;     at double (repl:2:10)

// similar to having extra parentheses 
function test (v) {
  return v;
}

test(5)(6); // == TypeError: test(...) is not a function

// But it works if it's designed to return a function:
function test2 (v) {
  return v2 => v2 + v;
}

test2(5)(6); // ==> 11

函数copy-list没有返回值!我对“value”过程的理解不够,无法进行修正。 - Georg Fuss
@GeorgFuss 在单参数调用中,它的工作方式与恒等函数(lambda (v) v)相同。哪个Scheme实现有一个不起作用的values - Sylwester
1
太棒了!这是我见过的最好的答案之一。 - Lassi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接