简体   繁体   English

常见的lisp中FUNCALL和#'函数名有什么区别?

[英]What is the difference between FUNCALL and #'function-name in common lisp?

I am reading through a book for homework, and I understand that using #' is treating the variable as a function instead of a variable. 我正在阅读一本关于作业的书,我明白使用#'将变量视为函数而不是变量。 But I am a little hazy on FUNCALL. 但我对FUNCALL有点朦胧。 I understand that lisp makes object out of variables, so is the function name just a 'pointer' (may be a bad word, but hopefully you get what I mean), in which case you use #' to invoke it, or is funcall the only way to invoke them? 我理解lisp使变量成为对象,所以函数名称只是一个'指针'(可能是一个坏词,但希望你得到我的意思),在这种情况下你使用#'来调用它,或者是funcall调用它们的唯一方法是什么? ex. 恩。

(defun plot (fn min max step)
(loop for i from min to max by step do
      (loop repeat (funcall fn i) do (format t "*"))
      (format t "~%")))

couldn't I just do: 我不能这样做:

(defun plot (fn min max step)
(loop for i from min to max by step do
      (loop repeat #'(fn i) do (format t "*"))
      (format t "~%")))

I guess my confusion lies in what exactly is in the function names. 我想我的困惑在于函数名称的确切含义。 When I read the book, it said that the variable's value is what will be the function object. 当我读到这本书时,它说变量的值是函数对象。

#'function-name is (function function-name) . #'function-name(function function-name) Nothing is called, evaluating either results in the function associated with function-name (the object representing the function). 没有任何东西被调用,评估与function-name相关联的function-name结果(表示函数的对象)。 funcall is used to call functions. funcall用于调用函数。

See funcall and function in the HyperSpec . 请参阅HyperSpec中的funcall函数

Sample session using both: 使用两者的示例会话:

CL-USER> (defun square (x) (* x x))
SQUARE
CL-USER> #'square
#<FUNCTION SQUARE>
CL-USER> (function square)
#<FUNCTION SQUARE>
CL-USER> (funcall #'square 3)
9
CL-USER> (funcall 'square 3)
9

The second invocation of funcall works because it also accepts a symbol as function designator (see the link for funcall above for details). 第二次调用funcall有效的,因为它还接受一个符号作为函数指示符(有关详细信息,请参阅上面的funcall链接)。

The #' and funcall notations are needed in Common Lisp because this language is a so-called "Lisp-2" where a given symbol can have two separate and unrelated main "meanings" normally listed as Common Lisp中需要#'funcall符号,因为这种语言是所谓的“Lisp-2”,其中给定的符号可以有两个独立funcall相关的主要“含义”,通常列为

  1. When used as first element of a form it means a function 当用作表单的第一个元素时,它表示一个函数
  2. When used in any other place it means a variable 当在任何其他地方使用时,它意味着变量

These are approximate explanations, as you will see in the following example that "first element of a form" and "any other place" are not correct definitions. 这些是近似解释,您将在以下示例中看到“表单的第一个元素”和“任何其他位置”不是正确的定义。

Consider for example: 考虑例如:

LISP-2

the above code prints 144 ... it may seem surprising at first but the reason is that the same name square is used with two different meanings: the function that given an argument returns the result of multiplying the argument by itself and the local variable square with value 12. 上面的代码打印144 ...起初可能看起来令人惊讶,但原因是同名square使用了两个不同的含义:给定参数的函数返回参数乘以自身的结果和局部变量square价值12。

The first and third uses of the name square the meaning is the function named square and I've painted the name with red color. 第一个和第三个使用名称square意思是名为square函数 ,我用红色绘制了名称。 The second and fourth uses instead are about a variable named square and are painted in blue instead. 第二次和第四次使用的是一个名为square变量 ,而是涂成蓝色。

How can Common Lisp decide which is which? Common Lisp如何决定哪个是哪个? the point is the position... after defun it's clearly in this case a function name, like it's a function name in the first part of (square square) . 点是位置...在defun之后它在这种情况下显然是一个函数名称,就像它在(square square)的第一部分中的函数名称一样。 Likewise as first element of a list inside a let form it's clearly a variable name and it's also a variable name in the second part of (square square) . 同样作为let表单中列表的第一个元素,它显然是变量名称,它也是(square square)第二部分中的变量名称。

This looks pretty psychotic... doesn't it? 这看起来很精神病......不是吗? Well there is indeed some splitting in the Lisp community about if this dual meaning is going to make things simpler or more complex and it's one of the main differences between Common Lisp and Scheme. 好吧,Lisp社区确实存在一些分歧,即这种双重含义是否会使事情变得更简单或更复杂,这是Common Lisp和Scheme之间的主要区别之一。

Without getting into the details I'll just say that this apparently crazy choice has been made to make Lisp macros more useful, providing enough hygiene to make them working nicely without the added complexity and the removed expressiveness power of full hygienic macros. 在没有深入细节的情况下,我只是说这个看起来很疯狂的选择是为了让Lisp宏更有用,提供足够的卫生,使它们能够很好地工作,而不会增加复杂性并消除完整卫生宏的表现力。 For sure it's a complication that makes it harder to explain the language to whoever is learning it (and that's why Scheme is considered a better (simpler) language for teaching) but many expert lispers think that it's a good choice that makes the Lisp language a better tool for real problems. 肯定这是一个复杂的问题,这使得向学习它的人解释语言变得更加困难(这就是为什么Scheme被认为是一种更好(更简单)的教学语言)但是许多专家lispers认为这是一个很好的选择,使得Lisp语言变成了真正问题的更好工具。

Also in human languages the context plays an important role anyway and there it's not a serious problem for humans that sometimes the very same word can be used with different meanings (eg as a noun or as a verb like "California is the state I live in" or "State your opinion"). 同样在人类语言中,语境无论如何都起着重要的作用,对于人类来说,这并不是一个严重的问题,有时候同一个词可以用于不同的含义(例如,作为名词或动词,如“加利福尼亚是我所居住的州” “或”陈述你的意见“)。

Even in a Lisp-2 you need however to use functions as values, for example passing them as parameters or storing them into a data structure, or you need to use values as functions, for example calling a function that has been received as parameter (your plot case) or that has been stored somewhere. 即使在Lisp-2中,您也需要将函数用作值,例如将它们作为参数传递或将它们存储到数据结构中,或者您需要将值用作函数,例如调用已作为参数接收的函数(你的情节案例)或已存储在某处。 This is where #' and funcall come into play... 这就是#'funcall发挥作用的地方......

#'foo is indeed just a shortcut for (function foo) , exactly like 'x is a shortcut for (quote x) . #'foo确实只是(function foo)的快捷方式,就像'x(quote x)的快捷方式一样。 This "function" thing is a special form that given a name ( foo in this case) returns the associated function as a value, that you can store in variables or pass around: 这个“函数”是一个特殊的形式,给定一个名称(在这种情况下为foo )返回相关的函数作为一个值,你可以存储在变量或传递:

(defvar *fn* #'square)

in the above code for example the variable *fn* is going to receive the function defined before. 例如,在上面的代码中,变量*fn*将接收之前定义的函数。 A function value can be manipulated as any other value like a string or a number. 函数值可以像任何其他值一样操作,如字符串或数字。

funcall is the opposite, allowing to call a function not using its name but by using a value... funcall是相反的,允许调用函数不使用其名称,但使用值...

(print (funcall *fn* 12))

the above code will display 144... because the function that was stored in the variable *fn* now is being called passing 12 as argument. 上面的代码将显示144 ...因为现在正在调用存储在变量*fn*的函数作为参数传递12。

If you know the "C" programming language an analogy is considering (let ((p #'square))...) like taking the address of the function square (as with { int (*p)(int) = &square; ...} ) and instead (funcall p 12) is like calling a function using a pointer (as with (*p)(12) that "C" allows to be abbreviated to p(12) ). 如果您知道“C”编程语言,则类比考虑(let ((p #'square))...)就像获取函数square的地址一样(与{ int (*p)(int) = &square; ...}{ int (*p)(int) = &square; ...} (funcall p 12)就像使用指针调用函数一样(与(*p)(12) ,“C”允许缩写为p(12) )。

The admittely confusing part in Common Lisp is that you can have both a function named square and a variable named square in the same scope and the variable will not hide the function. Common Lisp中令人困惑的部分是你可以在同一范围内同时拥有一个名为square的函数和一个名为square的变量,并且该变量不会隐藏该函数。 funcall and function are two tools you can use when you need to use the value of a variable as a function or when you want a function as a value, respectively. 当您需要将变量的值用作函数或者您希望将函数用作值时,可以使用funcallfunction两个工具。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM