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何度も呼び出すと、1つの整数を格納するのに十分なメモリしか使用しません。このアルゴリズムはO(1)です。私にはいいですね。

「これをより少ない行で書く」という意味もあります。これにより、コードのエラーが発生しにくくなり、理解しやすくなります。それは確かに合理的な目標です。あなたのコードはそもそもひどいものではないので、それは必要ではありませんが、悪い考えでもありません。関数に加えることができるいくつかの変更があります。の3番目の句全体を削除しcond(= iivar 2)ケースを最後の句にして、iivarそこで3に設定する必要をなくすことができます。まあ、それはすでに良いです。

しかし、待ってください、関数は(eq last-repeatable-command 'incremental-insert-o)最大3回呼び出されます!それは沢山。書き直してみよう!interactiveまず、次のように、呼び出しを使用して基本関数の定義から始めましょう。

(defun incremental-insert-o ()
  (interactive))

次に、コードから物事を再構築します。iivarまず、正しく追跡できるかどうかを見てみましょう。読みやすくするために、その変数の名前をに変更しますincremental-insert-o-consecutive。EmacsLispには単一の名前空間があるため、という名前の変数を使用する他のすべての変数は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ずつ増加し、何か他のことをするとリセットされます。今、私たちは正しいメッセージを印刷する必要があります。何を印刷したいですか?さて、最初に関数を呼び出すときは、1つ「o」を出力し、次に2回目は「oo」を出力し、3回目以降は「ooo」を出力します。2番目の文字列を印刷すると、最初の文字列が2回印刷され、3番目の文字列が最初の文字列を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 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はMAXINTとして少なくとも536870911を保証します。それでは、その日を呼びましょう。コードを短くし、繰り返しの部分はありません。読みやすくなると思います。

于 2013-01-13T08:48:52.743 に答える
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インタープリターを追い越すことはできません、それはすべて良いです;)


これが私があなたのコードに少し改善するためにしたことです(今では最悪のシナリオは6つではなく5つの条件しかチェックしません:)

(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 に答える