[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

learning lisp別冊: 6点入力が題材



渡辺@雑用片付けモードです。

6点入力をキーボードで行うサンプルコードをn高橋さんが書いてくださった後、
実は、高橋さんに、私が書いたコードを添削してもらっていました。Lisp講座の
一環として参考になるかもしれないので、そのときのメールのやり取りを以下に
まとめて書きます。
# もう高橋さんのLisp講座に使われたネタもあるけどね。(笑)
なお、オリジナルの自明な間違いなどは修正してあります。

0) 高橋2渡辺
   省略

1) 渡辺2高橋

これでどうですか?

(defun braille-test ()
  (let ((i 0) (braille-str) (this-char))
    (ding)
    (while (< i 100)
      (while (not (= ?  (setq this-char (read-char))))
	(setq braille-str (concat braille-str (char-to-string this-char))))
      (insert (format "{%s} " braille-str))
      (setq braille-str nil)
      (setq i (1+ i)))
    (ding)))

2) 高橋2渡辺

文字の比較には = ではなくて char-equal を使った方がお行儀がいい
と言えます。まあ面倒なので私もよく = でやっちゃいますが。

また string の concat は Emacs Lisp にとって比較的重たい操作ですので、
できるだけ回数を減らすようにした方が速度の点で有利です。

それから braille-str はストリングを格納する変数ですから、リセットする
ときは nil よりも "" の方がいいでしょう。変数に型がないのが Lisp のい
いところでもあるんですが、プログラミング作法の点からするとストリング用
の変数にセットする物はストリングで統一した方が better です。

あと、これは好みの問題ですが、
  (let ((i 0) (x))      ;; 1
は
  (let ((i 0) (x nil))  ;; 2
や
  (let ((i 0) x)        ;; 3

と等価です。いずれも x は nil で初期化されますが、初期値が nil であるこ
とを明示したいときは 2 を、初期値を気にしない場合は 3 を用いることが多
いようです。1 は文法的には正しいのですが、なぜそう書いたのかがはっきり
しないので避けた方がいいかもしれません。

で、私ならこう書きます。点字入力を10文字分受け付けます。

(defun braille-test ()
  (let ((i 0) this-char)
    (ding)
    (insert "{")
    (while (< i 10)
      (setq this-char (read-char))
      (if (not (char-equal this-char ?\ ))
	  (insert this-char)
	(insert "} {")
	(setq i (1+ i))))
    (delete-char -2)
    (ding)))

ずるいでしょ。:-)

3) 渡辺2高橋

(メールが行方不明??)

4) 高橋2渡辺

> 文字型は整数で表されていても char-equal のほうが良いのですか。

たしかに Emacs では文字を整数で表現していますが、プログラムの可読性を
上げるために文字同士を = で比較するのはやめた方がいいと思います。
case-fold が問題になる可能性がある場合は eq を使うべきかと思います。ち
なみに XEmacs では文字型は数字ではありません(相互変換はできますが)。

>> また string の concat は Emacs Lisp にとって比較的重たい操作ですので、
>> できるだけ回数を減らすようにした方が速度の点で有利です。

> ELispの文字列の長さは固定されているので、毎回新しいセルを作るからですか。

そうです。concat するたびに元のデータを新しい領域にコピーします。私も
あまり詳しいことは知らないのですが、「ストリングの生成は重い」と半田さ
んが言ってました。

> concatに限らず文字列の操作は比較的重たい操作なのでしょうか?

というわけででもないと思います。バッファの中の文字列の比較や操作は効率
的にできるようになっているはずです。何しろエディタですから。

> (let VARLIST BODY...): bind variables according to VARLIST then eval BODY.
> The value of the last form in BODY is returned.
> Each element of VARLIST is a symbol (which is bound to nil)
> or a list (SYMBOL VALUEFORM) (which binds SYMBOL to the value of VALUEFORM).
> All the VALUEFORMs are evalled before any symbols are bound.

> だから、シンボルかリストを書けて、(synbol)としか書かないのはよくなさげ
> ですね。どうしてこれでもOKになるのだろう。

まあ多分歴史的経緯でしょうね。
あともしかしたら (symbol) と (symbol . nil) が等価であるということが関
係しているのかもしれません。

>> で、私ならこう書きます。点字入力を10文字分受け付けます。
>> 
>> (defun braille-test ()
>>   (let ((i 0) this-char)
>>     (ding)
>>     (insert "{")
>>     (while (< i 10)
>>       (setq this-char (read-char))
>>       (if (not (char-equal this-char ?\ ))
>> 	  (insert this-char)
>> 	(insert "} {")
>> 	(setq i (1+ i))))
>>     (delete-char -2)
>>     (ding)))
>> 
>> ずるいでしょ。:-)

> これの欠点は、最後や中断時に余分な  { が残ることですね。

一番最後まで行けば残りませんよ。(delete-char -2) してますから。
また途中で中断されたときの後かたづけをちゃんとやるためには
unwind-protect という関数を使います。

(defun braille-test ()
  (let ((i 0) this-char)
    (insert "{")
    (unwind-protect
      (while (< i 10)
        (setq this-char (read-char))
        (if (not (eq this-char ?\ ))
            (insert this-char)
          (insert "} {")
          (setq i (1+ i))))
      (if (eq (preceding-char) ?\{)
	  (delete-char -2)
	(insert "}")))))

> それともともとの目的が同時入力されたキーを取得することだから、
> やはりどこかの変数に納めなければいけないと思います。
> ( 揚げ足取り(^.^) )

> では以下のように文字列の代わりにリストを使うのはどうですか?

すでにバッファ内に入っているのですから、buffer-substring を使って同時
入力されたキーを返せばいいんじゃないでしょうか。

> (defun braille-test ()
>   (let ((i 0) (braille-str "") (braille-list nil) this-char)
>     (ding)
>     (while (< i 10)
>       (while (not (char-equal ?\  (setq this-char (read-char))))
> 	(setq braille-list (cons this-char braille-list)))
>       (setq braille-str (mapconcat '(lambda (x) (char-to-string x)) 
> 				   braille-list ""))
>       (insert (format "{%s} " braille-str))
>       (setq braille-list nil)
>       (setq i (1+ i)))
>     (ding)))

文字のリストをストリングに変換するのなら、mapconcat を使う必要はありま
せん。単に (concat braille-list) だけで OK です。

(concat '(?a ?b ?c)) => "abc"

--
渡辺隆行 (WATANABE Takayuki)