emacs - 通过 cl 函数传递回调

标签 emacs lisp elisp

我正在尝试使用出色的 request.el来自 REST API 的库请求数据:

(request
 "http://httpbin.org/get"
 :params '(("key" . "value") ("key2" . "value2"))
 :parser 'json-read
 :success (function*
           (lambda (&key data &allow-other-keys)
             (message "I sent: %S" (assoc-default 'args data)))))

效果很好。作为一个 lisp 新手,我真的不知道 function* 在这里做了什么,我只是从 request.el-examples 中得到的。

然后我尝试将此调用包装在一个函数中以减少样板代码,如下所示:

(defun my/do-request (callback)
  (request
   "http://httpbin.org/get"
   :params '(("key" . "value") ("key2" . "value2"))
   :parser 'json-read
   :success callback))

(my/do-request (lambda (data)
                 (message "got data: %s" data)))

但是回调没有被调用?我也试过像这样传递回调:

(defun my/do-request (callback)
  (request
   "http://httpbin.org/get"
   :params '(("key" . "value") ("key2" . "value2"))
   :parser 'json-read
   :success (function*
             (lambda (&key data &allow-other-keys)
               (callback data)))))

同样的结果。我想我可能需要在这里进行词法绑定(bind),但这也无济于事。

如何减少此处的样板代码?

最佳答案

调用新函数时,通常最好使用与您最初拥有的值完全相同的值进行尝试:

(my/do-request
 (function*
  (lambda (&key data &allow-other-keys)
   (message "I sent: %S" (assoc-default 'args data)))))

上面打印了所需的消息。

第一种方法

您调用了如下代码,但没有打印任何内容:

(my/do-request (lambda (data)
                 (message "got data: %s" data)))

原来是有错误,可惜没有到达用户。如有疑问,您应该启用调试器:

(setf debug-on-error t)

您可以在 *scratch* 缓冲区或执行 M-: 之后在迷你缓冲区中评估上述内容(eval-表达式).

然后,当您重新评估调用时,应显示以下内容:

Debugger entered--Lisp error: (wrong-number-of-arguments (lambda (data) (message "got data: %s" data)) 8)
  (lambda (data) (message "got data: %s" data))(:data ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . .....) (url . "http://httpbin.org/get?key=value&key2=value2")) :symbol-status success :error-thrown nil :response [cl-struct-request-response 200 nil ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . .....) (url . "http://httpbin.org/get?key=value&key2=value2")) nil success "http://httpbin.org/get?key=value&key2=value2" nil (:params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response #0) #<killed buffer> "HTTP/1.1 200 OK\nConnection: keep-alive\nServer: gunicorn/19.8.1\nDate: Thu, 05 Jul 2018 11:44:15 GMT\nContent-Type: application/json\nContent-Length: 249\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\nVia: 1.1 vegur\n" nil curl nil])
  apply((lambda (data) (message "got data: %s" data)) (:data ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) :symbol-status success :error-thrown nil :response [cl-struct-request-response 200 nil ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) nil success "http://httpbin.org/get?key=value&key2=value2" nil (:params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response #1) #<killed buffer> "HTTP/1.1 200 OK\nConnection: keep-alive\nServer: gunicorn/19.8.1\nDate: Thu, 05 Jul 2018 11:44:15 GMT\nContent-Type: application/json\nContent-Length: 249\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\nVia: 1.1 vegur\n" nil curl nil]))
  apply(apply (lambda (data) (message "got data: %s" data)) (:data ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) :symbol-status success :error-thrown nil :response [cl-struct-request-response 200 nil ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) nil success "http://httpbin.org/get?key=value&key2=value2" nil (:params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response #1) #<killed buffer> "HTTP/1.1 200 OK\nConnection: keep-alive\nServer: gunicorn/19.8.1\nDate: Thu, 05 Jul 2018 11:44:15 GMT\nContent-Type: application/json\nContent-Length: 249\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\nVia: 1.1 vegur\n" nil curl nil]))
  request--safe-apply((lambda (data) (message "got data: %s" data)) (:data ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) :symbol-status success :error-thrown nil :response [cl-struct-request-response 200 nil ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) nil success "http://httpbin.org/get?key=value&key2=value2" nil (:params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response #1) #<killed buffer> "HTTP/1.1 200 OK\nConnection: keep-alive\nServer: gunicorn/19.8.1\nDate: Thu, 05 Jul 2018 11:44:15 GMT\nContent-Type: application/json\nContent-Length: 249\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\nVia: 1.1 vegur\n" nil curl nil]))
  request--callback(#<killed buffer> :params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response [cl-struct-request-response 200 nil ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) nil success "http://httpbin.org/get?key=value&key2=value2" nil (:params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response #0) #<killed buffer> "HTTP/1.1 200 OK\nConnection: keep-alive\nServer: gunicorn/19.8.1\nDate: Thu, 05 Jul 2018 11:44:15 GMT\nContent-Type: application/json\nContent-Length: 249\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\nVia: 1.1 vegur\n" nil curl nil])
  apply(request--callback #<killed buffer> (:params (("key" . "value") ("key2" . "value2")) :parser json-read :success (lambda (data) (message "got data: %s" data)) :error #[128 "\302\300\303\301\"\"\207" [request-default-error-callback ("http://httpbin.org/get") apply append] 6 "\n\n(fn &rest ARGS2)"] :url "http://httpbin.org/get?key=value&key2=value2" :response [cl-struct-request-response 200 nil ((args (key . "value") (key2 . "value2")) (headers (Accept . "*/*") (Accept-Encoding . "deflate, gzip") (Connection . "close") (Host . "httpbin.org") (User-Agent . "curl/7.55.1")) (origin . ...) (url . "http://httpbin.org/get?key=value&key2=value2")) nil success "http://httpbin.org/get?key=value&key2=value2" nil #0 #<killed buffer> "HTTP/1.1 200 OK\nConnection: keep-alive\nServer: gunicorn/19.8.1\nDate: Thu, 05 Jul 2018 11:44:15 GMT\nContent-Type: application/json\nContent-Length: 249\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\nVia: 1.1 vegur\n" nil curl nil]))
  request--curl-callback(#<process request curl> "finished\n")

根据文档,回调函数应该是可变的,这意味着以下工作:

(lambda (&rest args) (message "got data: %s" args))

但是,您会看到太多数据。

参数以键/值对的形式传递。为了获取与 :data 符号关联的数据,您必须执行以下操作:

(lambda (&rest args)
  (message "got data: %s" (getf args :data)))

结果值是一个关联列表,您可以从中像上面那样访问 'args 条目(即 (assoc-default 'args data))。

但是,除了执行 (getf args :data),您还可以这样写:

(lambda (&key data) ...)

特殊的 &key 符号用于自动访问隐式参数列表中与 :data 关联的值。 但是,关键字参数来自Common Lisp,Emacs Lisp 不知道如何开箱即用地处理&key。这就是为什么有一个 (function* ...) 宏环绕 lambda。然后您可以选择使用 function* 宏或自己处理参数列表,如上所示。这取决于你需要什么。正如文档所建议的,如果您使用 &key,您还应该使用 &allow-other-keys

第二种方法

在您的第二种方法中,将 debug-on-entry 设置为 t 表示 callback 不是已知函数:

Debugger entered--Lisp error: (void-function callback)

这是因为 Emacs Lisp 是 Lisp-2,即您不能简单地通过将参数作为函数调用的第一个元素来调用参数中给定的函数。 Emacs Lisp 将语法理解为“调用名为回调的函数”,而不是“调用绑定(bind)到变量回调的函数对象”。您需要使用 funcall:

(function*
  (lambda (&key data &allow-other-keys)
    (funcall callback data)))

但是有了上面的内容,现在的错误是 callback 是一个 undefined variable 。而现在,这是由于动态范围界定。如果您评估以下行并重新评估 defun,代码将按预期工作:

(setf lexical-binding t)

或者,您可以将以下内容作为文件的第一行:

;; -*- lexical-binding: t -*-

关于emacs - 通过 cl 函数传递回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51186823/

相关文章:

file - 在 Emacs 中打开文件的速度非常快,类似于 Sublime 的 Goto Anything

lisp - 什么是印读一致性?

emacs - 在光标点生成一个随机的 5 个字母 + 数字字符串。 (全部小写)

emacs - 在 Split View中重新打开缓冲区

emacs - C-k 删除一行但当光标位于行尾时

emacs - 如何转义双引号?

emacs - 如何优雅地关闭 emacs 守护进程?

lisp - 超过 1 行代码的宏

lisp - 是否可以增量宏定义?

elisp - 如何使用: ido-find-file in an elisp function?