(書評)プログラミング言語Gaucheの感想(詳しい版)
初めに
前回もこの本に関する感想を書いたのですが,メモが残っていたのでそれを遺します.
全部は読んでないですし,章ごとに内容の密度が全然違うと思いますがご了承ください...
5章まで
環境設定やこの本で扱う関数型言語Schemeの話,処理系Gaucheの話が中心です.
また他の言語とも共通する基本的な文法の話もココらへんで終わります.次章から徐々に「関数型言語っぽさ」が出てきます.
6章「リスト」
リストは基本でありSchemeで最も重要な概念だと思います.なのでこの章と次の手続きの章はしっかり読んだ方がいいと思いますね.
対で表現することで中の木構造がはっきりする点がこの言語の長所なのかなと思います.
また,この章では再帰を扱いますが,この再帰に関しては紙とペン(タブレットとスタイラスペンでもいいです)を持って流れを追って理解しましょう.
for文を使って手続き型言語に慣れていると書いてしまいますが,再帰でも書けるようになれるようにすれば少しずつリストと対の重要性がわかってくると思います.←(初心者並みの感想)
7章「手続き」
この章では主に手続きを扱っているのでlambdaが頻出...関数型言語の醍醐味ですね(?)
入れ子構造になったリストで手続きを取る手続きfoldが出てきます.
これこそがSchemeの根幹なのかなとまた関数型言語初心者の私は思ってしまい,複雑ではありますがデータ構造の一部として手続きを扱えるので慣れればかなりの武器になると感じました.
また,コラムで触れられていますが何やらSchemeは宣言的な(declarative)プログラミングに適しているようです.
他の手続き的な(procedural)言語とはどのような発想の違いがあるのかFizzBuzzを例に説明しています.
前者は"What", 後者は"How"に注目するものだそうです.
手続き型的な発想,では毎回特殊な処理を行いそれを繰り返すという発想でプログラミング
をし,
宣言的な発想では
「データからデータへの変換を考えれば良く,出力は後からどうにでもなる」
という発想をするので結果としてモジュール性,つまり汎化性能があがるそうです(要はプログラマーとしての実力が上がるよということでしょう).
8章「真偽値と条件判断」
真偽を判断する手続きを述語(predicate)といい,特に等価性を判断するものを等価述語(equivalence predicate)と言います.
この章では主にこの等価述語について触れ,それ以外の述語についても触れています.
ここではよりSchemeのデータ構造に関して踏み込んでいて,その「形」が非常に重要でそれを含めると様々な「等しい」という基準があるということが書かれています.
Schemeでは計算とは式の値を求めることで,全ては式だそうです.
なぜならSchemeでは手続きそのものも値だから,手続き自体を返す式が書けるためです.
本書では例としてある値の正負にお応じて+手続きと-手続きを返せることがあげられています.
そして,計算対象が全て式なので代入文(assignment statement)はなく置き換え(substitution)モデルのみで説明可能
9章「状態の管理」
これまでの内容を使って色々なデータ構造を書いていこうという章です.
手続き型言語と比べてSchemeでは破壊的な操作が少ないようです.というのも破壊的な操作には引数と戻り値以外の状態も考える必要があり考慮することが増えるからだそうです.
連想リストは対のリストなので2つのcar
を組み合わせたcaar
という手続きが出てきて面白いです.
ここでは連想リストを使うのでこれを用いてリストの中にリストがある場合に今までする処理をどう実装するかみたいなことをしています.
この章では具体と抽象の往復が書かれており,とりあえず典型的な処理を書き,その中で共通する点を見つけてそれを抽象的なプログラミングに落とすということが重要らしいです.
10章「テストと例外処理」
gauche.testモジュールを使ってテストを行うという章です.ただ,個人的にSICPの前段階としてやっているのでこの章はサラッと読みました.
11章「評価モデル」
この章では第3部の機能の拡張のための基礎となる部分の話をします.
タイトルの評価モデルとは一般性を損なわず些末な点から開放されてプログラムの意味を理解するための抽象的なモデルのことで,規則の集合です.
その規則を使ってプログラムを変形する仮想的な機会を評価機と呼びます.
置き換えモデル,環境フレームモデルという2つのモデルにここでは触れています.
後者のモデルを導入することで前者で生じる副作用を回避することができるという話がされています.
12章「数値」
ここから第3部「実用的なプログラミング」となり,今までの章の応用的な話が増えます...がおそらく簡易レファレンス的な使い方も出来るように文法のまとめがなされています.
この章ではGaucheの扱えるデータ型に関する話がされています.
面白いのはGaucheには有理数という型があるという点です.また,整数はメモリの許す限りいくらでも大きい数を扱えます.
また複素数を普通に使えるところが面白いです.例えば
'''Scheme (sqrt -1) '''
と打つと
"""Scheme 0.0 + 1.0i """
と複素数で返ってきます.
また数に正確数と非正確数があるというのが面白いです.前者は'.'が式にない値,後者はある値です.例えば"1, 1/2, (+124 4)"は正確数で"1.0, 1/2.0, 0.5+1.0i"は非正確数です.
比較演算子は2項演算子ではなく,リストない全ての数を評価します.
四則演算は基本は普通と同じですが,減算と除算は引数が一つのときは符号を反転したものと逆数を返します.また"+."など"."を加えることで非正確数を返すように出来ます.
文字列に関しては特筆するところはないかなと.感想としては結構Gaucheでは文字列は扱いやすそうです.
14章「出入力」
この章は出入力を扱います.出力も入力も出来ないというのはプログラミングにおいてほとんど何も出来ないということを意味しますが,同時にそれだけ重要なので割と面倒くさい言語が多いと思います.
C言語のファイル入出力はポインタも考える必要があるので難しい(現在形)です.
でもこれさえ乗り切ればその言語の初心者くらいは名乗れると思うので(競技プログラミングも出来る!)要点だけは抑えるようにしたいです.
GaucheではC言語同様に入力が終端に達したときにEOFオブジェクトが返されるそうです.なのでCtrl-dで終了させることが可能なようです.
出力は色々種類があります.write
display
print
format
が主なものです.
write
は引数のオブジェクトを外部表現に出力します.write/ss
と書くとそのオブジェクトが共有構造を持つかまで表示してくれます.(共有構造とは部分集合を持っているということです)display
は引数のオブジェクトを読みやすい形に変えます.例えば#\a
を引数とする場合,write
だとそのまま#\a#<undef>
と表示しますがdisplay
ではa#<undef>
と表示します.(#<undef>
はどちらも戻り値です.)print
は複数のオブジェクトをdisplay
形式で出力し最後に開業します.format
は(format <出力先> <書式指定> <引数> ...)こんな感じで書き,パディングなどが出来ます.出力先は真偽値で標準出力など選ぶことができます.書式指定はC言語で言う%s
みたいなものです.
Scheme自体は動的型言語ですが,変数束縛に関して静的であり変数が出現するソースコード上の位置で決まります.これを静的環境と呼ぶことがあるようです.(前まで混同していてSchemeも静的な型宣言が出来ると思っていました)
15章「テキストの構築」
ここではCGIスクリプトをHTMLに出力する方法など14章の出力の部分の延長をやります,興味がない(SchemeやGaucheを使ってアプリケーションを作る気はない)ので最初の方だけ読んで具体的な方法に関しては飛ばし読みをしました.
16章「様々なデータ構造」
今までは対とリストを中心に扱ってきましたが,これらを利用してこの章では集合,列,連想リスト,木など,Gaucheで提供されているデータ構造について紹介されています.以下では本書で取り上げられたものについて書いていこうと思います.
- 列としてはベクタが紹介されています.
- ベクタは値の並びで一次元配列だと考えて良いでしょう.多次元配列はarray.gaucheで使用できるようです.
#(<要素1> <要素2> ...)
と書きます. - しかし#の前には`(バッククオーテーション)を書いてクオートすることに注意です.
- 他にも
(vector <要素1> <要素2> ...)
と書くなど色々と書き方があります.
- ベクタは値の並びで一次元配列だと考えて良いでしょう.多次元配列はarray.gaucheで使用できるようです.
- 連想配列としてはハッシュテーブルとツリーマップとストリームが紹介されています.
- ハッシュテーブルは
make-hash-table
という手続きで作成が可能です.要素に順番は記憶されていません.要素の追加はhash-table-put!
で取り出すのはhash-table-get
削除はhash-table-delete!
で行います. 要素の取り出しの際は
eq?
つまり,メモリ上で同じオブジェクトかということが問われるので注意です(ちなみに空リストはメモリ上に一つしかないのでこれも注意です).文字列をキーにするためにはmake-hash-table `string=?
と変え以下のように書きますScheme (define table (make-hash-talbe `string=?))
ツリーマップはハッシュテーブルと異なりキーの順序を保存しているのでキーの大小関係を求める際にはハッシュテーブルよりは効率的です.実装は上の
hash-table
の部分をtree-map
に変えるだけです.- ストリームは遅延評価のためのデータ構造です.そのため遅延リストと呼ばれることもあるそうです.「遅延評価とは必要になったときだけ計算する」という意味らしいです.そのため無限の長さのデータ構造も扱うことが出来るようです.Pythonで言うイテレーションです.(わかりやすい解説)
- ハッシュテーブルは
17章「総称関数とオブジェクト」
7章の説明で書いたようにモジュール性を重視するSchemeの処理系のGaucheらしく,Gaucheには総称関数と呼ばれる引数の型を判断して適切な処理を選択する手続きがあります.
例えばref
はstring
, vector
, list
など引数に合わせてhash-table-get
のようにしていた要素を取り出します.これはCommon Lispから輸入された概念だそうです.
総称関数は内部的にはメソッドの集まりなので新しいメソッドを定義することも可能です.
使い捨てのツール,アイディアを試すときに手軽書けるというものです.
また値を集めたコレクションというデータ型とコレクションの性質に加え要素の順序が意味を持つシーケンスというデータ型が紹介されています.これらは今までに出て来たデータ型の復習ですね.
Gaucheではオブジェクト指向をサポートしています.多分オブジェクト指向の言語を触ったことがあれば大体何をしているかは分かると思います(というか今から始める人で触ったことない人っているのかな...?).
CPLと言うクラスの優先度リストが無いとクラスの継承が出来ないのだけ注意です.
実はGaucheではすべてのデータ型がオブジェクトシステム上で構築されているのでオブジェクト指向言語処理系とも言えるそうです(top$\subset$number$\subset$real$\subset$rational$\subset$integetのように).
他のオブジェクト指向言語とは異なりfoo.bar
のような書き方ではないため特段それを木にしないでコーディングが出来るようになっています.
18章「構文の拡張」
Schemeでは他の多くの言語のように予約語というのがなく,基本的な構文もあくまで初期値が設定されているだけで通常の変数と同様にスコープ次第ではローカル変数が定義されて,大域的なifは上書きされます.
マクロの紹介もこの章ではされています.しかし,割とあっさりされているので正直マクロの凄さみたいなのはよくわかりませんでした...(実力不足です)多分,しっかりマクロについて書かれているっぽい「On Lisp」を読んだ方がいいですね(読む本リストに追加しておきます()).
19章「継続」
継続とは一言で表すと
「プログラムのその時点より後に行う計算」
だそうです.この章ではその継続を扱います...が正直よくわかりませんでした!!!
このサイトに載っている情報も見たんですが(大体書いているのは著者のShiroさんです笑)わからない...
時間が経ったら分かるようになるタイプの概念かなと思います()
19章「モジュールシステム」
ライブラリの話です.適宜参照しましょうという章です.
20章「デバッグ」
デバッグするときに有用なマクロや手続きが紹介されています.こちらも適宜参照しましょうという感じです.
21章以降+まとめ
これ以降の章はデータベースの話などWebアプリケーション開発(実際24章からはWebアプリケーションへの応用について書かれている)が多いので自分の目的上は必要がないと判断して読みませんでした.
この本を一冊こなせば後はネットにある情報を参照すれば大分書けると思います.詰まったときは参考書として読むみたいな使い方を今後は想定しています.
後,Schemeという言語は文法は簡素であり,あまり勉強することはなく書き手の実力が出そうな言語だという印象を受けました.ですので,しっかり勉強しないとダメだなぁといういつもの感想を抱きました...
まあとりあえず後はSICPを読みながら手を動かそうと思います!