1

问这个问题我觉得有点傻,但我觉得我的代码效率很低。我想我的逻辑在这里不太好。

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

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

3 回答 3

3

现在,您需要更高效的代码。您可以通过此来表示几件事。您可能意味着您想要执行得更快的代码。现在的代码有多慢?当我在我的 Emacs 上运行它时,它是即时的。鉴于根据定义,此代码是从按钮按下调用的,它不必非常快。你的代码对于它的用例来说已经足够快了,所以我不担心让它更快。它也不使用内存:如果你调用它n,它仍然只使用足够的内存来存储一个整数:这个算法是O(1). 听起来不错。

你也可以说“用更少的行写这个”。这也将使代码不易出错,并且更易于理解。这当然是一个合理的目标。您的代码一开始并不糟糕,因此它不是必需品,但也不是一个坏主意。我们可以对您的功能进行一些修改。您可以删除您的整个第三个子句cond,并让(= iivar 2)case 成为最后一个子句,从而无需在iivar此处设置为 3。嗯,已经好多了。

但是等等,该函数(eq last-repeatable-command 'incremental-insert-o)最多调用 3 次!好多啊。让我尝试重写它!首先,让我们从一个基本函数定义开始,通过一个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”,然后第二次,打印出“oo”,然后第三次和所有其他时间,打印出“ooo”。注意打印第二个字符串只是打印第一个字符串两次,第三个字符串打印第一个字符串三次:

(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 次中做正确的事情,但在插入“ooo”时不会结束;它继续打印“oooo”等。所以我们只需要将重复次数限制为 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保证至少 536870911 作为 MAXINT。所以让我们称之为一天。我们确实使代码更短,并且没有重复的部分。我认为这使它更具可读性。

于 2013-01-13T08:48:52.743 回答
2

Here's something I could think of, however, take it with a grain of salt, because it may be overly complex, and you don't want to bring this much complexity into what you do:

(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

I've made it to use a struct, so that you could add more conditions, independent of the state itself, for example, but mostly so the names would be more self-explanatory.

Also, probably, that's not the place you should really care about efficiency - so far your fingers cannot outrun the eLisp interpreter, it's all good ;)


Here's something I did to your code to possibly improve it a bit (now the worst case scenario will only check 5 conditions instead of 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)
于 2013-01-13T08:27:49.580 回答
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)))

)
于 2013-01-13T10:13:45.190 回答