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. 参考文献

1. ML の 起動/終了

起動
sml
終了
ctrl-d
注) sml と打って「コマンドが見つかりません」というメッセージが出る場合は,sml へのパスが通っていない.その場合は,/usr/share/smlnj/bin/sml と打つか,もしくは /usr/share/smlnj/bin をパスに入れること.

2. 基本事項

「評価する」
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 型であることを示す.
このウェブページでの例の見方
例:と書いてある以降で - で始まるのが人が入力する行(- はシステムのプロンプトを示す).- がない行がシステムからの出力.

3. 基本データ型

整数 (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

4. 基本関数

4.1 整数 (int)

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 による除算を試みた時に発生する例外.

4.2 実数 (real)

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

4.3 ブール値 (bool)

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

4.4 文字列 (string)

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
範囲外の添字を指定した時の例外

4.5 文字 (char)

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
文字コードの範囲外

5. 組とリスト

5.1 組

任意の型の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

5.2 リスト

リスト
リストは唯一の型からなる要素の並びである.要素の並びをコンマで分けて角括弧で囲む.違う型のものを要素とすることはできないことに注意.
例:- [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
空リストに対して空リストでは行なえない演算を行おうとした時に発生する例外.

6. 関数定義

関数の定義方法その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 をかけた.

7. 関数定義におけるパターン

関数のパラメータに複数のパターンを指定でき,パターンごとに違った処理をすることができる.
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 のエラーがでる.

8. let

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

9. 例外処理

正しく定義されている関数でも引数によっては値を生じないことがある.典型的な例は 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

10. 参考文献

  1. Jeffrey D. Ulman 著,神林靖訳「プログラミング言語 ML」アスキー出版局,1996, ISBN 4-7561-1641-8
  2. Jeffrey D. Ulman, "Elements of ML programming ML97 edition", Prentice Hall, 1998, ISBN 0-13-790387-1
  3. 大堀淳著「プログラミング言語 Standard ML 入門」共立出版株式会社,2001, ISBN 4-320-12024-8

SASAKURA Mariko sasakura@momo.cs.okayama-u.ac.jp