在 Common Lisp 中,如何使用词法范围和 funcall 使另一个函数作为参数传递?
In Common Lisp, how to use lexical scope and funcall to make another function be passed as an argument?
我正在使用 SBCL、Emacs 和 Slime。因此,我可以这样做:
CL-USER> (defvar example #'(lambda (x) (* x 20)))
EXAMPLE
CL-USER> (funcall example 10)
200
好的。它按预期工作。使用库 Dexador,我也可以:
CL-USER> (ql:quickload :dexador)
To load "dexador":
Load 1 ASDF system:
dexador
; Loading "dexador"
.......
(:DEXADOR)
CL-USER> (dex:get "http://www.paulgraham.com")
"big HTML ommited"
200
#<HASH-TABLE :TEST EQUAL :COUNT 11 {10029F1443}>
#<QURI.URI.HTTP:URI-HTTP http://www.paulgraham.com>
#<SB-SYS:FD-STREAM for "socket 10.0.0.193:44936, peer: 74.6.52.135:80" {1002681F73}>
现在,我正在尝试使要传递的参数成为一个函数!更具体地说,dex:get
函数。我尝试了不同的方法,但 none 成功了:
CL-USER> (defvar example-failing #'(lambda (x) (x "http://www.paulgraham.com")))
; in: DEFVAR EXAMPLE-FAILING
; (LAMBDA (X) (X "http://www.paulgraham.com"))
;
; caught STYLE-WARNING:
; The variable X is defined but never used.
; in: DEFVAR EXAMPLE-FAILING
; (X "http://www.paulgraham.com")
;
; caught STYLE-WARNING:
; undefined function: COMMON-LISP-USER::X
;
; compilation unit finished
; Undefined function:
; X
; caught 2 STYLE-WARNING conditions
EXAMPLE-FAILING
CL-USER> (funcall example-failing dex:get)
; Evaluation aborted on #<UNBOUND-VARIABLE GET {1002C57103}>.
CL-USER> (funcall example-failing 'dex:get)
; Evaluation aborted on #<UNDEFINED-FUNCTION X {1002DEA263}>.
CL-USER> (funcall example-failing #'dex:get)
; Evaluation aborted on #<UNDEFINED-FUNCTION X {1002F906C3}>.
CL-USER> (funcall example-failing (function dex:get))
; Evaluation aborted on #<UNDEFINED-FUNCTION X {1003147F83}>.
我设法做到了:
CL-USER> (defvar hacky-eval #'(lambda (x) (eval x)))
HACKY-EVAL
CL-USER> (funcall hacky-eval (dex:get "http://www.paulgraham.com"))
"big html omitted"
但是,这感觉是不好的做法。有另一种方法来解决这个问题吗?
谢谢
我对你的问题感到困惑,虽然不像你看起来那么困惑。您似乎已经知道,要调用一个作为变量值的函数,您需要
funcall
如果你把所有的论点都当作单独的东西;
apply
如果你只有一个参数列表;
并且为了获得你需要的东西的函数值 (function thing)
或等效地 #'thing
[1].
但是你在你的函数中忘记了这一点,也没有注意来自 SBCL 的大量警告。
所以
(defvar *example* (lambda (f) (funcall f "http://www.paulgraham.com")))
...
(funcall *example* #'dex:get)
请注意,none 其中(您的问题中没有任何内容)依赖于词法范围:这在任何历史 Lisp 中都适用。
[1]:(lambda ...)
不需要 #'
,因为 lambda
是一个扩展为 (function (lambda ...))
的宏。非常古老的代码有时会使用显式 #'(lambda ...)
形式,因为此宏并不总是存在于 CL 中。
您的代码:
(defvar example-failing
#'(lambda (x)
(x "http://www.paulgraham.com")))
这在 Common Lisp 中毫无意义。 x
是一个变量。您不能像 (x arg)
那样将变量用作函数。在 Common Lisp 中,函数和变量有不同的命名空间。例如LET
引入局部变量,FLET
引入局部函数。
调用绑定到变量的函数的方式有:
(funcall x arg)
(apply x (list arg))
因此正确的例子是:
(defvar example-failing
#'(lambda (x)
(apply x (list "http://www.paulgraham.com"))))
或
(defvar example-failing
#'(lambda (x)
(funcall x "http://www.paulgraham.com")))
你的解决方案是没有解决方案
这是你的例子:
CL-USER> (defvar hacky-eval #'(lambda (x) (eval x)))
HACKY-EVAL
CL-USER> (funcall hacky-eval (dex:get "http://www.paulgraham.com"))
"big html omitted"
这并不像你想象的那样有效。
(funcall hacky-eval (dex:get "http://www.paulgraham.com"))
和
一样
(funcall hacky-eval "big html omitted")
然后
(eval "big html omitted")
然后
"big html omitted"
您对 eval
的所有调用都是对字符串本身求值。
您确实需要了解 Lisp 中的基本求值规则:
(defun foo (arg)
(eval arg))
(foo (+ 3 4))
与:
完全相同
(defun foo (arg)
arg)
(foo (+ 3 4))
与
相同
(identity (+ 3 4))
注意:如果您只将自我评估数据传递给 EVAL,那么它所做的只是 return 数据
函数调用 (foo (+ 1 2))
的工作方式如下:
- Lisp 认为 FOO 是一个函数
- Lisp 计算参数。 (+ 1 2) -> 3
- Lisp 使用评估的参数调用函数 FOO:(funcall #'foo 3)
- Lisp 计算函数 FOO: (EVAL 3) -> 3
- Lisp returns 来自 FOO 的值 -> 3
首先使用正确的 defun
:
(defun request (url)
(dex:get url))
CL-USER> (request "http://…")
现在您想使用 dex:get
以外的东西?好吧……写另一个函数,因为它们的参数处理,headers,return 值……可能不同。
(defun request-drakma (url)
(drakma:… url))
也许在以后的代码中您想引用这两个函数?
(defun do-request (url &optional (get-fn #'request))
(funcall get-fn url))
(defvar example #'(lambda (x) (* x 20)))
这里你给一个匿名函数起一个名字...只需使用defun
^^
我正在使用 SBCL、Emacs 和 Slime。因此,我可以这样做:
CL-USER> (defvar example #'(lambda (x) (* x 20)))
EXAMPLE
CL-USER> (funcall example 10)
200
好的。它按预期工作。使用库 Dexador,我也可以:
CL-USER> (ql:quickload :dexador)
To load "dexador":
Load 1 ASDF system:
dexador
; Loading "dexador"
.......
(:DEXADOR)
CL-USER> (dex:get "http://www.paulgraham.com")
"big HTML ommited"
200
#<HASH-TABLE :TEST EQUAL :COUNT 11 {10029F1443}>
#<QURI.URI.HTTP:URI-HTTP http://www.paulgraham.com>
#<SB-SYS:FD-STREAM for "socket 10.0.0.193:44936, peer: 74.6.52.135:80" {1002681F73}>
现在,我正在尝试使要传递的参数成为一个函数!更具体地说,dex:get
函数。我尝试了不同的方法,但 none 成功了:
CL-USER> (defvar example-failing #'(lambda (x) (x "http://www.paulgraham.com")))
; in: DEFVAR EXAMPLE-FAILING
; (LAMBDA (X) (X "http://www.paulgraham.com"))
;
; caught STYLE-WARNING:
; The variable X is defined but never used.
; in: DEFVAR EXAMPLE-FAILING
; (X "http://www.paulgraham.com")
;
; caught STYLE-WARNING:
; undefined function: COMMON-LISP-USER::X
;
; compilation unit finished
; Undefined function:
; X
; caught 2 STYLE-WARNING conditions
EXAMPLE-FAILING
CL-USER> (funcall example-failing dex:get)
; Evaluation aborted on #<UNBOUND-VARIABLE GET {1002C57103}>.
CL-USER> (funcall example-failing 'dex:get)
; Evaluation aborted on #<UNDEFINED-FUNCTION X {1002DEA263}>.
CL-USER> (funcall example-failing #'dex:get)
; Evaluation aborted on #<UNDEFINED-FUNCTION X {1002F906C3}>.
CL-USER> (funcall example-failing (function dex:get))
; Evaluation aborted on #<UNDEFINED-FUNCTION X {1003147F83}>.
我设法做到了:
CL-USER> (defvar hacky-eval #'(lambda (x) (eval x)))
HACKY-EVAL
CL-USER> (funcall hacky-eval (dex:get "http://www.paulgraham.com"))
"big html omitted"
但是,这感觉是不好的做法。有另一种方法来解决这个问题吗?
谢谢
我对你的问题感到困惑,虽然不像你看起来那么困惑。您似乎已经知道,要调用一个作为变量值的函数,您需要
funcall
如果你把所有的论点都当作单独的东西;apply
如果你只有一个参数列表;
并且为了获得你需要的东西的函数值 (function thing)
或等效地 #'thing
[1].
但是你在你的函数中忘记了这一点,也没有注意来自 SBCL 的大量警告。
所以
(defvar *example* (lambda (f) (funcall f "http://www.paulgraham.com")))
...
(funcall *example* #'dex:get)
请注意,none 其中(您的问题中没有任何内容)依赖于词法范围:这在任何历史 Lisp 中都适用。
[1]:(lambda ...)
不需要 #'
,因为 lambda
是一个扩展为 (function (lambda ...))
的宏。非常古老的代码有时会使用显式 #'(lambda ...)
形式,因为此宏并不总是存在于 CL 中。
您的代码:
(defvar example-failing
#'(lambda (x)
(x "http://www.paulgraham.com")))
这在 Common Lisp 中毫无意义。 x
是一个变量。您不能像 (x arg)
那样将变量用作函数。在 Common Lisp 中,函数和变量有不同的命名空间。例如LET
引入局部变量,FLET
引入局部函数。
调用绑定到变量的函数的方式有:
(funcall x arg)
(apply x (list arg))
因此正确的例子是:
(defvar example-failing
#'(lambda (x)
(apply x (list "http://www.paulgraham.com"))))
或
(defvar example-failing
#'(lambda (x)
(funcall x "http://www.paulgraham.com")))
你的解决方案是没有解决方案
这是你的例子:
CL-USER> (defvar hacky-eval #'(lambda (x) (eval x)))
HACKY-EVAL
CL-USER> (funcall hacky-eval (dex:get "http://www.paulgraham.com"))
"big html omitted"
这并不像你想象的那样有效。
(funcall hacky-eval (dex:get "http://www.paulgraham.com"))
和
一样(funcall hacky-eval "big html omitted")
然后
(eval "big html omitted")
然后
"big html omitted"
您对 eval
的所有调用都是对字符串本身求值。
您确实需要了解 Lisp 中的基本求值规则:
(defun foo (arg)
(eval arg))
(foo (+ 3 4))
与:
完全相同(defun foo (arg)
arg)
(foo (+ 3 4))
与
相同(identity (+ 3 4))
注意:如果您只将自我评估数据传递给 EVAL,那么它所做的只是 return 数据
函数调用 (foo (+ 1 2))
的工作方式如下:
- Lisp 认为 FOO 是一个函数
- Lisp 计算参数。 (+ 1 2) -> 3
- Lisp 使用评估的参数调用函数 FOO:(funcall #'foo 3)
- Lisp 计算函数 FOO: (EVAL 3) -> 3
- Lisp returns 来自 FOO 的值 -> 3
首先使用正确的 defun
:
(defun request (url)
(dex:get url))
CL-USER> (request "http://…")
现在您想使用 dex:get
以外的东西?好吧……写另一个函数,因为它们的参数处理,headers,return 值……可能不同。
(defun request-drakma (url)
(drakma:… url))
也许在以后的代码中您想引用这两个函数?
(defun do-request (url &optional (get-fn #'request))
(funcall get-fn url))
(defvar example #'(lambda (x) (* x 20)))
这里你给一个匿名函数起一个名字...只需使用defun
^^