Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jquery-ui/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么在函数位置禁止函数对象(但允许lambda形式)? 函数位置的lambda形式表达式_Lambda_Syntax_Common Lisp - Fatal编程技术网

为什么在函数位置禁止函数对象(但允许lambda形式)? 函数位置的lambda形式表达式

为什么在函数位置禁止函数对象(但允许lambda形式)? 函数位置的lambda形式表达式,lambda,syntax,common-lisp,Lambda,Syntax,Common Lisp,函数位置的lambda表达式编译得很好: > ((lambda (n) (> n 10)) 42) T 从函数位置的lambda构建的闭包 另一方面: > (defun greater-than (x) (lambda (n) (> n x))) GREATER-THAN > ((greater-than 10) 42) Compile-time error: illegal function call 不起作用 (我显然需要调用FUNCALL使其

函数位置的lambda表达式编译得很好:

> ((lambda (n) (> n 10)) 42)
T
从函数位置的lambda构建的闭包 另一方面:

> (defun greater-than (x)
     (lambda (n) (> n x)))
GREATER-THAN

> ((greater-than 10) 42)
Compile-time error:  illegal function call
不起作用

(我显然需要调用
FUNCALL
使其工作:
(FUNCALL(大于10)42)
=>
T

为什么是这种设计? 我理解为什么将函数对象作为值绑定的符号不能工作,例如:
(let((foo(lambda()42)))(foo))
。分离名称空间和所有这些


但是为什么要禁止函数对象本身处于函数位置?这一决定背后的理由是什么?

主要是“历史原因”,即实现问题,但就函数和值命名空间的分离而言,它在语义上也不明显。运算符位置的表单计算为其函数值。只有符号具有函数值

Lambda
表单是一种特殊情况;您可能会想象它们在函数和值名称空间中计算为函数。然而,这实际上并不正确,只是一种近似/合理化

在我年轻的时候,我设想应该有一个读取器宏镜像
#
,例如
#^
,这样您就可以将某些内容“拉”到函数名称空间中,就像使用
#
将某些内容“拉”到值名称空间中一样。然而,它只会扩展到
funcall

(#^(生成加法器5)3)
≡ <代码>(funcall(生成加法器5)3)

我现在很不愿意为这种琐碎的用途引入reader宏。放松点,只需使用funcall,它最终的效果非常好。

Common Lisp的整个两个名称空间性质有其优点也有其缺点。CL Preference获得两个名称空间的好处和原因是它更容易生成编译器和高效代码。这是古老的历史,也是CL设计的一个关键选择

当您的代码有一个带有符号或lambda的表单时,它可以很容易地编译成机器代码

第二个您希望复合表达式返回应用的函数,您将无法确定此编译时间(至少不总是,至少在70年代不一定如此)因此,您需要
funcall
的所有功能,即使不在代码中使用它。您也许可以制作一个宏来自动添加
funcall
,但我觉得对于初学者来说,学习Common Lisp的双名称空间将比现在更加困难

性能参数 现在很明显,
(+23)
保证与
(funcall#'+23)相等且效率高出很多倍
即使从它们的外观可以清楚地看出,它们都是相同的,并且非常静态。也许如果我们不需要编写
funcall
,我们会更频繁地使用这种松散的样式,默认情况下,代码的性能会更低。对于您经常看到的方案来说,这可能是正确的

((if (some-fun ..) 
     fun1 
     fun2) 
 3 
 4)
…而不是可能更优化的:

(if (some-fun ..)
    (fun1 3 4)
    (fun2 3 4))
在CL中,选择第二个选项可能更容易,因为第一个选项看起来像:

(funcall (if (some-fun ..) 
             #'fun1
             #'fun2) 
         3 
         4)

我真的不喜欢这个论点,因为人们永远不应该为了性能而进行微优化。它可能会使代码更难读,更难写,并且没有真正的速度提升。

什么是lambda形式?

函数位置的lambda表单编译得很好:

> ((lambda (n) (> n 10)) 42)
T
在常见的Lisp语言中,a不是a。具有lambda表达式和零个或多个参数的列表是一种形式,特别是lambda形式。可以计算形式,但lambda表达式不是。注意:还有一个宏运算符
lambda
,它将宏形式
(lambda…
扩展为
(函数(lambda…)
-这是一种有效形式,使用特殊运算符
功能

(                       ; the whole list is a valid lambda form

 (lambda (n) (> n 10))   ; a lambda expression, not a form

 42)                     ; 42 is a form, too
上面的lambda形式大致类似于:

(let ((n 42))
  (> n 10))
在运行时调用求值结果的函数对象

但为什么要禁止函数对象本身处于函数位置?这一决定背后的基本原理是什么

Common Lisp更喜欢在函数位置上有已知(或未知)函数

(sin 3)
((lambda (x) (sin 3)) 3)
(unknown-function 3)
最重要的是,这三个函数不能是其他任何东西:它们不能是数字、字符串或其他数据对象。无法将另一个数据对象放在那里。诸如
DEFUN
FLET
等运算符拒绝定义属于其他数据类型(数字、字符串等)的函数

如果我们在函数位置进行计算,我们可以写:

((if (> foo bar) 42 #'sin) 3)
根据
(>foo bar)
的评估,这可能类似于
(sin3)
(423)
,后者不是有效形式,因为数字不是函数

Common Lisp要求显式使用
FUNCALL
APPLY
MULTIPLE-VALUE-CALL
中的一种调用函数对象。这在源代码中可以清楚地表明正在计算/检索和调用函数对象。它们还进行必要的检查,确保传递的对象确实是函数或符号对函数进行排序

背景


有关详细信息,请参见。

?所有好的答案。我选择这一个是因为
(if(>foo bar)42#sin)
示例。我知道这个后续问题主要是基于观点的,所以我将其隐藏在commo部分:你认为这是一个好的设计选择吗?我的意思是,是的,作为一个程序员,你可以像
(if(…)42#sin)那样搞砸
example但它不像common lisp在所有其他方面都那么严格(没有编译时类型检查)。@Frank:我个人支持显式FUNCALL等让代码从长远来看更易于维护的观点。