随后运行相同的功能,产生不同的结果

时间:2013-01-12 23:00:36

标签: emacs refactoring lisp

我对提出这个问题感到有些愚蠢,但我觉得我的代码效率低下。我想我在这里没有太好的逻辑。

基本上,我希望在随后运行相同的命令时会发生一些不同的事情。

我的想法是拥有一个(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)

3 个答案:

答案 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)))

)
相关问题