我的代码表示错误"应用程序:不是程序"或者"致电非程序"

时间:2018-01-02 17:00:48

标签: scheme runtime-error racket

在执行代码期间,我在不同的Scheme实现中遇到以下错误:

球拍:

application: not a procedure;
 expected a procedure that can be applied to arguments
  given: '(1 2 3)
  arguments...:

的Ikarus:

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)

麻省理工学院计划:

;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.

狡诈:

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)

1 个答案:

答案 0 :(得分:9)

为什么会这样?

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中的过程或不同的值,最接近的绑定是您在操作符和操作数位置获得的绑定。这将是普通lispers的一个典型错误,因为在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!可以返回任何值,我知道一些方案实现(如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

foldmap这样的高阶函数中,如果没有以正确的顺序传递参数,则会产生类似的错误。

尝试按照Algol派生语言申请

在algol语言中,例如JavaScript和C ++,当尝试将fun应用于参数arg时,它看起来像:

fun(arg)

这被解释为Scheme中的两个独立表达式:

fun   ; ==> valuates to a procedure object
(arg) ; ==> call arg with no arguments

fun作为参数应用arg的正确方法是:

(fun arg)

多余的括号

这是一般的“抓住所有”其他错误。像((+ 4 5))这样的代码在Scheme中不起作用,因为此表达式中的每组括号都是过程调用。您根本无法添加任意数量的内容,因此您需要保留(+ 4 5)

为什么要允许这些错误发生?

运算符位置的表达式允许将变量作为库函数调用,为语言提供了强大的功能。这些是你习惯的时候会喜欢的功能。

以下是abs的示例:

(define (abs x)
  ((if (< x 0) - values) x))

这在执行(- x)(values x)(返回其参数的标识)之间切换,您可以看到它调用表达式的结果。以下是copy-list使用cps的示例:

(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