为什么会这样
方案过程/函数调用如下所示:
(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-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) 看起来是正确的,因为它是一个检查thurthness 的值,它与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 方案实际上返回绑定值或原始值,结果将是 @987654359 @ 或 10,但在许多实现中,您会得到一个常量值,例如 #<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++ 等算法语言中,当尝试应用 fun 和参数 arg 时,它看起来像:
fun(arg)
这在 Scheme 中被解释为两个单独的表达式:
fun ; ==> valuates to a procedure object
(arg) ; ==> call arg with no arguments
以arg 作为参数应用fun 的正确方法是:
(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