はじめに
OCamlのモジュールシステムとCommon LispのCLOSは、どちらもプログラムを構造化し、再利用性を高めるための強力な仕組みです。しかし、そのアプローチは大きく異なります。今回は、図形を描画するという同じ具体例を使って、それぞれの違いを見ていこうと思います。
OCamlのモジュールシステムとCommon LispのCLOSは、どちらもプログラムを構造化し、再利用性を高めるための強力な仕組みです。しかし、そのアプローチは大きく異なります。今回は、図形を描画するという同じ具体例を使って、それぞれの違いを見ていこうと思います。
Rustのトレイトは、型が持つべき振る舞い(メソッド)を定義する機能です。これは、他の言語におけるインターフェースや型クラスに非常に似ています。
ラムダ計算とは、簡単に言ってしまうと関数だけから成り立つ計算モデルで、すべての計算可能な関数が表現でき、正しく評価できるものらしいです。
関数型言語の理解も深まるだろうということで、最近勉強しています。
やってみると、なかなか面白く、なるほどの連続です。
では、実際どのように表現できるのか見ていこうかと思います。
親父の死後、遺品整理をゆるゆると進めている流れで、自分の不要なモノも整理しようと、クローゼットのダンボールを開けていたところ、学生時代の教科書が出てきました。
経済学部だったので、経済原論、経済史、経営論など「あ〜やったな、こんな授業」といった懐かしい本がいっぱい入っていました。
懐かしく感じながら見ていると、なんとムイスリ出版社「Pascalの基礎」という3.5インチ2DDフロッピー付きの本が出てきました。
私はどうやら学生時代にPascalの授業を受けていたようです。
このlocally abstract typeなるものが役立つところは、まずファーストクラスモジュールだそうです。
まずは簡単なシグネチャを書いてみます。
module type Id = sig
type t
val id : t -> t
end
「Real World OCaml」(以下RWO)に書かれている通りにやってみると、私の場合エラーが発生しました。OSはManjaro Linuxです。conf-pkg-config.2のビルドで失敗したようです。
どうやら私のマシンにはpkg-configが入っていないので、このエラーが発生したようです。
Manjaroではpkgconfという名前でライブラリが提供されているようなので、そちらをダウンロードし、再びインストールを試みたところ、うまくいきました。
もし、同じようなエラーが発生している方がいらっしゃいましたら、pkg-configの有無をチェックしてみてください。
またRWOでは以下のように書かれていますが、
「Real World OCaml」(以下RWO)のCommand-Line Parsingの章を読んで、コードを試していたところ、例となっているプログラムのCommand.runがうまく動作しませんでした。
utopでチェックしてみたところ、以下のような表示になりました。
OCamlにおいて、基本的にはモジュールを引数に取り、モジュールを返すというものですが、モジュール自体の使い方によって、意味合いが違うものになるのではないかと個人的には思っています。
OCamlでは、整数と浮動小数点数の演算子が明確に区別されています。その理由は「Real World OCaml」に書かれていますが、整数型と浮動小数点数型間で、自動的にキャストしないことで他言語で発生する予期しないバグを防ぐためです。このバグについてはこちら
近年、Pythonではプリミティブな型やtypingで型アノテーションを記述し、mypyで型チェックするという流れができているようです。例えばint型を2つ受け取り、int型を返すplus関数がある場合、以下のように型アノテーションを書きます。
>>> def plus(x: int, y: int) -> int:
... return x + y
...
プログラミング言語でよく目にするファーストクラスという言葉。例えば某言語で関数はファーストクラスだ、とすると、その言語では関数を変数に格納できたり、関数のパラメータとして渡すことができたり、関数の戻り値として返すことができたりするということです。
OCamlのマニュアルのChapter3にオブジェクト指向についての記述があります。要約すると、”OCamlにおけるobject,class,typeはJavaやC++のようなメインストリームのオブジェクト指向言語とは違う。OCamlではそれらの機能をそれらの言語ほど頻繁には使わない。その代わりに、モジュールやファンクタを使う。” というようなことが書かれています。
他言語のclassなどのようにモジュールやファンクタを使えるということか。
つまり、モジュールやファンクタを上手に使えるようになれ、ということのようです。
OCamlのListモジュールにはpartitionという便利な関数がありまして、これを使うとクイックソートが簡単に書くことができます。
let rec qsort = function
| [] -> []
| h :: t ->
let (l, r) = List.partition (fun x -> x < h) t in
(qsort l)@[h]@(qsort r)
こりゃ便利!と思い、他の言語でも使いたい!ということで自前で書くことにしました。
最近OCamlに触れることがよくあり、色々調べていると出てきたReason。公式ドキュメントによると、Reasonは新しい言語ではなく、OCamlで動く新しい構文とツールチェインだそうで、JavaScriptの負の遺産を取り除き、ES2030の機能を足し、JSとOCamlエコシステムの両方にアクセスできるもので、BuckleScriptによって、JavaScriptにコンパイルする、と書かれていました。ちょっと何言ってるのか分からない。しかし、OCamlを使っているので、強い型システム、安全性を持っているということは何となく分かります。
構文もOCamlによく似ているので、ひょっとしたら私にもできるのかもと思い、今回試してみることにしました。
L99-80からはグラフ理論についての問題になっています。当然ながら私はグラフ理論なんてものは人生初の勉強なので、まずは数学の方の勉強から入っています。勉強していると、意外と身近なもので面白いです。例えば一筆書き、路線図、などでもグラフ理論が使えるようです。
OCamlのL99問題のページでは、このあたりからほぼ解答がなくなってしまっていて、本当に自分の書いたコードが正しいのかどうかが分からないので、ここのところ続けていたOCamlのL99問題をCommon Lispで書いてみるという勉強も滞ってしまってます。なので、現在HaskellのH99を参考に、OCamlのコードやCommon Lispのコードを考えるという状況です。つまり今度はHaskellの知識が必要となってきて、数学と3つの言語を同時に勉強するという、えげつない状況になりました。天井がなく無限に拡がっていると捉えるか、底が知れず、どんどん泥沼にハマっていると捉えるか……
「OCamlのL99問題をCommon Lispで書いてみる」ことで、OCamlとCommon Lispを同時に勉強するという無謀な方法をしばらく地道に続けている私ですが、素人発想ながら、意外と効果があるのではないかと最近思っています。
ここ最近ずっとOCamlのL99をCommon Lispで書いてみるということをやっているのですが、あまり同じ言語ばかりやっていては、私の頭の中から他言語の知識が消えてしまう恐れがありますので、定期的に使う必要があります。悲しい脳みそです。
Common Lispのほうは現在L-37あたりまで進みましたが、今回は気分転換に、その先のL-46あたりのLogic and CodesというパートをRustでやってみようと思います。
まずドキュメントには以下のコードが書かれています。
# type bool_expr =
| Var of string
| Not of bool_expr
| And of bool_expr * bool_expr
| Or of bool_expr * bool_expr;;
OCamlのL99問題の32問目は2つの正の整数の最大公約数(the greatest commom divisor)を求めよ、という問題。これは私自身、過去にやったことがあるので、できる気がします(記憶が確かなら)
ユークリッドの互除法は例えば a = 10 b = 8 とした時、以下のような流れで計算していきます。
といった流れで求めることができます。
素数とは1より大きい自然数で、正の約数が1とそれ自身のみという数字のことで、私は学校での知識としての素数より、映画「博士の愛した数式」で寺尾聰さん演じる博士がもっとも愛した数字としての印象が強いです。この映画では素数の他にも「完全数」「友愛数」なども出てきます。この映画で、数学に興味を持った方は意外と多いのではないでしょうか。
今回はその博士に最も愛された素数についての問題をやってみます。これはこのところずっと勉強しているOCamlのL99の31問目が素数の問題だからですが、実は素数判定にはやり方が色々あるようで、OCamlのL99-31のドキュメントには「より賢い解決法として「エラトステネスの篩(ふるい)」というものがあるから調べてみて」と書かれています。
しばらく記事の更新が遅れてしまってました。実はここのところOCamlのL99問題の答えを読みながら、それをCommon Lispで書いてみるということをずっとやっていました。
なぜこのようなやり方をしているのかと言いますと、そもそも私はOCamlにはまだ慣れていないし、Common Lispも初心者という状態です。しかし、年齢的にも生活的にも時間が限られている。したがって、少しばかり理解しているOCamlのコードを読むことでより一層OCamlに慣れ、さらにCommon Lispで書くことによって、Common Lispにも慣れていく、という方法を思いつきました。そもそもL99問題の解答は、プロの方がベストなものを提供してくれているわけですから、よい見本にもなるのではないか、と思います。