Lispの話(データの読み書き)
こんにちは、たくさん寝太郎です。
かなり涼しくなって秋を感じますね〜。
もうちょっと夏らしいことしておけば良かったと後悔しています。
さて、今回はデータの読み書きの話、同図象性の話をしようと思います。
テキストの表示と読み込み
print,prin1,princ
printは引数をコンソールに表示する関数です。
(print "foo") => "foo" (progn (print "I") (print "am")) => "I" "am"
2つ目のコードから分かる通り、printは空白と改行を付加します。
(progn (prin1 "I") (prin1 "am")) => "I""am"
prin1を使うと空白も改行も付加されません。
(progn (princ "I ") (princ "am")) => I am
princは文字列を"で囲まず人が読みやすいように表示してくれますが、一度出力すると適切なLispのデータ構造に戻すのは難しくなってしまいます。(対称性の破れ)
read
readはユーザがREPLに打ち込んだ内容を読み取ります。
(read) (+ 1 2) => (+ 1 2)
この通りreadはデータを読み込むだけで評価は行いません。
データを評価したい時はevalを使います。
(eval (read)) (+ 1 2) => 3
(let ((name (read))) (princ name)) takusan netaro
このコードを実行すると「variable NETARO has no value」とerrorが出てきます。この問題に対処するためにread-lineを使います。
(let ((name (read-line))) (princ name)) takusan netaro => takusan netaro
read-lineは改行までのデータを読み取り、文字列として返します。
Lispにおけるコードとデータの対称性
プログラムコードとデータを同じデータ構造を使って扱うプログラミング言語は同図象性を持つ(homoiconic)と言います。
準クオート
以前の記事でデータモードの切り替えには'(シングルクオート)を用いると話しましたが、データの一部分だけコードモードに戻したい時は`(バッククオート)を用います。この機能を準クオートと言います。
`(+ 1 ,(* 2 3)) => (+ 1 6)
コードモードに戻したい部分の前に,を付けておくとその部分を評価することができます。(アンクオート)
では次のようにコードを変数に格納してみましょう。
(defparameter *foo* `(+ 1 ,(* 2 3)))
*foo*には(+ 1 6)というデータが格納されますが、これを評価する(エバる)には先ほど紹介したevalを用います。
(eval *foo*) => 7
[注意]〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
evalやreadは便利に見えますが悪意あるコマンドを渡された時に実行してしまうという脆弱性が存在します。熟練したLisperであればこれらのリスクを回避する事も出来ますが、慣れない内は無闇にこれらのコマンドを使うのは避けた方が良いでしょう。
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
このようなデータとコードの対称性によりLispは同図象性を持つ言語の代表となっています。
次回は高階関数、lambda記法の話をしようと思います。