在elisp中将函数作为参数传递

3
我将以下代码添加到了我的.emacs文件中。
(defun delete-right-window ()
  (interactive)
  (windmove-right)
  (delete-window))

(defun delete-left-window ()
  (interactive)
  (windmove-left)
  (delete-window))

(defun delete-below-window ()
  (interactive)
  (windmove-down)
  (delete-window))

(defun delete-above-window ()
  (interactive)
  (windmove-up)
  (delete-window))

(global-set-key (kbd "C-s-<right>") 'delete-right-window)
(global-set-key (kbd "C-s-<left>") 'delete-left-window)
(global-set-key (kbd "C-s-<down>") 'delete-below-window)
(global-set-key (kbd "C-s-<up>") 'delete-above-window)

正如您所看到的,大部分代码都是重复的。 我阅读了如何在elisp中将函数作为参数传递?并尝试通过以下方式重构代码来传递windmove-*函数:
(defun delete-other-window (callback)
  (interactive)
  (funcall callback)
  (delete-window))
...

(defun delete-right ()
  (delete-other-window 'windmove-right))

我把按键绑定成了这样:
(global-set-key (kbd "C-s-<right>") 'delete-right)

但是,当我按下C-s-<right>键时,它并没有起作用,只在迷你缓冲区中显示Wrong type argument: commandp, delete-right

我错过了什么或者应该做些什么才能使代码正常工作呢?

3个回答

2
为了将一个函数变成一个命令,你需要在函数中添加特殊的形式interactive
(defun delete-right ()
  (interactive)
  (delete-other-window 'windmove-right))

“interactive” 应该被定位在绑定到按键的函数内。谢谢。 - ntalbs

2
您的代码存在问题,即绑定到键的函数应该是可交互的,而不是它调用的其中一个函数:
(defun delete-other-window (callback)
  (funcall callback)
  (delete-window))

(defun delete-right ()
  (interactive)
  (delete-other-window 'windmove-right))

您也可以使用宏:
(defmacro defun-delete-other-window (direction)
  `(defun ,(intern (concat "delete-" direction)) ()
     (interactive)
     (,(intern (concat "windmove-" direction)))
     (delete-window)))

(defun-delete-other-window "right")

宏方法的一个小缺点是,您不能再跳转到例如windmove-right的定义。 - abo-abo
谢谢你教我interactive应该在绑定按键的函数中。将interactive移动到delete-right后,它运行良好。 - ntalbs

2
这是一个解决方法:
(defun delete-after (fn)
  `(lambda () (interactive)
      (,fn)
      (delete-window)))

(global-set-key (kbd "C-s-<right>") (delete-after 'windmove-right))
(global-set-key (kbd "C-s-<left>") (delete-after 'windmove-left))
(global-set-key (kbd "C-s-<down>") (delete-after 'windmove-down))
(global-set-key (kbd "C-s-<up>") (delete-after 'windmove-up))

好的,我们这里有一个刚开始学习elisp的用户。你真的认为使用像lambda和反引号这样的高级技巧生成匿名函数的函数来提供解决方案是个好主意吗?特别是当他已经有了一个简单、直接的实现方式时? - Lindydancer
是的,问题的实现已经在提问之前得到了明确的解决。 OP正在寻求优雅的解决方案,而我向他展示了它。 - abo-abo
将lambda绑定到键或将其添加到钩子中从来不是一种优雅的方式。当用户检查键绑定或钩子包含的内容时,匿名lambda就像是一个死胡同,里面堆满了瓦砾(当然要带着一点玩笑的口吻 ;))。 - Lindydancer
在我的Emacs中,f1 k会显示匿名lambda的代码。有人认为这甚至更有效率,因为我不必跳转到定义来查看源代码 :) - abo-abo
你好,indydancer。在我看来,我期待有很多不同的回答。相同的回答价值较低,你不觉得吗?所以,abo-abo的回答对我很有价值。我认为OP只是希望得到回答的人之一。所以你不需要仅仅为了回答OP而发表回答。如果你有另一个可能的答案,请发表。谢谢。 - Kei Minagawa
虽然我对elisp还很陌生,但我已经学习了一段时间的clojure,我认为我可以理解这个提议的代码。虽然使用delete-other-window可以减少重复的代码,但仍需要定义四个看起来相似的函数,并且每个函数都应该分别绑定。所以我想在解决这个问题后再进一步,这段代码可以成为我的终极目标。 - ntalbs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接