我对提出这个问题感到有些愚蠢,但我觉得我的代码效率低下。我想我在这里没有太好的逻辑。
基本上,我希望在随后运行相同的命令时会发生一些不同的事情。
我的想法是拥有一个(cond )
,其中对于每种情况,我都要测试之前使用的命令是否相同,以及根据按下的次数设置的变量值。
在这种情况下,我也觉得我没有正确获得标题/标签,所以请随时编辑。
((and (eq last-repeatable-command 'thecommand)
(= varcounter 1))
(message "second time called")
(setq varcounter 2))
再次按下时,下一个子句将会触发。
虽然下面的代码有效,但我相信这可以更有效地完成,我希望有人可以就如何处理这个问题发表指示。
长代码示例:
(defun incremental-insert-o ()
(interactive)
; init if not bound
(when (not (boundp 'iivar)) (setq iivar 0))
(cond
((and (eq last-repeatable-command 'incremental-insert-o)
(= iivar 1))
(insert "o o ")
(setq iivar 2))
((and (eq last-repeatable-command 'incremental-insert-o)
(= iivar 2))
(insert "o o o ")
(setq iivar 3))
((and (eq last-repeatable-command 'incremental-insert-o)
(= iivar 3))
(insert "o o o "))
(t
(insert "o ")
(setq iivar 1)))
)
(global-set-key [f8] 'incremental-insert-o)
答案 0 :(得分:3)
现在,您要求更高效的代码。这可能意味着一些事情。您可能意味着您希望代码执行得更快。代码现在有多慢?当我在我的Emacs上运行时,它是即时的。根据定义,这个代码是从buttonpress调用的,它不一定非常快。你的代码对于它的用例来说已经足够快了,所以我不担心它会更快。它也不使用内存:如果你调用它n
次,它仍然只会使用足够的内存来存储一个整数:这个算法是O(1)
。听起来不错。
你也可以指“用更少的线写下”。这也将使代码更不容易出错,并且更容易理解。这当然是一个合理的目标。你的代码开头并不可怕,所以它不是必需品,但也不是一个坏主意。我们可以对您的功能进行一些修改。您可以删除cond
的整个第三个子句,并让(= iivar 2)
案例成为最后一个,从而无需将iivar
设置为3。嗯,那已经好了。
但等等,该函数最多调用(eq last-repeatable-command 'incremental-insert-o)
三次!好多啊。让我试着改写它!首先,让我们从基本函数定义开始,调用interactive
,如下所示:
(defun incremental-insert-o ()
(interactive))
现在,我要从代码中重构一些东西。首先,让我们看看我们是否可以正确跟踪iivar
。为了便于阅读,我将该变量重命名为incremental-insert-o-consecutive
,并且因为Emacs Lisp具有单个命名空间,所以使用名为iivar
的变量的任何其他内容都将读取和写入代码所在的相同位置于:
(defun incremental-insert-o ()
(interactive)
(if (eq last-repeatable-command 'incremental-insert-o)
(setq incremental-insert-o-consecutive
(1+ incremental-insert-o-consecutive))
(setq incremental-insert-o-consecutive
1)))
这有用吗?我会像你一样将它绑定到[F8]
:(global-set-key [f8] 'incremental-insert-o)
。现在,点击[F8]
来运行它,但它不告诉你返回值是什么。让我们稍微改变一下这个函数来测试它:
(defun incremental-insert-o ()
(interactive)
(if (eq last-repeatable-command 'incremental-insert-o)
(setq incremental-insert-o-consecutive
(1+ incremental-insert-o-consecutive))
(setq incremental-insert-o-consecutive
1))
(message "incremental-insert-o-consecutive is currently %s" incremental-insert-o-consecutive))
点击[F8]
几次以确保它有效,确实如此!它从1开始,每调用一次连续增加1,并在你做其他事情时重置。现在,我们只需要打印出正确的信息。我们想要打印什么?好吧,第一次调用该功能时,打印出一个“o”,然后第二次打印出“o o”,然后打印第三次,所有其他时间打印“o o o”。请注意,打印第二个字符串只是打印第一个字符串两次,第三个字符串打印第一个字符串三次:
(defun incremental-insert-o ()
(interactive)
(if (eq last-repeatable-command 'incremental-insert-o)
(setq incremental-insert-o-consecutive
(1+ incremental-insert-o-consecutive))
(setq incremental-insert-o-consecutive
1))
(dotimes (i incremental-insert-o-consecutive)
(insert "o ")))
这几乎是对的!它在1到3的时间内做正确的事情,但在插入“o o o”时没有限制;它继续打印“o o o o”等。所以我们只需要限制重复的限制在3:
(defun incremental-insert-o ()
(interactive)
(if (eq last-repeatable-command 'incremental-insert-o)
(setq incremental-insert-o-consecutive
(1+ incremental-insert-o-consecutive))
(setq incremental-insert-o-consecutive
1))
(dotimes (i (min incremental-insert-o-consecutive
3))
(insert "o ")))
现在,这似乎完全符合您的要求。让我们看一下原始函数的变化。这会计算超过3的重复次数。但输出行为是相同的,所以我认为这不重要,并且保持重复的实际计数似乎更好。如果你溢出整数会破坏,但这似乎不太可能。 Emacs guarantees at least 536870911 as MAXINT。让我们称之为一天。我们确实让代码更短,并且没有重复的部分。我认为这使它更具可读性。
答案 1 :(得分:2)
这是我能想到的东西,然而,把它当作一粒盐,因为它可能过于复杂,你不想把这么多的复杂性带入你所做的事情中:
(defstruct command-state
action next-state)
(defmacro define-action-states (name condition &rest actions)
(labels ((%make-command-state
(action name)
`(make-command-state :action (lambda () ,action))))
`(let ((head ,(%make-command-state (car actions) name)))
(defvar ,name nil)
(setq ,name head)
,@(loop for action in (cdr actions)
collect
`(setf (command-state-next-state ,name)
,(%make-command-state action name)
,name (command-state-next-state ,name)))
(setf (command-state-next-state ,name) head
,name head)
(defun ,(intern (concat (symbol-name name) "-command")) ()
(when ,condition
(unwind-protect
(funcall (command-state-action ,name))
(setq ,name (command-state-next-state ,name))))))))
(define-action-states print-names (= 1 1)
(message "first state")
(message "second state")
(message "third state")
(message "fourth state"))
(print-names-command)
;; will print messages looping through them,
;; each time you call it
我已经使用了struct
,所以你可以添加更多的条件,例如,独立于状态本身,但大多数情况下这些名称会更加不言自明。
另外,可能,这不是你真正关心效率的地方 - 到目前为止,你的手指无法超越eLisp解释器,这一切都很好;)
以下是我对您的代码所做的一些事情,可能会对其进行一些改进(现在最糟糕的情况是只检查5个条件而不是6个:)
(defun smart-killer ()
(interactive)
(let* ((properties (symbol-plist 'smart-killer))
(counter (plist-get properties :counter)))
(if (region-active-p)
(kill-region (region-beginning) (region-end))
(if (eq last-repeatable-command 'smart-killer)
(if (> counter 3)
(message "Kill ring is already filled with paragraph.")
(if (> counter 2)
(progn
(yank)
(kill-new "")
(mark-paragraph -1)
(kill-region (region-beginning) (region-end)))
(if (> counter 1)
(kill-region (point) (line-beginning-position))
(kill-line))))
(when (not (looking-at "\\<\\|\\>")) (backward-word)) ; begin/end of word
(kill-word 1))
(plist-put properties :counter (mod (1+ counter) 5)))))
(put 'smart-killer :counter 0)
答案 2 :(得分:1)
这就是我最终想出的:
(defun smart-killer ()
(interactive)
(cond
; [1] If region active, kill region
((region-active-p)
(kill-region (region-beginning) (region-end)))
; [2] If this command was last called, check how many times before it ran
((eq last-repeatable-command 'smart-killer)
(cond
; [2a]
((= sm-killer 1)
(kill-line))
; [2b]
((= sm-killer 2)
(kill-region (point) (line-beginning-position)))
; [2c]
((= sm-killer 3)
(yank)
(kill-new "")
(mark-paragraph -1)
(kill-region (region-beginning) (region-end)))
; [2d]
((= sm-killer 4)
(message "Kill ring is already filled with paragraph.")))
(incf sm-killer))
; [3]
(t
(when (not (looking-at "\\<\\|\\>")) (backward-word)) ; begin/end of word
(kill-word 1)
(setq sm-killer 1)))
)