« プログラミングを始めるには(27) | Main | プログラミングを始めるには(29) »

August 25, 2014

プログラミングを始めるには(28)

defunの次はletの話。私が普段書くプログラムでletを使わないものはほとんどないと言って良い。

let is a special form in `C source code'.
(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.

これはシンボルをローカルに(局所的に)定義して、それを使用したprogn(式を順番に評價していって最後の式の結果を返す)を書くためのスペシャルフォームである。letはパラレルバインディング、let*はシリアルバインディングになっている。二つの違いは定義したローカルシンボルを、次のローカルシンボル定義で使用出來るかどうかなので、使用頻度としては必然的にlet*の方が多くなる。

こういう基本的なものについては、眞面目に書くとむずかしくなるだけなので、何か他の似たもので說明すると分かりやすい。

main()
{
  int a = 1;
  int b = 2;
  int c = 3;
  int d = 0;

  d = a + b + c;

  printf("d = %d\n", d);

  return 0;
}

(defun main ()
  (let ((a 1)
	(b 2)
	(c 3)
	(d 0))
    (setq d (+ a b c))
    (message "d = %d\n" d)
    0))

ではこの2つを比較すると、どこが違うのか。Celispでは言語が違うのだから書き方が違うのは當然として、ローカル變數abcdを定義して、abcの合計をdに設定し、それをプリントするという意味ではほとんど同じに見えないだろうか。

main()
{
  int a = 1;
  int b = 2;
  int c = 3;
  int d = 0;

  {
/*     int a = 10; */
    int b = 20;
    int c = 30;
    int d = 0;

    d = a + b + c;

    printf("d = %d\n", d);
  }

  d = a + b + c;

  printf("d = %d\n", d);

  return 0;
}

(let* ((a (let* ((a 1)
		 (b (+ a 1))
		 (c (+ a b 1)))
	    (print (format "a=%d, b=%d, c=%d, total=%d" a b c (+ a b c)))
	    a))
       (b (let* ((a 10)
		 (b (+ a 10))
		 (c (+ a b 10)))
	    (print (format "a=%d, b=%d, c=%d, total=%d" a b c (+ a b c)))
	    b))
       (c (let* ((a 100)
		 (b (+ a 100))
		 (c (+ a b 100)))
	    (print (format "a=%d, b=%d, c=%d, total=%d" a b c (+ a b c)))
	    c)))
  (print (format "a=%d, b=%d, c=%d, total=%d" a b c (+ a b c)))
  (print (+ a b c)))

"a=1, b=2, c=4, total=7"

"a=10, b=20, c=40, total=70"

"a=100, b=200, c=400, total=700"

"a=1, b=20, c=400, total=421"

421
421

慣れないとごちゃごちゃしているように見えるかもしれないが、この例でlet*の性質が分かる。既に定義濟みのシンボルをオーバーライドすることが出來る。C言語でもブロックで圍むことにより、Lispほどスマートではないが同樣のオーバーライドが可能だ。しかし、性質はそうでも使用目的はたいていの場合、データの局所化はもちろんだが、處理の局所化というニュアンスが强いと思う。

(let* (a b total)
  (setq a (save-excursion
	    (set-buffer (find-file-noselect "0015.el"))
	    (goto-char (point-min))
	    (read (current-buffer))))
  (setq b (string-to-number (read-string "b=")))
  (setq total (+ input-a input-b))
  (format "a=%d, b=%d, total=%d" a b total))

(let* ((input-a (save-excursion
		  (set-buffer (find-file-noselect "0015.el"))
		  (goto-char (point-min))
		  (read (current-buffer))))
       (input-b (string-to-number (read-string "b=")))
       (total (+ input-a input-b)))
  (format "input-a=%d, input-b=%d, total=%d" input-a input-b total))

上の2つは結果は同じだが、私は下の書き方を好む。變數aと變數bの入力處理がバインディングのところに集約されるからだ。このあたりはもっと複雜な機能になった場合にプログラムの見通しという點で重要になると思われる。

|

« プログラミングを始めるには(27) | Main | プログラミングを始めるには(29) »

Comments

Post a comment



(Not displayed with comment.)


Comments are moderated, and will not appear on this weblog until the author has approved them.



TrackBack

TrackBack URL for this entry:
http://app.cocolog-nifty.com/t/trackback/74224/60206527

Listed below are links to weblogs that reference プログラミングを始めるには(28):

« プログラミングを始めるには(27) | Main | プログラミングを始めるには(29) »