ML 演習 第 3 回 新井淳也、中村宇佑、前田俊行 2011/04/26.

Slides:



Advertisements
Similar presentations
C 言語講座第 5 回 構造体. 構造体とは ... 異なる型の値をまとめて新しい型とする 機能がある . つまり , 複数の変数を 1 つのまとまりにできる . 配列と違って同じ型でデータをまとめるのではな く違った型のデータをまとめられる .
Advertisements

プログラミング言語論 第10回(演習) 情報工学科 木村昌臣   篠埜 功.
情報・知能工学系 山本一公 プログラミング演習Ⅱ 第3回 配列(1) 情報・知能工学系 山本一公
(Rubyistのための) 超音速:ML入門
東京工科大学 コンピュータサイエンス学部 亀田弘之
ML 演習 第 1 回 佐藤 春旗, 山下 諒蔵, 前田 俊行 May 30, 2006.
ML 演習 第 8 回 2007/07/17 飯塚 大輔, 後藤 哲志, 前田 俊行
プログラミング言語としてのR 情報知能学科 白井 英俊.
情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
ソフトウェア基礎科学 授業資料: 論理関係(logical relations)のお話
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
ML 演習 第 4 回 新井淳也、中村宇佑、前田俊行 2011/05/10.
システムプログラミング 第5回 情報工学科 篠埜 功 ヒアドキュメント レポート課題 main関数の引数 usageメッセージ
型宣言 (Type Declarations)
シミュレーション物理5 運動方程式の方法: サブルーチンの使い方.
型宣言(Type Declarations)
プログラミング演習II 2004年10月19日(第1回) 理学部数学科・木村巌.
第8回 プログラミングⅡ 第8回
2006/10/19 山下 諒蔵 佐藤 春旗 前田 俊行 大山 恵弘 佐藤 秀明 住井英二郎
条件式 (Conditional Expressions)
プログラミング演習II 2004年12月 21日(第8回) 理学部数学科・木村巌.
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
2007/1/11 山下 諒蔵 佐藤 春旗 前田 俊行 大山 恵弘 佐藤 秀明 住井 英二郎
ML 演習 第 7 回 新井淳也、中村宇佑、前田俊行 2011/05/31.
関数 関数とスタック.
データ構造と アルゴリズム 第四回 知能情報学部 新田直也.
第6回独習Javaゼミ 第6章 セクション4~6 発表者 直江 宗紀.
ML 演習 第 4 回 末永 幸平, 遠藤 侑介, 大山 恵弘 2005/06/21.
暗黙的に型付けされる構造体の Java言語への導入
関数の定義.
PROGRAMMING IN HASKELL
オープンソフトウェア利用促進事業 第3回OSSモデルカリキュラム導入実証
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
デジタル画像とC言語.
地域情報学 C言語プログラミング 第1回 導入、変数、型変換、printf関数 2016年11月11日
第5章 計算とプログラム 本章で説明すること ・計算の概観と記述法 ・代表的な計算モデル ・プログラムとプログラム言語.
プログラミング 4 木構造とヒープ.
B演習(言語処理系演習)第2回 田浦.
C言語 はじめに 2016年 吉田研究室.
15.cons と種々のデータ構造.
プログラミング演習I 2003年4月30日(第3回) 木村巌.
復習 2次元配列 4列 j = 0 j = 1 j = 2 j = 3 i = 0 i = 1 i = 2 3行
PROGRAMMING IN HASKELL
2006/7/18(通信コース)2006/7/26(情報コース) 住井
精密工学科プログラミング基礎 第7回資料 (11/27実施)
2008/7/16(情報コース)2008/7/22(通信コース) 住井
PROGRAMMING IN HASKELL
~sumii/class/proenb2010/ml5/
コンパイラ 2012年10月11日
情報工学科 3年生対象 専門科目 システムプログラミング 第3回 makeコマンド 動的リンクライブラリ 情報工学科 篠埜 功.
情報工学科 3年生対象 専門科目 システムプログラミング 第3回 makeコマンド 動的リンクライブラリ 情報工学科 篠埜 功.
PROGRAMMING IN HASKELL
精密工学科プログラミング基礎Ⅱ 第2回資料 今回の授業で習得してほしいこと: 配列の使い方 (今回は1次元,次回は2次元をやります.)
プログラミング言語論 第九回 理工学部 情報システム工学科 新田直也.
関数と再帰 教科書13章 電子1(木曜クラス) 2005/06/22(Thu.).
プログラミング演習I 2003年6月11日(第9回) 木村巌.
データ構造と アルゴリズム 第四回 知能情報学部 新田直也.
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
プログラミング言語論 第九回 理工学部 情報システム工学科 新田直也.
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
情報処理Ⅱ 2005年11月25日(金).
第10回 関数と再帰.
プログラミング演習II 2003年12月10日(第7回) 木村巌.
プログラミング演習II 2004年11月 2日(第3回) 理学部数学科・木村巌.
プログラミング入門2 第5回 配列 変数宣言、初期化について
情報処理Ⅱ 小テスト 2005年2月1日(火).
プログラミング入門2 第3回 条件分岐(2) 繰り返し文 篠埜 功.
計算技術研究会 第5回 C言語勉強会 関数(function)を使う
情報処理Ⅱ 第8回:2003年12月9日(火).
岩村雅一 知能情報工学演習I 第7回(後半第1回) 岩村雅一
Presentation transcript:

ML 演習 第 3 回 新井淳也、中村宇佑、前田俊行 2011/04/26

今回の内容 OCaml のモジュールシステムについて OCaml コンパイラの使い方 Structure Signature Functor 分割コンパイルなど ※今日使うソースは 演習ホームページに置いておきます

The Module System モジュールシステム

大規模ソフトウェアの プログラミングは難しい 人間が記憶できるプログラムの量には 限界があるから 例 1: OCaml 処理系のソースプログラム 全てを記憶している人は (多分) いない 例 2: Linux カーネルのソースプログラム 全てを記憶している人は (多分) いない

ならない ではどうするか? 答: 複数人でプログラミングする 10 人でやれば 1 人あたりの量は 10 分の 1 に 100 人でやれば 100 分の 1 に 1000000 人でやれば 1000000 分の 1 に... ... ならない

複数人でのプログラミング 最悪のシナリオ 似たようなプログラムが大量にできてしまう プログラムの改善・修正が難しくなってしまう 他人の書いたプログラムは読みにくい 自分で書いた方が早い プログラムの改善・修正が難しくなってしまう 似たようなプログラムを全て修正しないといけない 修正が及ぼす影響が予測できない

最悪のシナリオを避けるには? プログラムを「モジュール化」する プログラムを幾つかの部分 (モジュール) に分ける モジュールの「仕様」と「実装」を切り分ける

プログラムをモジュールに分ける 1つの大きな プログラム これだけなら簡単 モジュール モジュール モジュール モジュール モジュール

モジュールの 「仕様」と「実装」を切り分ける モジュールの外からの 使われ方を表すもの 実装 仕様を実現する データ・プログラムなど 仕様 実装 モジュール

「仕様」と「実装」を分けると 何がうれしいか? モジュールの外からの利用が容易になる モジュールの「仕様」だけ見ればよいので 「実装」は基本的には気にしなくてよい モジュールの「実装」の修正が容易になる モジュールの「仕様」さえ守っていればよいので 「仕様」以外の使われ方を気にせず修正できる

OCaml の提供する モジュールシステム Structure Signature Functor モジュールの実装と名前空間を提供する 型や関数の実装を一つの名前空間にまとめてくれる Signature Structure の仕様を定義する Structure の外から使える型や関数を定義する Structure の型や関数の実装 (定義) を隠蔽できる Functor Structure から別の structure を作る関数のようなもの

Structure モジュールの実装を定義する 構文: module 「モジュール名」 = struct 「内容」 end 「内容」の部分に型や関数の定義を書く モジュール名の先頭は大文字

Structure の例: 多重集合 module Multiset = struct type 'a t = 'a list let empty_set = [] let add elem set = elem :: set let rec remove elem = function | [] -> [] | hd :: tl when hd = elem -> tl | hd :: tl -> hd :: (remove elem tl) let rec countsub elem n = function | [] -> n | hd :: tl when hd = elem -> countsub elem (n+1) tl | _ :: tl -> countsub elem n tl let count elem set = countsub elem 0 set end example4-1.ml

Structure の使い方 Structure 内部の変数や型を使うには ドット表記を使う 構文: (* 「モジュール名」 . 「変数名 or 型名」 *) # let e = Multiset.empty_set;; val e : 'a list = [] # let s = Multiset.add 5 e;; val s : int list = [5] # Multiset.count 5 s;; - : int = 1

モジュール名の省略 Structure を open することで モジュール名を省略できる (* open 「モジュール名」 *) # open Multiset;; # let s = add 5 empty_set;; val s : int list = [5] # let s = add 5 s;; val s : int list = [5; 5] # count 5 s;; - : int = 2

OCaml の組込みのモジュール 他にもいろいろある 詳しくはマニュアルの Part IV を参照 # List.length ["a"; "b"; "c"];; - : int = 3 # String.sub "1234567" 2 3;; - : string = "345" # Printf.printf "%d %s\n" 123 "abc";; 123 abc - : unit = () 他にもいろいろある 詳しくはマニュアルの Part IV を参照

Signature モジュールのインタフェースを与える 構文: Signature に書いた型や関数だけが モジュールの外から利用できる module type 「シグネチャ名」 = sig 「内容」 end 「内容」の部分に型の宣言や関数の型を書く シグネチャ名の先頭は (慣習的に) 大文字

Signature の例: 集合 module type MULTISET = sig type 'a t val empty_set : 'a t val add : 'a -> 'a t -> 'a t val remove : 'a -> 'a t -> 'a t val count : 'a -> 'a t -> int end example4-1.ml

Signature の適用 Signature を structure にあてはめる 実体は元のモジュールと同じ 構文 module 「モジュール名」 : 「シグネチャ」 = 「モジュール」 または module 「モジュール名」 = (「モジュール」 : 「シグネチャ」) 実体は元のモジュールと同じ ただしモジュール外からは signature で示された 型や関数しか使えない

集合の実体が list であることが 外部からは分からない Signature の適用の例 # module AbstMultiset : MULTISET = Multiset;; module AbstMultiset : MULTISET # AbstMultiset.empty_set;; - : 'a AbstMultiset.t = <abstr> # AbstMultiset.add 0 AbstMultiset.empty_set;; - : int AbstMultiset.t = <abstr> 集合の実体が list であることが 外部からは分からない

countsub は MULTISET にはないので 外からはアクセスできない Signature の適用の例 (続き) # AbstMultiset.countsub;; Unbound value AbstMultiset.countsub # AbstMultiset.add 0 Multiset.empty_set;; This expression has type 'a list but is here used with type int AbstMultiset.t countsub は MULTISET にはないので 外からはアクセスできない 実体は同じでも違う型と見なされる

補足: Signature の適用 「シグネチャ」や「モジュール」の部分に 直接シグネチャやモジュールの定義を 書くこともできる 構文 module 「モジュール名」 : 「シグネチャ」 = 「モジュール」 または module 「モジュール名」 = (「モジュール」 : 「シグネチャ」) 「シグネチャ」や「モジュール」の部分に 直接シグネチャやモジュールの定義を 書くこともできる module Foo : sig ... end = struct ... end

Functor モジュールを受け取ってモジュールを返す 関数のようなもの Functor を作る構文:

Functor の例: 多重集合再び example4-2.ml type comparison = Less | Equal | Greater module type ORDERED_TYPE = sig type t val compare : t -> t -> comparison end module Multiset2 = functor (Elem : ORDERED_TYPE) -> struct type t = Elem.t list let eq x y = Elem.compare x y = Equal let rec remove elem = function | [] -> [] | hd :: tl when eq hd elem -> tl | hd :: tl -> hd :: (remove elem tl) … (* その他 *) … example4-2.ml

Functor からモジュールを作るには Functor にモジュールを渡すことで functor が定義しているモジュールが得られる 例: 構文: 「functor」 (「モジュール」 ) 例: module StringMultiset = Multiset2(OrderedString) 括弧は必須 example4-2.ml

Functor に対する signature Signature の functor を作る構文: functor (「仮引数」 : 「シグネチャ」) -> 「シグネチャ」 Signature の functor の定義の例: module type MULTISET2 = functor (Elem : ORDERED_TYPE) -> sig type t val empty_set : t val add : Elem.t -> t -> t val remove : Elem.t -> t -> t val count : Elem.t -> t -> int end example4-2.ml

Recursive Module 相互再帰的なモジュールも定義できる 例: 構文: module rec 「モジュール名1」 : 「signature1」 = 「struct1」 and 「モジュール名2」 : 「signature2」 = 「struct2」 … 例: # module rec Even : sig val f : int -> bool end = struct let f n = if n = 0 then true else Odd.f (n - 1) end and Odd : sig val f : int -> bool end = let f n = if n = 0 then false else Even.f (n - 1) module rec Even : sig val f : int -> bool end and Odd : sig val f : int -> bool end # Even.f 156;; - : bool = true

How to Use Compilers コンパイラの使い方

OCaml のコンパイラ 実行可能形式ファイルを生成してくれる 二種類のバックエンドがある ocamlc: バイトコードコンパイラ バイトコードを生成 バイトコードインタプリタ (ocamlrun) 上で実行される ocamlopt: ネイティブコードコンパイラ x86 や SPARC などの機械語を生成 モジュール単位での 分割コンパイルをサポートしている

OCaml のコンパイラが 扱うファイルの種類 ソースファイル .ml モジュールの実装 .mli モジュールのシグネチャ オブジェクトファイル .cmo 実装のバイトコード .cmi インタフェースのバイトコード .o 実装のネイティブコード .cmx 実装のネイティブコードの付加情報 .a, .cma, .cmxa ライブラリ

モジュールと分割コンパイルの関係 モジュールの signature と structure を 別々のファイルとして分割コンパイルできる ファイル : someModule.mli module SomeModule : sig end = struct (someModule.mli の中身) ファイル名の 先頭は 小文字にする ファイル名の 先頭は 小文字にする (someModule.ml の中身) ファイル : someModule.ml

モジュールの分割コンパイル .mli ファイルをコンパイル  .cmi が生成される .ml ファイルを ocamlc でコンパイル  .cmo が生成される .mli があれば .cmi を用いて型チェックしてくれる .ml ファイルを ocamlopt でコンパイル  .cmx と .o が生成される

.mli, .ml によるモジュールの例 strSet.ml, strSet.mli sort.ml

分割コンパイルの例 $ ocamlc -c strSet.mli $ ocamlc -c strSet.ml $ ocamlc -c sort.ml $ ls -F *.cm* sort.cmi sort.cmo strSet.cmi strSet.cmo $ ocamlc -o sort strSet.cmo sort.cmo $ ls -F sort sort* 順序が重要: sort.ml の中で StrSet を使っているので sort.cmo を strSet.cmo より後に書く必要がある

sort の実行例 $ ./sort <<END > bbb > ccc > aaa > END aaa bbb ccc

.cmo をインタプリタで利用する #load でバイトコードファイルを読み込み可能 # #load "strSet.cmo";; # StrSet.empty_set;; - : StrSet.t = <abstr> # StrSet.countsub;; Unbound value StrSet.countsub # open StrSet;; # add "abc" empty_set;;

OCamlMakefile を使う Makefile 中で OCamlMakefile を使うと OCaml プログラムの分割コンパイルが簡単になる Makefile:  プログラムなどの生成手順を記述したファイル OCamlMakefile の入手方法 パッケージ $ sudo apt-get install ocamlmakefile 直接ダウンロード http://www.ocaml.info/home/ocaml_sources.html#OCamlMakefile 詳しい使い方は同梱の README.txt を参照

Makefile の書き方の例 Makefile SOURCES = strSet.mli strSet.ml sort.ml RESULT = sort include OCamlMakefile ソースファイルを 羅列する Makefile ファイルの順番に注意 出力ファイルの 名前を指定する

パッケージで導入した場合はこの場所にある ビルド (make) の実行例 パッケージで導入した場合はこの場所にある このコピーは一回だけで十分 (ビルド毎にコピーする必要はない) $ cp /usr/share/ocamlmakefile/OCamlMakefile ./ $ ls Makefile OCamlMakefile sort.ml strSet.ml strSet.mli $ make make[1]: ディレクトリ `/tmp/sort' に入ります ocamldep strSet.mli > ._bcdi/strSet.di ( … 省略 … ) Makefile sort sort.cmo strSet.cmi strSet.ml OCamlMakefile sort.cmi sort.ml strSet.cmo strSet.mli

第 3 回 課題 締切: 5/10 13:00 (日本標準時)

課題 1 (10 点) sort の例を自分で試してみよ ※ 今後課題で OCamlMakefile を用いても構わない 例に従って実行ファイル生成し、実行してみよ .cmo ファイルをインタプリタで利用してみよ .mli をコンパイルしないとどうなるか試してみよ 最後のリンク時にモジュールの順番を変えると どうなるか試してみよ OCamlMakefile を用いてみよ その他いろいろ試してみよ ※ 今後課題で OCamlMakefile を用いても構わない

課題 2 (5 点) 前回 (第 2 回) の課題 2 で作ったスタックを モジュール化せよ シグネチャも与えて 内部の実装を適切に抽象化すること

課題 3 (5 点) 前回 (第 2 回) の課題 4 (または課題 9) で作ったキューをモジュール化せよ シグネチャも与えて 内部の実装を適切に抽象化すること

課題 4 (15 点) リスト以外のデータ構造を使って signature MULTISET2 に対する 別の実装を与えよ ただし、add, remove は平均時間計算量 O(log n) となるようにすること

課題 5 (20 点) 課題 4 での別の実装が 元の実装と「同じ」であることを証明せよ 「同じ」の定義は自分で与えること

課題 6 (15 点) ORDERED_TYPE で表現される型の key と 任意の型の値についての連想配列 (マップ) を作る functor を作成せよ シグネチャも与えて内部の実装を適切に抽象化すること 必要ならば組込みの例外 Not_found を用いること 標準ライブラリの Map モジュールを用いないこと

課題 6 (例 1) # module NCStringMap = MyMap(NoCaseString);; sig type key = NoCaseString.t type 'a t = 'a MyMap(NoCaseString).t val empty : 'a t val add : key -> 'a -> 'a t -> 'a t val remove : key -> 'a t -> 'a t val get : key -> 'a t -> 'a end

課題 6 (例 2) # open NCStringMap;; # let sa = add "C" "/* */" empty;; val sa : string NCStringMap.t = <abstr> # let sa = add "OCaml" "(* *)" sa;; # let sa = add "Perl" "#" sa;; # get "ocaml" sa;; - : string = "(* *)" # get "ruby" sa;; Exception: Not_found.

課題 7 (15 点) とある木の型を以下のように定義する type 'a t = | Leaf | Node of 'a * 'a t t このとき、与えられた関数を木のノードの各要素に適用した木を返す関数 map を定義せよ map : ('a -> 'b) -> 'a t -> 'b t

課題 8 (15 点) 以下のような signature を持つ module EQ を定義せよ ただし、各関数は呼び出されれば必ず停止し 例外が発生しないようにすること module EQ : sig type ('a, 'b) equal val refl : ('a, 'a) equal val symm : ('a, 'b) equal -> ('b, 'a) equal val trans : ('a, 'b) equal -> ('b, 'c) equal -> ('a, 'c) equal val apply : ('a, 'b) equal -> 'a -> 'b module Lift : functor (F : sig type 'a t end) -> sig val f : ('a, 'b) equal -> ('a F.t, 'b F.t) equal end

課題 9 (15 点) 前回 (第2回) の課題6の値と式の定義を 課題8の結果を用いて以下のように定義したとする: type 'a value = | Bool of (bool, 'a) EQ.equal * bool | Int of (int, 'a) EQ.equal * int;; type 'a expr = | Const of 'a value | Add of (int, 'a) EQ.equal * (int expr) * (int expr);; このとき前回の課題7と同様に 式を評価して値を返す関数 eval を定義せよ val eval : 'a expr -> 'a value