anything-mxを試してみた

■ - asdf

こっちでリファクタリングして試してみたけどiciclesみたいでおもしろい。いっそのことiciclesを捨ててanything一本にしたいくらいだ。

anything内部でanything-patternと(match. (...))が 使われえる直前でanything-patternの値を書き変えることができるように

実は現状のanything.elでもパッチを当てずに実現することができる。initでバッファ内に候補を書き出し、candidatesでre-search-forwardを使って動的に候補を絞り込む。動的に絞り込むのでvolatileを入れる必要がある。candidates内ではletでanything-patternを束縛しておかないといけない。このテクニックはid:IMAKADOさんの anything-c-moccur.el で使われている。

というか現状のanything.elがやっている文字列リストをstring-matchで絞り込むやりかたは遅い上、Emacsの精神に反している。↑のようにバッファ内でre-search-forwardを使うのが圧倒的に速いしEmacs流だ。re-search-forwardならばマッチしない候補を一気にすっとばしてくれるから速いのはうなずける。

現在この方法をなるべく少ないコードで実現するために改良している。手元に動くコードがあるけど、仕様が固まるまでもうちょっと待っててほしい。

anything.elメンテナとしては、anything-input-filterの導入には慎重だ。導入するとしたらむしろ、anything-sourcesで使える新しいattributeという形になるだろう。

 --spc2.*-loopはこんなに速いのか*1。たしかにloopはコンパイル時に展開されるのは知っているけど、Emacs Lispでそこまで劇的に速度が上がるとは思いもよらなかった。だけど1/100000秒短縮したところで意味ない気もする。

(defun --spc2.*-loop (input)
  (let* ((len (length input))
         (len_ (* 2 len))
         (ret (make-string len_ 0)))
    (loop for idx from 0 to (1- len) with idd
          initially (setq idd 0)
          do (let ((e (aref input idx)))
               (if (= e ? )
                   (progn (aset ret idd ?.)
                          (setq idd (1+ idd))
                          (aset ret idd ?*))
                 (aset ret idd e))
               (setq idd (1+ idd))))
    (subseq  ret 0 (loop for x_ from (1- len_) downto 0
                         unless (zerop (aref ret x_))
                         do (return (1+ x_))))))
(defun --spc2.*-rris (input)
  (replace-regexp-in-string " " ".*" input))

;; バッファを使うのは遅いのか…
(defun --spc2.*-buf (input)
  (with-temp-buffer
    (insert input)
    (goto-char 1)
    (while (search-forward " " nil t)
      (delete-region (match-beginning 0) (match-end 0))
      (insert ".*"))
    (buffer-string)))

(--spc2.*-loop "del o w")               ; => "del.*o.*w"
(--spc2.*-rris "del o w")               ; => "del.*o.*w"
(--spc2.*-buf "del o w")                ; => "del.*o.*w"

(benchmark-run 1000 (--spc2.*-loop "del o w")) ; => (0.10929799999999999 0 0.0)
(benchmark-run 1000 (--spc2.*-rris "del o w")) ; => (0.014977999999999998 0 0.0)
(byte-compile '--spc2.*-loop)
(benchmark-run 1000 (--spc2.*-loop "del o w")) ; => (0.00652 0 0.0)
(benchmark-run 1000 (--spc2.*-buf "del o w"))  ; => (2.097865 0 0.0)

しかし、1MBの文字列だと逆転してしまってる。バッファを使うのと大差なくなる。要はデータの大きさにあわせて適切なアルゴリズムを使えってことだな。ふむ。

(benchmark-run 1 (--spc2.*-loop s))     ; GCしまくりで計測不能
(benchmark-run 1 (--spc2.*-rris s))     ; => (0.7352460000000001 0 0.0)
(benchmark-run 1 (--spc2.*-buf s))      ; => (0.936077 0 0.0)

*1:バイトコンパイル忘れて遅いなんて書いてしまった。