为什么会发生
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-lispers 犯的一个典型错误,因为在 CL 中它们可以list
用作参数而不会弄乱 function 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 方案实际上返回绑定值或原始值,结果将是25
or 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 派生语言中应用
在 algol 语言中,如 JavaScript 和 C++,当尝试fun
使用参数时arg
,它看起来像:
fun(arg)
这在 Scheme 中被解释为两个单独的表达式:
fun ; ==> valuates to a procedure object
(arg) ; ==> call arg with no arguments
fun
应用arg
as 参数的正确方法是:
(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