首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在常见的Lisp中,如何使用词法作用域和函数使另一个函数作为参数传递?

在常见的Lisp中,如何使用词法作用域和函数使另一个函数作为参数传递?
EN

Stack Overflow用户
提问于 2022-04-25 14:31:04
回答 3查看 61关注 0票数 0

我用的是SBCL,Emacs和Slime。因此,我可以:

代码语言:javascript
复制
CL-USER> (defvar example #'(lambda (x) (* x 20)))
EXAMPLE

CL-USER> (funcall example 10)
200

好的。它如预期的那样工作。利用图书馆的德克萨多,我也可以这样:

代码语言:javascript
复制
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函数。我尝试过不同的方法,但都没有成功:

代码语言:javascript
复制
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}>.

我设法做到了:

代码语言:javascript
复制
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"

但是,这感觉很糟糕。有的另一种方法来解决这个问题吗?

谢谢

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-04-25 17:25:15

你的代码:

代码语言:javascript
复制
(defvar example-failing
  #'(lambda (x)
     (x "http://www.paulgraham.com")))

这在公共Lisp中是没有意义的。x是一个变量。您不能像在(x arg)中那样将变量用作函数。在通用Lisp中,函数和变量有不同的名称空间。例如,LET引入了一个局部变量,而FLET引入了一个本地函数。

调用绑定到变量的函数的方法如下:

代码语言:javascript
复制
(funcall x arg)

(apply x (list arg))

因此,正确的例子是:

代码语言:javascript
复制
(defvar example-failing
  #'(lambda (x)
     (apply x (list "http://www.paulgraham.com"))))

代码语言:javascript
复制
(defvar example-failing
  #'(lambda (x)
     (funcall x "http://www.paulgraham.com")))

您的解决方案不是解决方案

这就是你的例子:

代码语言:javascript
复制
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"

这不像你想的那样有效。

代码语言:javascript
复制
(funcall hacky-eval (dex:get "http://www.paulgraham.com"))

就像

代码语言:javascript
复制
(funcall hacky-eval "big html omitted")

然后

代码语言:javascript
复制
(eval "big html omitted")

然后

代码语言:javascript
复制
"big html omitted"

eval的所有调用都是将字符串计算给自己。

您确实需要理解Lisp中的基本评估规则:

代码语言:javascript
复制
(defun foo (arg)
  (eval arg))

(foo (+ 3 4))

与以下内容完全相同:

代码语言:javascript
复制
(defun foo (arg)
  arg)

(foo (+ 3 4))

这和

代码语言:javascript
复制
(identity (+ 3 4))

注意:如果您只将自评估数据传递给EVAL,那么它所做的就是返回数据。

函数调用(foo (+ 1 2))的工作方式如下:

function

  • Lisp
  1. Lisp看到FOO是一个计算参数的函数。(+ 12) -> 3
  2. Lisp使用求值参数调用函数FOO:(

#'foo 3)

  1. Lisp计算函数FOO:(EVAL 3) -> 3
  2. Lisp从FOO -> 3

返回值(S)

票数 2
EN

Stack Overflow用户

发布于 2022-04-25 14:52:05

我被你的问题弄糊涂了,虽然没有你看上去那么困惑。您似乎已经知道,要调用作为变量值的函数,您需要

  • funcall如果只有一个参数列表,则所有参数都是单独的things;
  • apply

得到需要(function thing)或等效#'thing1的东西的函数值。

但是,在您的函数中,您忘记了这一点,而不注意来自SBCL的大量警告。

所以

代码语言:javascript
复制
(defvar *example* (lambda (f) (funcall f "http://www.paulgraham.com")))
...
(funcall *example* #'dex:get)

请注意,所有这些(以及问题中的任何内容)都不依赖于词法范围:这在任何历史的Lisp中都是可行的。

1:您不需要#'只用于(lambda ...),因为lambda是一个扩展到(function (lambda ...))的宏。非常老的代码有时使用显式的#'(lambda ...)表单,因为这个宏并不总是存在于CL中。

票数 4
EN

Stack Overflow用户

发布于 2022-04-26 06:43:15

从使用适当的defun开始

代码语言:javascript
复制
(defun request (url)
  (dex:get url))

CL-USER> (request "http://…")

现在,您想使用dex:get以外的其他东西吗?Well…编写另一个函数,因为它们的参数处理、标头返回值…可能不一样。

代码语言:javascript
复制
(defun request-drakma (url)
   (drakma:… url))

也许在以后的代码中,您想要引用任何一个函数?

代码语言:javascript
复制
(defun do-request (url &optional (get-fn #'request))
  (funcall get-fn url))

代码语言:javascript
复制
(defvar example #'(lambda (x) (* x 20)))

这里给匿名函数…取了一个名字只需使用defun^^

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72001208

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档