プログラマとプロマネのあいだ

プログラマもやるし、プロマネもやるし、たまに似非アーキとか営業っぽいこともやるITエンジニアがスキルアップの話を中心に日常を綴るブログです。

「Ansi Common Lisp」12章読了

データの構造と題して、なんか抽象的な感じ。

共有構造

「あるリストが別のリストの一部」である状態を指すらしい。

* (setf part (list 'b 'c))
Warning:  Declaring PART special.

(B C)
* (setf whole (cons 'a part))
Warning:  Declaring WHOLE special.

(A B C)
* 

「あるリストが別のリストの一部」であるかどうかを検査するには、tailpを使う。

* (tailp part whole)

T
* (tailp '(b c) whole)

NIL
* 

書き換え

共有部分を書き換えると、両方のリストに影響しますよ。という至極当たり前の話のようです。

* (setf (second part) 'd)

D
* part

(B D)
* whole

(A B D)
* 

これが意図しない場合は、コピーを作って操作するとか、そもそもsetfみたいな副作用の伴う処理をしないのが良いのだそうです。

破壊的関数

たとえば、deleteはremoveの破壊版であり、引数として渡されたリストを破壊してよいことになっているが、書き換え内容に関する保証はない。

で、載っているサンプルを動かしてみる。

* (setf lst '(a r a b i a))
Warning:  Declaring LST special.

(A R A B I A)
* (delete 'a lst)

(R B I)
* lst

(A R B I)
* 

lstが(R B I)になっているかと思いきゃ、(A R B I)になっているというワナ。副作用もよく動作を理解して使わないといけないってことですね。

循環構造

リストのcdr部に自分自身を設定するとできるらしい。

* (setf x (list 'a))

(A)
* (progn (setf (cdr x) x) nil)

NIL
* x

(A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A
 A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A
...

大域変数*print-circle*をtにすると、循環構造用の形式で表示される。

* (setf *print-circle* t)

T
* x

#1=(A . #1#)
* 

これ、他の言語でもできるのかやってみた。
まずはRuby

lst = [1,2,3]
lst << lst

print lst

結果はこう。

123123[...]

[...]ってのが循環表記なのか?
Java

import java.util.*;

public class loop
{
	public static void main(String[] args)
	{
		List x = new ArrayList();
		x.add("a");
		x.add(x);

		System.out.println(x);
	}
}

結果はこう。

[a, (this Collection)]

今度は(this Collection)だって。なんかちゃんと考えられているのね。
次、JavaScript

<script language = "JavaScript">
lst = ["a"];
lst[1] = lst;

alert(lst);
alert(lst[1][0]);
</script>

結果はこう。

a,
a

循環構造に対して特別な出力はしないようですが、構造としてきちんと扱えていることは確認できますね。
Lispのデフォルトみたいに循環構造をprintすると暴走するような言語はなさそうです。
本書中ではバッファやプールを表現するのに役に立つことがあると書いてあるので、そういうシーン(なさそうですが。。)になったら思い出そう。

定数的データ構造

定数的データ構造とは、'(a b c d)みたいなやつのことらしい。
戻り値として定数的データ構造を戻す関数があったとして、戻り値に副作用を加えると、関数の挙動が変わってしまうという話。

* (defun arith-op (x)
    (member x '(+ - * /)))

ARITH-OP
* (nconc (arith-op '*) '(as it were))

(* / AS IT WERE)

; --------------------------------------------------
; この時点で、arith-opの中身は、(member x '(* / as it were))に変わっている
; --------------------------------------------------

* (arith-op 'as)

(AS IT WERE)
*