プログラミング再入門

プログラミングをもう一度ちゃんと勉強する読書ノート

Scheme修行 第14章 名前をつけましょう(その2)

letを使って余計な計算を避ける方法を習います。

ノート:

depth*

depth*は

  1. 木に適用する関数で
  2. 数を値とする

その値は

  1. 木が空リストであれば、1
  2. 木の先頭要素がアトムであれば、木の残り部分にdepth*を適用した値
  3. そうでなければ、
    1. 木の先頭より後ろにdepth*を適用した値が、木の先頭部分に*depthを適用した値に1加えた値よりも大きい場合、木の先頭より後ろにdepth*を適用した値
    2. そうでない場合、木の先頭部分に*depthを適用した値に1加えた値

木の階層の深さを表す。

最初の定義をdepth1*として動作確認をする。

> (let ((l '((pickled) peppers (peppers pickled))))
    (depth1* l))
2
> (let ((l '(margarine
             ((bitter butter)
              (makes)
              (batter (bitter)))
             butter)))
    (depth1* l))
4
> (let ((l '(c (b (a b) a) a)))
    (depth1* l))
3
> 

letは値に名前を結びつける時点で値の部分を評価してしまうので、引数のチェック等が必要な場合はletの位置を良く考える必要がある。
二番目の定義をdepth2*として動作確認をする。

> (let ((l '(()
             (((bitter butter)
               (makes)
               (batter (bitter)))
              butter))))
    (depth2* l))
. . mcar: expects argument of type <mutable-pair>; given ()
> 

letで値に名前を結びつける時点で、最低1回は実行される事が分かっている関数でないと冗長になる。

letを正しい位置に置いた版をdepth3*として動作確認をする。

> (let ((l '(()
             (((bitter butter)
               (makes)
               (batter (bitter)))
              butter))))
    (depth3* l))
5

同じ考え方をすると(depth* (cdr l))の部分はもう一段前で実行する事は可能。この定義をdepth4*として動作確認をする。

> (let ((l '(()
             (((bitter butter)
               (makes)
               (batter (bitter)))
              butter))))
    (depth4* l))
5
> 

あまり効率を求めるとコードの可読性は落ちる。

if、max

condで条件が一つとelseしかないのであればifが使える。elseを書く必要がない。

maxを使った版のdepth*をdepth5*として動作確認をする。

> (let ((l '(()
             (((bitter butter)
               (makes)
               (batter (bitter)))
              butter))))
    (depth5* l))
5
> 

それぞれの引数はmaxを呼び出す時に一回ずつ評価されるのでletを用いるのと同じ効果が得られる。

scramble

内部関数Pを使ったscrambleでも(cons (car tup) rup)の部分が分岐のどちらに行っても1回実行されるのでletを使って関数定義を短く出来る。

> (scramble '(1 1 1 3 4 2 1 1 9 2))
(1 1 1 1 1 4 1 1 1 9)
> (scramble '(1 2 3 4 5 6 7 8 9))
(1 1 1 1 1 1 1 1 1)
> (scramble '(1 2 3 1 2 3 4 1 8 2 10))
(1 1 1 1 1 1 1 1 2 8 2)
> 

本の定義はPの引数rpに対して次のPの呼び出しで使うリストにもletで同じ名前rpを割り当てているので注意。似ていても異なる名前にした方が良い気がするが。