【问题标题】:In Common Lisp, how to use lexical scope and funcall to make another function be passed as an argument?在 Common Lisp 中,如何使用词法作用域和 funcall 使另一个函数作为参数传递?
【发布时间】:2022-11-10 04:12:51
【问题描述】:

我正在使用 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 函数。我尝试了不同的方法,但都没有成功:

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"

但是,这感觉是不好的做法。在那儿其他解决这个问题的方法?

谢谢

【问题讨论】:

    标签: common-lisp anonymous-function lexical-scope funcall


    【解决方案1】:

    我对你的问题感到困惑,尽管不像你看起来那么困惑。您似乎已经知道,要调用作为变量值的函数,您需要

    • funcall 如果您将所有论点都视为个别事物;
    • apply 如果您只有一个参数列表;

    并获得您需要(function thing) 或等效#'thing[1] 的功能值。

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

    所以

    (defvar *example* (lambda (f) (funcall f "http://www.paulgraham.com")))
    ...
    (funcall *example* #'dex:get)
    

    请注意,这一切(以及您的问题中的任何内容)都不依赖于词法范围:这在任何历史的 Lisp 中都适用。


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

    【讨论】:

    • 您的帖子中是否缺少两个括号?也许(defvar *example* (lambda (f) (funcall f "http://www.paulgraham.com")))
    • @PedroDelfino 是的,谢谢。现在修好了。
    【解决方案2】:

    你的代码:

    (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,那么它所做的就是返回数据

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

    1. Lisp 看到 FOO 是一个函数
    2. Lisp 计算参数。 (+ 1 2) -> 3
    3. Lisp 使用评估参数调用函数 FOO:(funcall #'foo 3)
    4. Lisp 计算函数 FOO:(EVAL 3) -> 3
    5. Lisp 从 FOO 返回值 -> 3

    【讨论】:

      【解决方案3】:

      首先使用正确的defun

      (defun request (url)
        (dex:get url))
      
      CL-USER> (request "http://…")
      

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

      (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^^

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-11
        • 2013-12-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多