WHAT'S NEW
CONTENTS
お気楽 OCaml プログラミング入門と Yet Another OCaml Problems のフォントを Web フォント (Noto Sans JP, Noto Sans Mono) に変更し、印刷用の CSS を追加しました。Web ブラウザの印刷機能を使って PDF に変換することもできます。表示が崩れるときはフォント Noto Sans Mono CJK JP をインストールしてください。
お気楽 OCaml プログラミング入門
CONTENTS
- 2020/06/21 はじめに
OCaml の特徴、プログラムの実行、簡単なベンチマーク、Enjoy Programming!!
- 2020/06/21 OCaml の基礎知識
使ってみよう、整数と実数、算術演算子、文字と文字列、比較演算子、論理演算子、条件分岐、変数、組 (tuple)、関数、局所変数と大域変数、問題
- 2020/06/21 再帰定義
再帰定義の基本、再帰定義のポイント、累乗の計算、累乗の高速化、フィボナッチ関数、ユークリッドの互除法、組み合わせの数、ハノイの塔、unit 型と逐次実行、問題
- 2020/06/21 相互再帰と末尾再帰
相互再帰、末尾再帰、フィボナッチ関数の高速化、問題
- 2020/06/21 高階関数
関数を引数にとる関数、多相性、匿名関数とカリー化、問題
- 2020/06/21 リスト
リストの構造、リストの構成法、リストの合成と分解、再帰定義によるリスト操作、リストの連結、リストの反転、リストの探索、問題
- 2020/06/28 パターンマッチング
match 式、function 文、リストのパターンマッチング、問題
- 2020/06/28 高階関数 (2)
マッピング、フィルター、畳み込み、畳み込みの使用例、問題
- 2020/06/28 ソート
挿入ソート、クイックソート、クイックソートの弱点、問題
- 2020/06/28 レキシカルスコープとクロージャ
レキシカルスコープ、レキシカルスコープと局所関数、クロージャ、連想リスト、ダイナミックスコープ、関数の合成、問題
- 2020/06/28 順列と組み合わせ
順列の生成、順列をリストに格納する、組み合わせの生成、組み合わせをリストに格納する、問題
- 2020/06/28 データ型の定義
ヴァリアントの定義、ヴァリアントの生成、パターンマッチング、option 型、再帰的なデータ型、連想リスト、レコードの定義、レコードの生成、レコードのパターンマッチング、多相的なレコード、問題
- 2020/06/28 ソート (2)
リストのマージ、マージソートの実装、問題
- 2020/06/28 例外
例外の定義、例外の送出、例外の捕捉、問題
- 2020/06/28 書き換え可能なデータ型
配列、配列の操作関数、参照、書き換え可能なレコード、多相性の制限、繰り返し、FizuuBuzz 問題、パスカルの三角形、数値積分
- 2020/07/05 ファイル入出力
標準入出力、ファイルのオープンとクローズ、input_char と output_char、ファイルの書き込み、問題
- 2020/07/05 バックトラック法
小町算、データ型の定義、式の生成、式の計算、実行結果、8クイーン、プログラムの作成、実行結果、8クイーンの高速化
- 2020/07/05 モジュール
スタック、スタックの定義、モジュールの定義、module による実装、シグネチャとデータ抽象、mli ファイルの定義、参照型を使ったスタックの実装、キュー、キューの実装、問題
- 2020/07/12 有理数
有理数の定義、有理数の生成、算術演算子の定義、比較演算子の定義、データ型の変換、実行例、モジュールの作成、小町分数 (1) (2)、単位分数の和、Four Fours、数式のパターン、データ型の定義、数式の計算、数式の生成、実行結果、補足:モジュール Num の使い方
- 2020/07/12 ファンクタ
二分探索木、木構造、二分木、二分木の実装、データの探索、データの挿入、データの削除、二分木の巡回、実行例、プログラムリスト、ファンクタの定義、シグネチャの定義、プログラムの作成、実行例、問題
- 2020/07/12 ファンクタ (2)
ファンクタのシグネチャ、with type、Map と Set、プログラムリスト、問題
- 2020/07/12 ハッシュ法
ハッシュ法とは?、プログラムの作成、ハッシュ関数の作成、実行例、ハッシュ法の長所と短所、Hashtbl の使い方、参考文献、プログラムリスト
- 2020/07/12 オブジェクト指向
オブジェクト指向とは?、クラスとインスタンス、メソッド、OCaml のクラス、インスタンス変数とメソッドの定義、オブジェクトの初期化、多相クラス、private メソッド、オブジェクトの型、OCaml の部分型、ダック・タイピング、辞書 (mutable) の実装、プログラムの作成、実行例
- 2020/07/12 集合
集合の基本操作、ファンクタの定義、union、intersection、difference、実行例、オブジェクト指向による実装、クラスの定義、バイナリメソッド、オブジェクトのコピー、実行例、プログラムリスト、問題
- 2020/07/19 継承
継承とは?、単一継承と多重継承、継承の仕組み、単一継承の使い方、オーバーライド、集合 (2)、initializer、抽象メソッドと抽象クラス、型変換、問題
- 2020/07/19 多重継承と Mix-in
多重継承の使い方、多重継承の問題点、Mix-in、enumerable、多相メソッド、リストクラス、実行例、クラスの関係、継承関係は必ずしも部分型にはならない、問題
- 2020/07/19 クラス型
クラス型の宣言、クラス型に名前を付ける、クラス型による型指定、クラス内の局所変数と局所関数、問題
- 2020/07/19 集合 (3)
モジュール内でクラスを定義する、オブジェクトを格納する集合、constraint、実行例、ファンクタ MakeSet でオブジェクトを格納する、問題
- 2020/07/19 経路の探索
グラフ、隣接行列と隣接リスト、バックトラックによる探索、幅優先探索、経路の管理、幅優先探索のプログラム、探索の一般化、反復深化、反復深化のプログラム
- 2020/07/19 パズルの解法 (1)
覆面算、魔方陣、対称解の排除、騎士の巡歴、マスターマインド、推測アルゴリズム、プログラムの作成、何回であたるか、問題
- 2020/07/26 パズルの解法 (2)
スライドパズルの説明、幅優先探索による解法、実行結果、双方向探索による高速化、最長手数の求め方、問題
- 2020/07/26 パズルの解法 (3)
反復深化による解法、下限値枝刈り法、下限値枝刈り法のプログラム、プログラムリスト、問題
- 2020/07/26 組み合わせの生成 (2)
ビット演算子、プログラムの作成、組み合わせに番号をつける方法、組み合わせを番号に変換、番号を組み合わせに変換、ちょっと便利なビット操作、ビットが 1 の個数を求める
- 2020/07/26 ラベル付き引数とオプション引数
ラベル付き引数、引数の順番、オプション引数、問題
- 2020/07/26 多相ヴァリアント
多相ヴァリアントの基本、多相ヴァリアントのデータ型、関数定義の拡張、問題
- 2020/07/26 メモ化と遅延評価
たらいまわし関数、メモ化による高速化、メモ化関数、遅延評価による高速化、問題
- 2020/07/26 遅延ストリーム
遅延ストリームの構造、遅延ストリームの生成、遅延ストリームの変換、遅延ストリームの操作関数、遅延ストリームの連結
- 2020/08/02 遅延ストリーム (2)
高階関数、マップ関数の便利な使い方、stream_flatmap、stream_take_while と stream_drop_while、組 (pair) を生成するストリーム、集合演算、ハミングの問題、順列の生成、8クイーンの解法、素数の生成、より高速な方法、双子素数
- 2020/08/02 継続渡しスタイル
継続とは?、継続渡しスタイルとは?、再帰呼び出しと継続渡しスタイル、二重再帰と継続渡しスタイル、CPS の便利な使い方、二分木の巡回を CPS で実装、二分木と遅延ストリーム、遅延ストリームを使わない方法
- 2020/08/02 便利なリスト操作関数
iota と tabulate、リストの分割、リストの置換、any と every、マッピング、リスト操作の一般化、解きほぐし (逆畳み込み)、問題
- 2020/08/02 便利なベクタ操作関数
データの探索、マッピング、畳み込み、イテレータ、リストとベクタの比較、二分探索、挿入ソート、クイックソート、問題
はじめに
OCaml は ML (Meta Language) という関数型プログラミング言語の一つです。ML は 1970 年代後半に Edinburgh 大学で定理証明を行うシステム Edinburgh LCF を記述するため、R. Minler 博士を中心に開発された言語です。その後、改良が重ねられ、いくつかの ML 処理系が作られました。その中で有名なのが SML/NJ (Standard ML of New Jersey) と OCaml (Objective Caml) です。Caml はフランスの INRIA 研究所で開発された ML 処理系で、そこにオブジェクト指向機能を加えたものが OCaml です。
UNIX 系 OS の場合、OCaml のインストールはパッケージマネージャを使うと簡単です。たとえば、Ubuntu であれば以下のコマンドでインストールすることができます。
sudo apt install ocaml
ただし、これでインストールされるのは少し古いバージョン (4.13.1, 2024 年 10 月現在) です。最新のバージョンをインストールする場合は OCaml のパッケージマネージャ OPAM を使用するようです。以下のページが参考になると思います。
Windows の場合、WSL (Windows Subsystem for Linux) もしくは Cygwin を使ったほうが簡単です。M.Hiroi は WSL2 で Ubuntu 22.04 を使っています。Cygwin には OCaml のパッケージが用意されているので、それをインストールするだけです。
●OCaml の特徴
関数型言語というと Lisp (Common Lisp, Scheme) が有名です。Lisp の場合、データに型はありますが、変数に型はありません。これに対し ML は強く型づけされた言語で、コンパイル時に静的な型チェックを行うことで、多くのエラーを検出することができます。
ML で一番有名な機能は「型推論」でしょう。ML はプログラムから変数などのデータ型を見つけてくれるので、プログラマが型を宣言する必要はほとんどありません。推論できない場合にかぎり、ML は型宣言を要求します。この機能により、ML は静的な型チェックを行う「型付きの言語」でありながら、 Lisp のような柔軟なプログラミングが可能になっています。
この他にも、パターンマッチング、多相型関数、モジュールなど、ML には興味深い機能がたくさんあります。なお、このような特徴は SML/NJ と OCaml で大きな違いはありませんが、文法面ではかなりの違いがあります。ご注意ください。
●プログラムの実行
OCaml はプログラムをコンパイルしてから実行します。OCaml のコンパイラは対話式コンパイラとバッチコンパイラの二種類があります。たとえば、シェルで対話式コンパイラ (ocaml) を起動すると、メッセージとプロンプト # が表示されます。この状態で OCaml のプログラムを入力して簡単に実行することができます。終了する場合は #quit;; と入力してください。
なお、ocaml にはコマンドラインの編集やコマンド履歴をたどる機能がありません。この場合、rlwrap をインストールすると便利になります。
sudo apt install rlwrap
あとはシェルで rlwrap ocaml を実行するだけです。簡単なプログラムであれば、これだけでもかなり便利になります。
バッチコンパイラはプログラムをコンパイルして実行可能ファイル (executable file) を生成します。バッチコンパイラは、プログラムをバイトコードにコンパイルする ocamlc と、ネイティブコード (機械語) にコンパイルする ocamlopt の二種類があります。バイトコードにコンパイルされたファイルは、バイトコードインタプリタによって実行されますが、ネイディブコードにコンパイルされたファイルは単独で実行することができます。
●簡単なベンチマーク
OCaml はネイティブコードにコンパイルすると、当然ですがプログラムを高速に実行することができます。また、バイトコードにコンパイルする場合でも、スクリプト言語やバイトコードにコンパイルする Lisp 処理系よりも速いようです。そこで、たらいまわし関数を使って実行速度を比較してみました。
リスト : たらいまわし関数
let rec tak x y z =
if x <= y then z
else
tak (tak (x - 1) y z) (tak (y - 1) z x) (tak (z - 1) x y)
(* 時間計測 *)
let () =
let a = Sys.time () in
ignore (tak 22 11 0);
print_float (Sys.time () -. a)
(* ... *) はコメントを表します。OCaml の場合、コメントは入れ子になってもかまいません。ファイル名を tak.ml とすると、コンパイルは次のように行います。
$ ocamlc -o tak tak.ml
$ ./tak
これで実行ファイル tak が作成されます。あとはそのまま実行するだけです。自動的にバイトコードインタプリタが呼び出されてプログラムが実行されます。ocamlopt の場合も同様にコンパイルすることができます。
それでは実行結果を示します。tak 22 11 0 を計算しました。
表 : tak 22 11 0 の結果
処理系 | 秒 |
Python (ver 3.10.12) | 62.23 |
Ruby (ver 3.0.2p107) | 25.59 |
Lua (ver 5.4.4) | 22.41 |
Gauche (ver 0.9.15) | 21.29 |
ocamlc (ver 4.13.1) | 9.46 |
SBCL (ver 2.1.11) | 4.90 |
Julia (ver 1.10.5) | 1.84 |
SBCL (最適化) | 1.50 |
GCC -O2 (ver 11.4.0) | 1.19 |
ocamlopt (ver 4.13.1) | 1.05 |
- 実行環境 : Ubunts 22.04 (WSL2), Intel Core i5-6200U 2.30GHz
- 改訂 2024 年 10 月 25 日
ocamlopt が gcc よりも速いとは驚きました。gcc のコンパイルオプションは -O2 を指定しただけなので、他のオプションを指定するともう少し速くなるかもしれません。興味のある方は試してみてください。
バイトコードにコンパイルする場合でも、OCaml は他の処理系より高速です。たらいまわし関数のように、再帰呼び出しの回数が多いテストは関数型言語に有利だったかもしれません。そうだとしても、OCaml のコンパイラは優秀だと思います。
●Enjoy Programming!!
M.Hiroi は OCaml でプログラミングするのは初めてです。このページで簡単なプログラムを作りながら OCaml を勉強していきたいと思っております。たいしたことはできませんが、よろしくお付き合いくださいませ。
初版 2008 年 6 月 7 日
改訂 2024 年 10 月 25 日
Yet Another OCaml Problems
参考文献, URL
●参考文献
- 五十嵐淳, 『プログラミング in OCaml』, 技術評論社, 2007
●参考 URL
- OCaml - OCaml, (本家)
- OCamlチュートリアル - OCaml, (本家, 日本語訳)
- Objective Caml 入門 (PDF), (五十嵐淳さん)
- 数理科学的バグ撲滅方法論のすすめ | 日経クロステック, (住井英二郎さん)
- OCaml.jp, "Objective Caml に関する情報を集めたコミュニティサイト" とのことです。
権利・免責事項など
『お気楽 OCaml プログラミング入門』の著作権は筆者「広井誠 (Makoto Hiroi)」が保持します。無断使用や無断転載は禁止いたします。お気楽 OCaml プログラミング入門で作成したプログラムはフリーソフトウェアとします。ご自由にお使いください。プログラムの改造や配布もご自由にどうぞ。その際は、出典を明記してくださるようお願いいたします。
ただし、これらのプログラムは無保証であり、使用したことにより生じた損害について、作者「広井誠 (Makoto Hiroi)」は一切の責任を負いません。また、これらのプログラムを販売することで利益を得るといった商行為は禁止いたします。
Copyright (C) 2008-2024 Makoto Hiroi
All rights reserved.