ML の基礎
ここでは,「非手続き型言語」(2018年度1学期)のために,ML の基礎を説明する.教科書にはMLについては載っていないので,必要な人はこのページを見て予習復習して欲しい.
演習では Standard ML of New Jersey (SML/NJ) を使用する.
目次
- 1. ML の 起動/終了
- 2. 基本事項
- 3. 基本データ型
- 4. 基本関数
- 4.1 整数 (int)
- 4.2 実数 (real)
- 4.3 ブール値 (bool)
- 4.4 文字列 (string)
- 4.5 文字 (char)
- 5. 組とリスト
- 5.1 組
- 5.2 リスト
- 6. 関数定義
- 7. 関数定義におけるパターン
- 8. let
- 9. 例外処理
- 10. 参考文献
- 起動
- sml
- 終了
- ctrl-d
注) sml と打って「コマンドが見つかりません」というメッセージが出る場合は,sml へのパスが通っていない.その場合は,/usr/share/smlnj/bin/sml と打つか,もしくは /usr/share/smlnj/bin をパスに入れること.
- 「評価する」
- ML は関数型言語である.ML のプログラムは「式」であり,その「式」の値を計算することを「式」を「評価する」と言う.ML は「式」を「評価する」ことでプログラムが実行される.
- ;
- ; が式の最後を示す.; が来たらそこまでの式を評価する.
- use "filename";
- エディタなどで作成したファイル filename を読み込む.
- val <変数名> = <値> で変数を宣言できる.ML のプロンプトが出ている状態で行えば,その処理系の実行を終えるまでどこでも使える変数を宣言できる.let 内で用いれば局所変数を定義することになる.
- 関数
- ML では基本的に 関数の名前が先に来て,その後そのパラメータ(または引数とも言う)が来る.例えば size "abc"; のように.しかし,中には 3 + 5; のように二つのパラメータの間に関数記号がくるものもある.これは中間演算子と呼ばれる.算術記号のように習慣的に中間演算子であるほうがみやすいものは中間演算子になるように定義されている.ある関数が中間演算子になっているかどうかは例を見て判断してもらいたい.ちなみに自分で作った関数を中間演算子として指定することもできる.ただし,中間演算子になれるのはパラメータを二つ取る関数だけである.
- op
- op は中間演算子を関数名とするもの.例えば + は中間演算子なので,通常は
- - 5 + 3;
- のように使うが,op を使うと
- - op + (5, 3);
- のように引数を演算子より後ろに書くことができる.関数の動作としては変わらない.
- 関数の型の表示方法
- 例えば val substring : string * int * int -> string とは,関数substring はパラメータを三つ取り,それぞれ string 型,int 型,int 型であり,関数 substring の値は string 型であることを示す.
- このウェブページでの例の見方
- 例:と書いてある以降で - で始まるのが人が入力する行(- はシステムのプロンプトを示す).- がない行がシステムからの出力.
- 整数 (int)
- 0, 1234, 111111 など一つ以上の数からなる文字列
- 負の整数は単項の負記号 ~ (チルダ)で表す.例えば,~1
- 実数 (real)
- 0.0, 1.1 など .(ピリオド)を含む数からなる文字列
- 負の整数は単項の負記号 ~ (チルダ)で表す.例えば,~1.0
- 注:0 は int, 0.0 は real.
- ブール値 (bool)
- true または false
- 文字列 (string)
- "foo" や "R2D2" のように引用符で囲まれた文字の並び
- 特殊文字列はC言語と同様に表現
- 文字 (char)
- #"c" のように書くと char になる
- 注:"c" は string, #"c" が char
- val ~ : int -> int
- マイナス演算子.val a = 1; のとき, ~a; は -1. val a = ~1; のとき ~a は 1.
- val + : int * int -> int
- 加算
- 例:- 13 + 5;
- val it = 18 : int
- val - : int * int -> int
- 減算
- 例:- 5 - 12;
- val it = ~7 : int
- val * : int * int -> int
- 乗算
- 例:- 3 * 5;
- val it = 15 : int
- val div : int * int -> int
- 除算
- 例:- 6 div 2;
- val it = 3 : int
- val mod : int * int -> int
- 余りを求める演算
- 例:- 5 mod 2;
- val it = 1 : int
- val > : int * int -> bool
- 比較演算
- 例:- 12 > 4;
- val it = true : bool
- val >= : int * int -> bool
- 比較演算
- 例:- 4 >= 4;
- val it = true : bool
- val < : int * int -> bool
- 比較演算
- 例:- 12 < 4;
- val it = false : bool
- val <= : int * int -> bool
- 比較演算
- 例:- 4 <= 4;
- val it = true : bool
- val = : ''a * ''a -> bool (多相型.int でも使える)
- 比較演算
- 例:- 4 = 4;
- val it = true : bool
- val <> : ''a * ''a -> bool (多相型.int でも使える)
- 比較演算
- 例:- 4 <> 4;
- val it = false : bool
- val abs : int -> int
- 絶対値演算
- 例:- abs ~1;
- val it = 1 : int
- exception Overflow
- 処理系で処理できる値を越える数を含むような計算式を評価する時に発生する例外.
- exception Div
- 0 による除算を試みた時に発生する例外.
- val ~ : real -> real
- マイナス演算子.val a = 1.0; のとき, ~a; は -1.0. val a = ~1.0; のとき ~a は 1.0.
- val + : real * real -> real
- 加算
- 例:- 13.1 + 5.2;
- val it = 18.3 : real
- val - : real * real -> real
- 減算
- 例:- 5.0 - 12.5;
- val it = ~7.5 : real
- val * : real * real -> real
- 乗算
- 例:- 3.0 * 5.0;
- val it = 15.0 : real
- val / : real * rea; -> real
- 除算
- 例:- 6.2 / 2.0;
- val it = 3.1 : int
- val > : real * real -> bool
- 比較演算
- 例:- 12.0 > 4.6;
- val it = true : bool
- val >= : real * real -> bool
- 比較演算
- 例:- 4.0 >= 4.0;
- val it = true : bool
- val < : real * real -> bool
- 比較演算
- 例:- 12.0 < 4.0;
- val it = false : bool
- val <= : real * real -> bool
- 比較演算
- 例:- 4.0 <= 4.0;
- val it = true : bool
- val abs : real -> real
- 絶対値演算
- 例:- abs ~1.0;
- val it = 1.0 : real
- val real : int -> real
- 整数の実数への変換
- 例:- real 1;
- val it = 1.0 : real
- val floor : real -> int
- パラメータ以下の最大の整数
- 例:- floor 12.4;
- val it = 12 : int
- val ceil : real -> int
- パラメータ以上の最小の整数
- 例:- floor 12.4;
- val it = 13 : int
- val round : real -> int
- 四捨五入
- 例:- round 3.5;
- val it = 4 : int
- val trunc : real -> int
- 0方向への丸め
- 例:- trunc 3.6;
- val it = 3 : int
- 例:- trunc ~3.6;
- val it = ~3 : int
- =, <> は実数では使えない.
- 実数の場合 0.0 での割算では inf (無限大) になる.
- 例:- 1.0 / 0.0;
- val it = inf : real
- val not : bool -> bool
- true に対しては false, false に対しては true となる.
- exp1 andalso exp2
- exp1 と exp2 の論理積 (exp1, exp2 ともに bool 型)
- exp1 が false のとき,exp2 については評価しない.
- exp1 orelse exp2
- exp1 と exp2 の論理和 (exp1, exp2 ともに bool 型)
- exp1 が true のとき,exp2 については評価しない.
- if exp1 then exp2 else exp3
- exp1 が true のとき,この式の値は exp2 となり,exp1 が false のとき,この式の値は exp3 となる.
- exp1 は bool, exp2 と exp3 に関しては同じ型であれば何でもよい.
- val = : ''a * ''a -> bool (多相型.bool でも使える)
- 比較演算
- 例:- true = true;
- val it = true : bool
- val <> : ''a * ''a -> bool (多相型.bool でも使える)
- 比較演算
- 例:- true <> true;
- val it = false : bool
- val size : string -> int
- 文字列の長さ
- val substring : string * int * int -> string
- 部分文字列の取りだし
- substring(s, n1, n2) で文字列 s の n1 番目(最初は 0 番目)からn2 個分の文字を切り出す.
- 例:- substring("abcdefg", 3, 2);
- val it = "de" : string
- val explode : string -> char list
- 文字リストへの変換
- 例:- explode "abcde";
- val it = [#"a",#"b",#"c",#"d",#"e"] : char list
- val implode : char list -> string
- 文字列に変換(explode の逆操作)
- 例:- implode [#"a", #"b", #"c"];
- val it = "abc" : string
- val concat : string list -> string
- 文字列リストの連結
- 例:- concat ["abc", "def"];
- val it = "abcdef" : string
- val <= : string * string -> bool
- 比較演算(辞書式順)
- 例:- "abc" <= "abcd";
- val it = true : bool
- val < : string * string -> bool
- 比較演算(辞書式順)
- 例:- "abc" < "abcd";
- val it = true : bool
- val >= : string * string -> bool
- 比較演算(辞書式順)
- 例:- "abc" >= "abcd";
- val it = false : bool
- val > : string * string -> bool
- 比較演算(辞書式順)
- 例:- "abc" > "abcd";
- val it = false : bool
- val = : ''a * ''a -> bool (多相型.string でも使える)
- 比較演算
- 例:- "abc" = "abc";
- val it = true : bool
- val <> : ''a * ''a -> bool (多相型.string でも使える)
- 比較演算
- 例:- "abc" <> "abc";
- val it = false : bool
- val ^ : string * string -> string
- 文字列の連結
- 例:- "abc" ^ "def";
- val it = "abcdef" : string
- val print : string -> unit;
- 文字列の印字
- 例:- print "abc\n";
- abc
- val it = () : unit
- exception Substring
- 範囲外の添字を指定した時の例外
- val chr : int -> chr
- 拡張 ASCII コードを文字に変換
- 例:- chr 97;
- val it = #"a" : char
- val ord : char -> int
- 文字を拡張ASCII コードに変換
- 例:- ord #"a";
- val it = 97 : int
- val str : char -> string
- 一文字の文字列に変換
- 例:- str #"c";
- val it = "c" : string
- val <= : char * char -> bool
- 比較演算(ASCII コードでの数の大きさ順)
- 例:- #"a" <= #"c";
- val it = true : bool
- val < : char * char -> bool
- 比較演算(ASCII コードでの数の大きさ順)
- 例:- #"a" < #"c";
- val it = true : bool
- val >= : char * char -> bool
- 比較演算(ASCII コードでの数の大きさ順)
- 例:- #"a" >= #"c";
- val it = false : bool
- val > : char * char -> bool
- 比較演算(ASCII コードでの数の大きさ順)
- 例:- #"a" > #"c";
- val it = false : bool
- val = : ''a * ''a -> bool (多相型.char でも使える)
- 比較演算
- 例:- #"a" = #"a";
- val it = true : bool
- val <> : ''a * ''a -> bool (多相型.char でも使える)
- 比較演算
- 例:- #"a" <> #"a";
- val it = false : bool
- exception Chr
- 文字コードの範囲外
- 組
- 任意の型の2個以上の式のリストを取り,それらをコンマで区切って丸括弧でくくったもの.
- 例:- val t = (3, "abc", true);
- val t = (3,"abc",true) : int * string * bool
- 組のアクセス
- 例:- val t = (3, "abc", true);
- val t = (3,"abc",true) : int * string * bool
- - #1(t);
- val it = 3 : int
- - #3(t);
- val it = true : bool
- リスト
- リストは唯一の型からなる要素の並びである.要素の並びをコンマで分けて角括弧で囲む.違う型のものを要素とすることはできないことに注意.
- 例:- [1,2,3];
- val it = [1,2,3] : int list
- 空リスト
- 空リストは nil または [] で表す.
- コンス演算子
- リストを構成する演算子で,最初の要素と2番目以降のリストをパラメータに取る.コロンの対(::)で表される.
- 例:- 1::[2,3];
- val it = [1,2,3] : int list
- 連結演算子
- 二つのリストを連結する.@ で表す.
- 例:- [1,2,3]@[4,5,6];
- val it = [1,2,3,4,5,6] : int list
- コンス演算子は要素とリストをつなぐもの,連結演算子はリストとリストをつなぐものである.
- コンス演算子と連結演算子は普通の演算子とは違って右結合である.すなわち,1::2::[3,4] は 1::(2::[3,4]) の意味である.
- val null : 'a list -> bool
- リストが空リストであれば true そうでないならば false
- val hd : 'a list -> 'a
- リストの頭部(head),すなわち最初の要素
- 例:- hd [2,4,6];
- val it = 2 : int
- val tl : 'a list -> 'a list
- リストの尾部,すなわち第二要素以下
- 例:- tl [2,4,6];
- val it = [4,6] : int list
- val rev : 'a list -> 'a list
- リストを逆順にする.
- 例:- rev [2,4,6];
- val it = [6,4,2] : int list
- val length : 'a list -> int
- リストの長さ
- 例:- length [2,4,6];
- val it = 3 : int
- val map : ('a -> 'b) -> 'a list -> 'b list
- 与えられた関数をリストの各要素に適用しその結果のリストを返す高階関数
- 例:- map (fn x => (x,x)) [1,2,3,4];
- val it = [(1,1),(2,2),(3,3),(4,4)] : (int * int) list
- 注:fn については関数定義のところで説明する.
- exception Empty
- 空リストに対して空リストでは行なえない演算を行おうとした時に発生する例外.
- 関数の定義方法その1
- fun <識別子> (<パラメータ・リスト)> = <式>;
- 関数は一つのパラメータしか取らない.複数のパラメータを取る場合はパラメータを一つの組とする.
- 例:三つ引数の中から最大値を計算する関数
- - fun max3(a:int, b, c) =
- if a > b then
- if a > c then a
- else c
- else
- if b > c then b
- else c;
- val max3 = fn : int * int * int -> int
- 関数の定義方法その2
- fn (<パラメータ・リスト)> => <式>
- この定義では関数の名前を与えない.関数の値そのものを表す式である.
- 関数は一つのパラメータしか取らない.複数のパラメータを取る場合はパラメータを一つの組とする.
- 例:- fn x => x + 1;
- val it = fn : int -> int
- - (fn x => x + 1) 3 * 10;
- val it = 40 : int
- これは (fn x => x + 1) に 3 というパラメータを与え,その結果 4 に 10 をかけた.
- 関数のパラメータに複数のパターンを指定でき,パターンごとに違った処理をすることができる.
- fun <識別子> (<最初のパターン>) = <最初の式>
- | <識別子> (<2番目のパターン>) = <2番目の式>
- ...
- | <識別子> (<最後のパターン>) = <最後の式>;
- 定義におけるすべての識別子は同じでなければならない.
- 等号の右側の式によって生ずるすべての値は同じ型でなければならない.
- すべてのパターンの型は同じでなければならない.
- ML は引数に一致するパターンが見つかるまで最初から順番に試していく.一致するパターンがみつかった時にその式を評価する.
- 同じパターンの中に同じ変数が2度現れてはいけない.
- よく使われる例は x::xs である.
- 例:リストを逆順にする関数
- - fun reverse (nil) = nil
- | reverse (x::xs) = reverse(xs) @ [x];
- val reverse = fn : 'a list -> 'a list
- 関数を定義した時の警告メッセージ Warning: match nonexhaustive はパターンがすべての場合を尽くしていない時に表示される.この場合も関数の定義はされているが,定義されていないパターンを引数としてこの関数を実行した時は,uncaught exception nonexhaustive match failure のエラーがでる.
- let を用いて関数内部に局所変数や局所関数を定義できる.局所変数といっても,C 言語のような手続き型言語で用いる局所変数とは若干異なるので注意のこと.
- let 式の文法
- let
- var または fun 宣言の列
- in
- 式
- end
- 例:階乗を求める関数
- - fun factorial (n) =
- let
- fun fact (0,a) = a
- | fact (n,a) = fact (n-1, n*a)
- in
- fact(n,1)
- end;
- val factorial = fn : int -> int
- - factorial (5);
- val it = 120 : int
- 正しく定義されている関数でも引数によっては値を生じないことがある.典型的な例は 0 による除算である.このような場合 ML 処理系は「例外」を発生させて処理を停止する.
- 例:0 除算の時の例外のメッセージ
- - 5 div 0;
- uncaught exception Div
- ユーザ定義の例外
- 組み込みの例外以外にユーザが例外を定義することもできる.
- 例外の定義.以下のようにするとその後例外 Foo が使えるようになる.
- - exception Foo;
- exception Foo
- 定義した例外の使い方の例.以下は引数のリストが空の時例外 Foo を発生させ,それ以外ではそのリストそのものを返す関数である.
- - fun toyprogram(nil) = raise Foo
- | fun toyprogram(L) = L;
- 実行すると
- - toyprogram([1,2,3]);
- val it = [1,2,3] : int list
- - toyprogram(nil);
- uncaught exception Foo
- Jeffrey D. Ulman 著,神林靖訳「プログラミング言語 ML」アスキー出版局,1996, ISBN 4-7561-1641-8
- Jeffrey D. Ulman, "Elements of ML programming ML97 edition",
Prentice Hall, 1998, ISBN 0-13-790387-1
- 大堀淳著「プログラミング言語 Standard ML 入門」共立出版株式会社,2001, ISBN 4-320-12024-8