To Home
Last modified: Tue Jun 9 08:06:36 JST 2009

Ocaml

3.09.3 がでています。マニュアルが派手だなぁ。(どういう印象だ)
recursive module ってはいってたのかぁぁぁ。Caml Weekly Newsでの記事
ocamlc -dtypes でコンパイルして、Emacsでソースを開いて、 式の上でマウスの真中ボタンをおすと、なんだかうれしくなります。
注: 関数型言語としてのOcamlを学ぶにはまず 五十嵐先生のページなどをみて下さい
手っ取り早くMLを勉強したければこちら超特急、超入門。超、超。。。

目次

Language
  Imperative features (1.5)
    配列(Array)
    再帰以外の繰り返し
  Labels(2.1)
    Optional arguments (2.1.2)
  Classes and objects (3.1)
    Class定義
    Instanceを作成
    メソッドへのアクセス
  Reference to self (3.2)
  Initializers (3.3)
  Inheritance (3.7)
Language extensions (7)
  Local Modules
structureとsignatureとfunctor
Libraries
  The core libraries
    Pervasives Module
      入出力
      文字列処理
  The standard libraries
    Array Module
    Arg Module
    Char Module
    Filename Module
    Genlex Module
    Hashtbl Module
    Lazy Module
    Lexing Module
    List Module
    Printf Module
    Queue Module
    Map Module
    Marshal Module
    Obj Module
    Set Module
    Stream Module
    String Module
    Sys Module
  Str library
    Str Module
  The threads library
    Thread Module
    Mutex Module
  Dbm library
    Dbm Module
  Graphic library
    Graphics Module
  Bigarray library
    Bigarray Module
  Unix library
    Unix Module
  Labltk library
    Tk Module
その他
コンパイル
ocamlc
ocamlmktop
ocamldebug
プロファイリング
逆アセンブル
Cの関数を呼ぶ
Ocamlでscript
lablgtk
lablgtk2
ocamlyacc / ocamllex
Standard MLとの比較
Emacs Lispたち
Ocamlのインストール
趣味の世界
ツール
  ocamlweb
  ocamldoc
リンク
CWNメモ
メモ
他のMLを使った感想
サンプル

Language

Ocamlは関数型言語なのに、imperative (命令的 つまりCの代入のように副作用を生む操作) から説明するなんて...。

Imperative features (1.5)

配列(Array)

  Array module がある。
  配列は [| と |] で囲んで表す。要素間は ; で区切る。添字はzero origin.
 e.g.)
    #let a = [| 1;2;3;4;5 |];;
Array Module

再帰以外の繰り返し

  while [condition] do ... done;;
  for [variable] = [initial_value] to [finish_value] do ... done;;
breakは例外処理でできる。 try ... with ** -> ..

Labels(2.1)

仮引数にラベルを付けることができる。
関数の仮引数の前に ~ を付ける。
インターフェイスと関数内部で異なる変数名を用いることができる。
e.g.)
  #let f  ~x:x1 ~y:y1 = x1 - y1;;
  val f : x:int -> y:int -> int = <fun>

関数適用時に明確にラベルを用いるときは次のように書く。
e.g.)
  #f ~x:3 ~y:2;;
  - : int = 1

Optional arguments(2.1.2)

引数をオプションにできる。
?([variable_name] = [default_value])と書くとオプション化される。
束縛するときは ~ を使う(Labelなので)

  option.ml

Classes and objects(3.1)

Classがある。moduleによく似ているが type..による型定義がない のと、継承、インスタンス化(new ...)があることなど(オブジェクト指向なので、 オブジェクトの状態を規定する)がことなる。
オブジェクト生成時に初期値を与えるときは、classの定義に おいてobjectを返す関数を定義する。
e.g.)
  class tak = fun init ->
    object
      val mutable money = init
      method earn = fun value -> money <- money + value
    end;;

Class定義

  class [class_name] =
    object
      val ... [definition_of_member_variable]
      method ... [definition_of_method]
    end;;
valでメンバー変数を定義、methodでメソッドを定義する。
内部でparametalizeされた型を扱える。
# class ['a] hoge (x : 'a)
 = object
     method print = print_string "new hoge"
     val a = x
     method get_a = a
   end;;
class ['a] hoge :
  'a -> object method get_a : 'a method print : unit val a : 'a end
# let a = new hoge 1;;
val a : int hoge = <obj>
# a#get_a;;
- : int = 1
# let a = fun x -> x#print 1 2;;
val a : < print : int -> int -> 'a; .. > -> 'a = <fun> (* 部分型を伴う型推論 *)

Instanceを作成

  let [variable_name] = new [class_name]

メソッドへのアクセス

  [class name]#[method name]
仮想関数が一つでもあるクラスはclass virtual [classname]のようにvirtualを付けて 定義しなければならない。

class type

moduleにmodule typeがあるように、classにもclass typeがある。

# class a =
  object
  method hello = "tak\n"
  end;;
class a : object method hello : string end
# class type at =
  object
  method hello : string
  end;;
      class type at = object method hello : string end
# class new_a = (a : at);;
class new_a : at
# (new new_a)#hello;;
- : string = "tak\n"

おまけ)

メンバー変数への外からの直接のアクセスはできない。

Reference to self (3.2)

オブジェクトが内部で自分自身を参照するには、 class定義のobjectの後に[variable name]を挿入する。その変数名に自分自身が束縛される。

おまけ)
  自分自身(self)のsかselfを使いたいらしい。

Initializers (3.3)

クラス定義におけるlet文はobject生成前に評価される。
後に評価して欲しいものはInitializerというキーワードの後に記述する。
Initializerの後は順序処理される。

中略)
  多重継承・仮想関数は、暇なときに。

Inheritance (3.7)

class定義の中でinheritに続けて継承するクラスを書く。

Language extensions (7)

Local Modules

Coreのスコープ内でModuleを定義できる。Setなんかをちょっと道具として使う場合に便利。
サンプル local_module.ml
あれ!? 超dependent SetのMakeに引数Stringを渡して適用した時のt型!? escape_abstract.ml
> ocamlopt.opt -i escape_abstract.ml -o escape_abstract
val friends_list : string list
val escape_abstract_data : unit -> Set.Make(String).t

structureとsignatureとfunctor

MLではモジュールシステム自体が型(signature)を持つ。structureは型、値、関数の定義をおく。 signatureは各structureに対する型のようなもので、各メンバの型を指定する。このsignatureに書かれた名前が 外部のモジュールから参照可能な名前である。
functorは structureからstructureへの関数のようなもの。structureの中で(未定義の)型、関数等を抽象化 出来る。functorの定義自体は
struct Foo (Something : Compareble) =  (* SomethingはCompareble signatureを満たす型 *)
  struct
    let compare x y = Something.compare x y
    ...
  end
のようにstructからstructへの関数として書く (引数にはsignatureをつける) か
struct Foo = functor (Something : Compareble)
 -> struct
   let compare x y = Something.compare x y
   ...
 end
のように、core言語のfunのようにfunctorキーワードを使ってfunctorを記述することも できる。このstructureに対するsignatureは
functor (Something : Compareble) ->
  sig
    val compare x y: 'a -> 'a -> int
  end
のようにfuntorキーワードで表現される。

文法

  module [structure name] =
    struct
      [implementation]
    end

  module type [signature_name] =
    sig
      [definition_of_interface]
    end

  module [object_name] =
    ([structure_name] : [signature name])

structure

  structureは、お互いに関連した定義を集めたものである。structureの中に 入れることで、名前の衝突を防ぐことができる。また、引数にstructureを与えて、 structureを返す「関数」のような機能をもつfunctorを定義することもできる。

signature

  signatureは、structureのインターフェイスである。ここに公開するものを 書いておく。公開されないものは外からは見えない。

# module type A = functor (Arg : sig type t end) -> sig type t = Arg.t end;;
module type A = functor (Arg : sig type t end) -> sig type t = Arg.t end
functor typeもあり。

with による signature の制限
例として次のようなコード (ocamlインタプリタ上でのセッション) を考える。
# module StringSet = (Set.Make(String) : Set.S);;
module StringSet : Set.S
# open StringSet;;
# add "mamewo" empty;;
Characters 4-12:
  add "mamewo" empty;;
      ^^^^^^^^
This expression has type string but is here used with type StringSet.elt
Set.S では 型 elt は type elt のように opaque な型として宣言されている。集合の 要素の型を抽象化しているので要素の型を指定できないのは当たり前である。そして、 (Set.Make(String) : Set.S) のように普通に signatureで制限してしまうと、Set.Make(String) の elt 型は opaque な型となり隠蔽される。実際、StringSet.t は string 型であるのだが、 それは外から見えないので add "mamewo" emtpy とすると型エラーが起こっている。
でも、それでは、集合に要素をいれることなんてできない!! こんなときに with による signature の制限が使える。これを使うことで、 signature内で opaque に宣言された型が manifest な型として(指定した型と互換性のある型) 使用できる。
# module StringSet =
  (Set.Make(String) : Set.S with type elt = string);;
module StringSet :
  sig
    type elt = string
    and t
    val empty : t
    val is_empty : t -> bool
    val mem : elt -> t -> bool
(* ... *)
  end
# open StringSet;;
# add "mamewo" empty;;
- : StringSet.t = <abstr>
# elements (add "mamewo" empty);;
- : StringSet.elt list = ["mamewo"]
with によって、外から elt という型を string と等価な型にしてほしいと指定 することができ、無事に string 型の要素を集合に付け加えることができる。
これは .mli ファイルを書くときに便利。たとえば、実装で、 上のような StringSet を定義して、すべての関数を公開する場合 Set.S をそのまま適用 しようと安直に考えると opaque な型になりうまくいかない。しかし、with を 用いて要素の型を指定することによって、 Set.S を有効に利用できる。

Libraries

The core library

Pervasives Module (Chapter 19)

このモジュールは自動的にopenされる。
  output,input,string_of_int,int_of_string,print_newline( ^ ),( + ),( - )...(数値演算)
等が定義されている。
また、
  stdin,stdout,stderr(input-output channels)や、numbers, booleans, string, exception, references, lists, arrays...等の型が定義されている。

入出力
  標準入出力
  標準入出力に割り当てられているout_channel,in_channelの変数名は
    stdin   標準入力
    stdout 標準出力
    stderr 標準エラー出力
  である。

  ファイル入出力
  output: out_channel -> string -> int (position) -> int (length) -> unit
    out_channelにstring上の位置positionから長さlengthだ け出力する。返値は実際に書き込んだ長さ。
  input: in_channel -> string -> int (position) -> int (length) -> int
    input_channelからstring上の位置positionにlength byteだけ 入力する。返値は実際に読み込んだ長さ。0ならばファイルの終りを意味する。 実装に よっては length byte 読み込む前に関数が返る。返り値が実際に読み込んだバイト数 を表す。丁度 length byte 読むには length byteだけ読めるまで input を呼ぶのを繰 り返すか、 really_input を使う。
  really_input: in_channel -> string -> int (position) -> int (length) -> int
    丁度 length byte 読み込む。読み込む前にファイルの終りに達したら End_of_file 例外が投げられる。

  input_all.ml
  input_line : in_channel -> string
一行読み込み。最後の改行文字はなくなる 最後まで読んだ時は End_of_file 例外を 投げる
  print_string : string -> unit
  文字列を標準出力に表示する
  print_newline : unit -> unit
  改行を入れる。
  flush : out_channel -> unit
  バッファをフラッシュする。

文字列処理
  string_of_int : int -> string
  int_of_string : string -> int
    int <-> stringの変換。stringは整数を表現したもの。 16進表示でもよい。
  ^ : string -> string -> string
    中置演算子。文字列をつなげる
  ignore : 'a -> unit
    値を捨てる。逐次実行で返り値がunitで無いものがあるとコンパイラが警告を だす。この警告を避けるためのもの。
  at_exit : (unit -> unit) -> unit = <fun>
  終了時に行なう処理を登録する。複数登録できて、後に登録したものが先に呼び出される。
  サンプルプログラム (at_exit.ml)

The Standard library

Array Module

Array.create int -> 'a -> 'a array
  配列を作る。initial valueの型で配列の型が決定される。
Array.length 'a array -> int
  配列長
Array.set 'a array -> int -> 'a -> unit
  代入
Array.get 'a array -> int -> 'a
  参照
Sys.argv.(1)のように括弧を用いてアクセスすることもできる。
# let a = [| 1;2;3;4;5 |];;
val a : int array = [|1; 2; 3; 4; 5|]
# a.(3);;
- : int = 4
# a.(1) <- 100;;
- : unit = ()
# a;;
- : int array = [|1; 100; 3; 4; 5|]

Arg Module

コマンドライン引数、オプションの処理を行う。Sys.argv.(0)は対象外 (プログラム名) -help でusageを表示するような 機能が埋め込まれている。
Arg.parse (string * Arg.spec * string) list -> (string -> unit) -> string -> unit
  第一引数はオプションの設定を記述しているリスト。要素の一番めはオプション文字列("-"を含む)、 二番めは動作、三番めは -help で表示される説明文字列である。第二引数はオプション以外の (ファイル名などの)コマンド引数を処理する関数、第三引数は -help オプションをつけた 時に表示される説明の一番最初に表示される文字列を指定する ("usage: cmd [options] [files]" のようにコマンドラインの文法とか)
Arg.usage (string * Arg.spec * string) list -> string -> unit
  -helpオプション指定時に表示されるような使用方法の表示をする。第二引数の stringは使用方法の最初の行の文字列


  arg_test.ml
参考 getopt
  pcc.ml
  オプションを複数回指定すると指定した分だけ呼ばれる。perl の Getopt::Std では複数回数同じオプションを指定すると最後のオプションの値で上書きされてしまう (涙)

Char Module

Char.uppercase : char -> char
Char.lowercase : char -> char
  大文字<->小文字の変換
Char.code : char -> int
  文字からASCIIコードに変換 (0 〜 255)
Char.chr : int -> char
  ASCIIコードから文字に変換

Filename Module

パスや拡張子を扱う文字列処理をする。
Filename.check_suffix : string -> string -> bool
第一引数の文字列が第二引数で指定されるsuffixにあうかをチェックする。あえばtrue。 別に "." が 意味をもつわけではなく、第一引数の文字列の末尾が suffix にあうか否かをチェックしている。

  サンプル check_suffix.ml
Filename.chop_extension : string -> string
  ファイル名から拡張子をとった文字列を返す。"." もとる。
使用例
  サンプルプログラム file.ml
  Strを使って模倣してみた fileregexp.ml
  たとえばmlのファイル名から、そのファイルが定義するモジュール名を求める basename.ml (basenameのサンプル)

Genlex Module

お手軽に字句解析を行なうことができる。キーワードを設定できる。
type token = | Kwd of string | Ident of string | Int of int | Float of float | String of string | Char of char
キーワード、識別子、整数、実数、文字列、文字が扱える。文字列は""で括られた もので、文字は''で括られた一文字。(* *) で括られるとコメント!!
Genlex.make_lexer : string list -> char Stream.t -> token Stream.t
字句解析関数を生成する。引数の文字列リストはキーワードを渡す。

# let st = Genlex.make_lexer [] (Stream.of_channel stdin);;
val st : Genlex.token Stream.t = <abstr>
# Stream.next st;;
(* takashi *) 12
- : Genlex.token = Genlex.Int 12

Hashtbl Module

ハッシュ。Hashtbl,Map,Setはfunctorとして定義されている。Hashtblはハッシュ関数と 等価関数(equal)を必要とし、Map,Setは比較関数を必要とする。どれもMakeがfunctorである。 ハッシュ関数は byterun/hash.c の hash_aux関数で具体的に定義されている。オブジェクトのタグを 見て型にふさわしいハッシュ関数をつかってハッシュ値を返す。この関数はocamlからは多相関数 Hashtbl.hashで使える。

値の型を制限する
create時が適当かと思いますが。
サンプル(restrictHashValueType.ml)

サンプル
  hashSample.ml

実行結果
> ocamlopt hashSample.ml -o hashSample
> ./hashSample
okui
wo
Fatal error: exception Not_found
メモ
Is_young,Is_atom,Is_in_heap..? Tag_valはタグとりだし
ハッシュ関数
accu = accu * α + new (α = 19 or 65599)
タグの種類(一部?)
String_tab,Double_tag,Double_array_tag,Abstract_tag,Infox_tag,Object_tag,Custom_tag
builtin_cprimにプリミティブ関数が並ぶ。 hash_univ_paramも登録されている。names_of_builtin_cprimにそれらの関数の名前が登録されている。byterun/dynlink.cで名前と一致する関数を取り出している。(54行目近辺)

Lazy Module

遅延評価のためのモジュール。Lazy.statusはDelayed of (unit -> 'a) (評価されていない) Value of 'a (評価された) Exception of exn の3つ。Lazy.status へのリファレンスをforceで評価する。DelayedはValueに変わり値のメモが取られる。

サンプル(講義を自分で復習してみただけだったりする)
  libSequence.ml (Lazyモジュールを用いたシーケンス)
  useSequence.ml (使い方 素数表とか)

Lexing Module

ocamlyacc, ocamllex を使ってパーザー、レキサーをつくった時に使用できるレキサー。
lexbuf の pos_cnum というフィールドのみ自動的に値が更新され、他は更新されない。 行番号情報を管理したい時は *.mll ファイルに管理するようなコードを記述しなければならない。 改行に出会った時に以下の関数を呼び出すとか。
let newline lexbuf =
  lexbuf.lex_curr_p <-
    { lexbuf.lex_curr_p with pos_lnum = lexbuf.lex_curr_p.pos_lnum + 1 }

let reset_lexer lexbuf =
  lexbuf.lex_curr_p <- { lexbuf.lex_curr_p with pos_lnum = 1 }

let nl = '\013' '\010' | '\010'

rule token = parse 
  nl { newline lexbuf; token lexbuf }
| ...
Lexing.lexeme lexbuf -> string
  正規表現にマッチした文字列を返す
Lexing.lexeme_char lexbuf -> int -> char
  マッチした文字列のi番目の文字を返す
Lexing.lexeme_start lexbuf -> int
  マッチした文字列のストリーム上での開始位置
Lexing.lexeme_end lexbuf -> int
  マッチした文字列のストリーム上での終了位置
  ocamllexで生成されたlexerはlexbufという名前のバッファを持つ。(引数名がlexbuf)

サンプル
  実数の簡単なパース lexfloat.tar.gz

List Module

List.fold_left ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
List.fold_right ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b
  畳み込み。leftは(f an (f an-1 (... (f seed a1)...)))で末尾再帰であり,rightは (f a1 (f a2 (... (f an seed)...)))で、末尾再帰でない。
例)
# List.fold_left (fun x y -> x ^ y) "" ["takashi"; "masuyama"; "mamewo"];;
- : string = "takashimasuyamamamewo"
# List.fold_right (fun x y -> x ^ y) ["takashi"; "masuyama"; "mamewo"] "";;
- : string = "takashimasuyamamamewo" 

List.combine 'a list -> 'b list -> ('a * 'b) list = <fun>
  aの要素とbの要素を順番に対応づけてペアにしたリストを返す。zipとか言ったような言わなかったような。

Printf Module

Printf.printf ('a, out_channel, unit) format -> 'a
  formatはCに似ている
    %s 文字列
    %d 10進数
    %x 16進数
    %o 8進数
    %f 浮動小数点数
formatから、第二引数以降の型が推論されている!!
Printf.sprintf ('a, unit, string) format -> 'a
  Printf.printfのStringバージョン。フォーマットにしたがった文字列を返す。

Queue Module

副作用を伴うキュー。

サンプル
  queue_test.ml

Map Module

全順序がついたキーである値を連想するときに使う。以下 Map.Make の結果の module についての解説。
add : Map.S.key -> 'a -> 'a Map.S.t -> 'a Map.S.t
  map にキーと値を登録する。先に同じキーに対して登録されていたものは失われる。

サンプル
  あとから add したものが先にあったものを上書きする maptest.ml
  小さい MultiMap を作ってみた。ただ値の部分をリストにするだけ。 multimap.ml
  Map同士の結合を定義してみた。確か union じゃなくて何か名前がある。customizedMap.ml

Marshal Module

値を入出力する。入力する値の型は推論できないので、プログラマが指定する。 Pervasives.input_value,Pervasives.output_valueはこのMarshalモジュールを使用して いる。
例)
# let f = open_out "hoge";;
val f : out_channel = <abstr>
# output_value f "hoge";;
- : unit = ()
# output_value f (1,2,3);;
- : unit = ()
# close_out f;;
- : unit = ()
# let f = open_in "hoge";;
val f : in_channel = <abstr>
# (input_value f : string );;
- : string = "hoge"
# (input_value f : int*int*int );;
- : int * int * int = 1, 2, 3     
input_valueの返り値の型を指定してやらなければならない。

  save_hash.ml
  (string, bool) Hashtbl.t 型の値を保存

Obj Module

Not for casual userとマニュアルには書いてあるので、積極的に 使うものではないらしい。値の内部表現に関する演算。
Obj.tag : t -> int
Obj.is_int : t -> bool
Obj.obj : t -> 'a

興味本位な例)
# let my_pen_name = "mamewotoko";;
val my_pen_name : string = "mamewotoko"
# (Obj.tag (Toploop.getvalue "my_pen_name")) == Obj.string_tag ;;
- : bool = true
# (Obj.obj (Toploop.getvalue "my_pen_name"));;
- : '_a = <poly>
# ((Obj.obj (Toploop.getvalue "my_pen_name")):string);;
- : string = "mamewotoko"
# ((Obj.obj (Toploop.getvalue "my_pen_name")):int);;
- : int = 538436204
型のチェックはしなければならない。

Set Module

むっちゃ使える集合ライブラリ。内部的にはバランスした二分木で実装されている。 Set.Make functor でモジュールを作成する。この functor に与えるべきものは、 比較できる型 t とその型に対する比較関数 compare である。 compare については 型多相に定義されているものが Pervasives モジュールにあるのでそれをそのまま 渡してしまってよい。(use_functor_now.ml 参照)
Set.diff: t -> t -> t
  Set.diff s1 s2 は s1 から s1 と s2 の intersection を取り除いた 集合を返す。集合の引き算。s1 になくて、s2 にある要素は結果に関係しない。
# module StringSet = Set.Make(String);;
module StringSet :
  sig
(* ... *)
# let s1 = 
  List.fold_right StringSet.add
    ["tak"; "mamewo"; "masu"] StringSet.empty;;
val s1 : StringSet.t = <abstr>
# let s2 = 
  List.fold_right StringSet.add
    ["tak"; "aki"; "mamewo"; "mango"] StringSet.empty;;
val s2 : StringSet.t = <abstr>
# StringSet.elements (StringSet.diff s1 s2);;
- : StringSet.elt list = ["masu"]
(* s2からs1をひく。s1にあるけど、s2にない "masu" はあってもなくても*)
# StringSet.elements (StringSet.diff s2 s1);;
- : StringSet.elt list = ["aki"; "mango"]
サンプル
  set_diff.ml

サンプル
  touple_set.ml
  use_functor_now.ml functorの引数にモジュール名ではなく struct .. end を書く。

Stream Module

順序を持ったデータの集まりで、先頭から順にアクセスすることを許す。ストリームの 関数は副作用を持つものと、値を返すだけで、副作用を持たないものがあるので、 注意が必要。ストリームは空かも知れないし、値を持つかも知れない。値を持つか、持た ないかはocamlでは'a option型で表現する。Some of 'a (値を持つ) | None (値を持たない)
Stream.from: (int -> 'a option) -> 'a t
Stream.of_list: 'a list -> 'a t
Stream.of_string: string -> char t
Stream.of_channel: in_channel -> char t
ストリームの構築。
Stream.iter: ('a -> unit) -> 'a t -> unit
ストリーム全体に関数を適用。
Stream.next: 'a t -> 'a
ストリームの最初の要素を返す。最初の要素は削除される。
Stream.count: 'a t -> int
捨てられた要素の数
Stream.npeek: int -> 'a t -> 'a list
最初のn個の要素をリストにして返す。n個未満の時はそれらをリストにする。

String Module

stringも"zero origin"。
stringは ""で囲む。
charは ' 'で囲む。
String.length [string]
  stringのために確保されている長さを返す。
String.get [string] [position]
String.set [string] [position] [char]
  stringのposition番目の文字をcharに置き換える。
String.create [length]
  長さlengthの文字列を作る。
  場所を確保するだけで、内容は不定。
String.make [length] [initial_char]
  initial_charの長さlengthの連を作る。
String.copy [string]
  文字列をそのまま返す
String.concat [string] [string_list]
  文字列と文字列のリストをとる。
  文字列のリストの要素の間に第一引数の文字列を連結した文字列を返す。
String.uppercase string -> string
String.lowercase string -> string
  文字列全体にCharのuppercase,lowercase (それぞれ、小文字、大文字への変換) をmapする。
String.escaped string -> string
  特殊文字を \ でエスケープした文字列を返す

メモ
  String s の n 番目の文字は s.[n] のようにして参照できる。配列の場合は () を使う ことに注意

サンプル
  [] で参照.... StringGetNotation.ml

Sys Module

Sys.argv
  コマンドライン引数文字列
Sys.command string
Sys.file_exists string -> bool
Sys.remove string -> unit
Sys.rename string -> unit
Sys.os_type
  OS情報を示す変数。
Sys.time unit -> float
Sys.chdir string -> unit
  作業ディレクトリの変更
Sys.getcwd unit -> string
  作業ディレクトリ名
Sys.getenv string -> string
  環境変数の取得

  getenv.ml
Sys.word_size
Sys.max_string_length
Sys.max_array_length

Str library

Str Module

Str moduleはCで実装されている (otherlibs/str/strstubs.c) 正規表現を 使える。ocamlインタプリタ上で使用するには #load "str.cma";; を実行する。
Str.regexp: string -> regexp
  正規表現をコンパイルする。
Str.regexp_case_fold: string -> regexp
  大文字、小文字の差を無視するように正規表現をコンパイルする。
Str.string_match: regexp -> string -> int -> bool
  正規表現によるパターンマッチを行う。マッチに成功したか?(bool)が返る。int はマッチを開始する位置。
Str.matched_group: int -> string -> string
  最後に行ったマッチでN番目のグループにマッチした部分文字列を返す。ここで、 stringはその最後に行ったマッチと同じ文字列にしなければならない。これはプログラマーの 責任。位置を覚えているらしい。
Str.string_before: string -> int -> string
Str.string_after: string -> int -> string
Str.match_beginning: unit -> int
  マッチした開始位置
Str.match_end: unit -> int
  マッチした終了位置
Str.split: regexp -> string -> string list
  regexp は区切り文字を正規表現で表したものである。区切り文字で文字を区切って結果を文字列 リストにして返す。
  例) split_by_colon.ml

グループにマッチした文字列を取り出す例

# #load "str.cma";;
# let r = Str.regexp "mamewo\\(.+\\)$";;
val r : Str.regexp = <abstr>
# Str.string_match r "mamewotoko" 0;;
- : bool = true
# Str.matched_group 1 "mamewotoko";;
- : string = "toko"
(* matched_group には直前のマッチに使った文字列を渡すべき。
   違う文字列を渡すとどうなるか観察してみる *)
# Str.matched_group 1 "mamewohogegheo";;
- : string = "hoge"
(* マッチした位置を保存しているのかな? *)

正規表現
.改行以外の任意の一文字にマッチ
^文頭にマッチ
$文末にマッチ
[]文字集合
*直前のパターンの0回以上の繰り返し
+直前のパターンの1回以上の繰り返し
?直前のパターンの1回または0回のマッチ
\|(中置)選択
\(\)グループ化



  .dependの依存元 (依存するほう?) を表示 inverseDepend.ml
  Bが入力。 A depend on B なるAを出力
  Str.string_matchは指定された位置からマッチを開始する。test.ml
  文字列ではグループ化はbackslash( \ ) をエスケープして "\\(...\\)" と表現するよ strtest.ml
  \| ってあんまり使わないけど、使いたくなることはある。grp.ml
  \(...\)? のようなマッチで、グループにマッチする文字列がなかった場合に、 あとで、 matched_group でこのグループにマッチした文字列を取り出すと Not_found 例外が返る。 regexp_question.ml
  EUCで日本語をマッチ。 teacup.ml

The threads library

Thread Module

Thread.t型がスレッドハンドラ型
Thread.create: ('a -> 'b) -> 'a -> t
  スレッド作成。引数には実行する関数とその引数を渡す。第一引数の関数の 返り値は捨てられる (discarded) ので、親からその返り値は直接は参照できない。 なにか外に値を返すなら、リファレンスに値を書き込むようにする。
let future f x = 
  (* 結果の書き込み先をローカルにする。 *)
  let result = ref [] in
  let t = Thread.create (fun x -> result := (f x)::!result) x in
  (t, result)
;;

(* ううむ、なにか違う名前だったような.... *)
let attach (t, result) =
  begin
    Thread.join t;
    match !result with
      hd::[] -> hd
    | _ -> failwith "???"
  end
;;

サンプル
  mlThread.ml
  return_thread_value.ml
  configure -with-pthread で configure を実行して、make world && make install すると、バイト コードだけでなく、ネイティブでもスレッドを使用できる。 つまり /usr/local/lib/ocaml/threas/threads.cmxa が作成される。

Mutex Module

排他ロック。
Mutex.create: unit -> t
  ロック作成。Mutex.t がロックの型
Mutex.lock: t -> unit
  ロックを獲得する。獲得できなかったら他のスレッドがロックを解放 (Mutex.unlock) するまで待つ。
Mutex.try_lock: t -> bool
  ロックの獲得を試みて成功したら獲得してtrueを返す。失敗しても解放を待たずに false を返す。
Mutex.unlock: t -> unit
  ロックを解放する。他のロックを獲得しようとしているスレッドが(あれば)再開する。

サンプル
関数を局所的に宣言できるので、ロックは必ずグローバルなんてことはしなくていい!! たとえば、2つのスレッドで一つのリファレンスに入った整数を1ずつ加えながら表示 するプログラムを考える。
ロックを獲得しないと...。同じ数字が2回表示されることがある。not_sequential.ml
ロックを用意して2スレッドが排他的にfのなかを実行するようにする sequential.ml

Dbm library

Dbm Module

  NDBMは文字列のキーと文字列の値を格納する簡単なデータベース。
Dbm.opendbm: string -> open_flag list -> int -> t
  データベース名と、モード、データベースファイルのアクセス権限を 引数にとる。モードはデータベースの読み込み、書き込み、作成を表したもので 下のように定義されている。これをリストにして渡す。データベース記述子を返す。
type open_flag =
  | Dbm_rdonly (* 読むだけ *)
  | Dbm_wronly (* 書くだけ *)
  | Dbm_rdwr   (* 読み書き *)
  | Dbm_create (* 存在しなかったら作成 *)

Dbm.close : t -> unit
  データベースを閉じる。
Dbm.find : string -> string
  キーを指定して検索する。存在しない場合はNot_found(Pervasivesモジュールに 定義されている)を投げる。
Dbm.add : string -> string -> unit
  データベースにデータを加える。第一引数にキー、第二引数にデータを指定する。 既にキーがある場合はDbm_errorを投げる。
Dbm.replace : string -> string -> unit
  addとほぼ同じだが、既にキーがある場合は書き換える。
Dbm.remove : t -> string -> unit
  キーを指定してデータを削除する。キーが内場合はDbm_errorを投げる。
Dbm.firstkey : t -> string
  最初のキーを返す。順序は不定。
Dbm.nextkey : t -> string
  次のキーを返す。順序は不定。
Dbm.iter : (string -> string -> 'a) -> t -> unit
  関数をキーとデータに対して適用する。引数の関数には第一引数に キー、第二引数にデータが渡される。
サンプル
  db.ml

Graphics library

Graphics Module

座標のとり方は数学と同じで左から右にx軸、下から上にy軸がのびる。 左下隅が原点になる。

Graphics.open_graph string -> unit
  グラフィックスウィンドウを表示する。引数は表示するディスプレイの 位置、大きさを指定する文字列。どのように解釈されるかは実装依存。
この文字列の解釈の仕方についてはマニュアルに書いてある。
" 100x100+0-0" はサイズ100x100のウィンドウを位置(0,0)に開くことを意味する。 この文字列の最初のスペースは必要(display-nameを指定せず、位置のみ指定 する場合)。X-Windowシステムでのdisplay-nameを指定できる。 (mamewo:0のような)。 空文字列を与えるとデフォルトの設定で表示する。
Graphics.set_color color -> unit
  色を設定する。色を表すデータ型型colorはGraphicsモジュール内でintとし て定義されている。0xRRGGBBのようにRGBを16進2桁ずつ割り当てられている。rgbという 3引数(RGB)をとりcolorを返す関数もある。
Graphics.fill_poly (int * int) array -> unit
  多角形を描き塗りつぶす。多角形の頂点の座標の配列を受け取る。

サンプル
  g.ml
  prototype.ml (気ままなお絵書き)
  prototype1_2.ml (もっと気ままに)
  drawing.ml (これが真のお絵書き)
  graphParser.ml (そしてちょっとした テキスト処理)
  graphParser2.ml (一行ずつの処理)

Bigarray library

Bigarray Module

サイズに制限無し。数値のみ。多次元配列(16次まで)を簡単に扱える。
インタプリタ上で使うにはbigarray.cmaを組み込む。
ocamlmktop -o mytop bigarray.cma
./mytop
3.04に変更したら #load "bigarray.cma"で使えた...。
Bigarrayモジュール内にArray1,Array2,Array3というモジュールが定義されている。 これらはそれぞれ一次元、二次元、三次元配列である。Bigarrayのインスタンスは ('a,'b,'c) tのような型を持つ。'aはOcamlでの型、'bは実際にBigarrayに格納されている 型、'cは配列のレイアウト(c_layout/fortrun_layout)の型である。Ocamlでfloatは 倍精度(64bit)であるが、Bigarrayに格納するfloatは単精度(32bit)であるという ことが可能である。Ocamlの型と実際に格納されている値の型との対応を決めるのが ('a,'b) kind型である。
Array1.create [kind] [layout] [size]
  生成。Bigarray.int,Bigarray.float32のようなkindとBigarray.c_layoutの ようなlayoutとサイズを与える。
Array1.dim [bigarray]
  配列長
Array1.set [bigarray] [index] [element]
  代入
Array1.get [bigarray] [index]
  参照

Unix library

I/O、プロセス間通信、ネットワーク通信。UnixLabelsはUnixモジュールのインターフェイス でラベル付きで関数が定義されている。ラベルが付くことで引数の意味が分かりやすくなっている。 UnixLabelsを使うには、使用する実装ファイル中で module Unix = UnixLabels と書く(みたい) ラベルを使って引数を指定するには以下のようにする。
let s = Unix.socket ~domain:Unix.PF_INET ~kind:Unix.SOCK_STREAM ~protocol:0 in ,..
逆にUnixLabelsを使うならラベルを付けないと怒られる。

Unix Module

Unix.getcwd: unit -> string
  作業ディレクトリ名を得る
  例 cwd.ml
Unix.execv: string -> string array -> unit
  指定された実行ファイルに指定した引数と現在のプロセスの環境を渡して実行する。execvp (実行ファイルをパスから検索する) execve (環境を渡せる) などの関数もある。Unix.execv のあとにかかれたコードは実行されない。
  例 exec_script.ml
Unix.in_channel_of_descr: file_descr -> in_channel
Unix.out_channel_of_descr: file_descr -> out_channel
Unix.descr_of_in_channel: in_channel -> file_descr
Unix.descr_of_out_channel: out_channel -> file_descr
  ファイルディスクリプタとチャネルの変換。
Unix.opendir: string -> dir_handle
  ディレクトリを開く
Unix.readdir: dir_handle -> string
  ディレクトリ内の次のエントリを読む。終りならばEnd_of_file例外が飛ぶ。
Unix.closedir: dir_handle -> unit
  ディレクトリを閉じる
Unix.gethostbyname: string -> Unix.host_entry
  ホスト名からホスト情報を得る。host_entryのh_addr_listがIPアドレスを表すinet_addrの 配列。これは抽象型。inet_addrの文字列の変換はstring_of_inet_addr関数を使う。
Unix.socket: socket_domain -> socket_type -> int -> file_descr
  ソケットを生成する。socket_domainは Unixドメイン(PF_UNIX)とInternetドメイン (PF_INET)がある。socket_typeは通信の階層に応じて、SOCK_STREAM,SOCK_DGRAM,SOCK_RAW,SOCK_SEQPACKET がある。3つめの引数は~protocolというラベルが付けられている。0にするとデフォルトのプロトコルが 使用される。file_descrが変えるので、Unix.read,Unix.write (send*,recv*)を使って読み書きする。
Unix.bind: file_descr -> sockaddr -> unit
  ソケットに名前を付ける。指定したアドレスをソケットに対応づける。sockaddrは ADDR_UNIX かADDR_INET。SOCKETのドメインに応じて決める。ADDR_UNIXはファイル名を引数にとる。ADDR_INETは inet_addrとポート番号(int)のtupleを取る。
Unix.connect : file_descr -> addr:sockaddr -> unit
  接続する。sockaddrは ADDR_UNIX , ADDR_INET がある。前者はマシン内での 通信に使用し、引数にファイル名を指定する。後者は異なるマシン間での通信に 使用し、IP アドレスと、ポート番号を指定する。IP アドレスは 数字を . で区切った IP アドレスを表す文字列があれば、Unix.inet_addr_of_string 関数で得られる。 マシンの名前がわかっている場合は gethostbyname で名前を問い合わせて、ホスト情報 を得て (host_entry 型) そこの h_addr_list フィールドをみる。これは IP アドレスの 配列になっている。
  例 http.ml, url.ml, proxy.ml, ftp.ml
Unix.listen: file_descr -> int -> unit
  ソケットに対して受け付ける接続の最大数を設定。
Unix.fork: unit -> int = <fun>
  プロセス生成。返り値が0ならば子プロセス。 fork.ml
Unix.system string -> Unix.process_status
  sh(シェル)を使ってコマンド実行する。
Unix.pipe: unit -> Unix.file_descr * Unix.file_descr
  パイプ生成。ペアの一番目の要素が読み込み口、二番目が書き込み口
  例 pipe.ml / pipe_session.txt
Unix.open_process_out: string -> Pervasives.out_channel
Unix.open_process_in: string -> Pervasives.in_channel
  引数で指定された文字列をシェル (sh) で解釈実行する。そのシェルの標準入力、標準出力につながっているチャネルを返す。
  例 external_process.ml
Unix.mktime: : Unix.tm -> float * Unix.tm
  Unix.tmの 年月日、時分、夏時間か?を埋めて他のフィールドである、曜日、 一年のうちで何日目かを埋めたUnix.tmを返してもらう関数。Cにもmktimeという同様な 関数がある。曜日は日曜日が0に対応する。月は0 originであり、これはCのmktimeと 同じである。年は1900年との差である。

# let a = { Unix.tm_sec = 0; Unix.tm_min = 0; Unix.tm_hour = 0;
 Unix.tm_mday = 13; Unix.tm_mon = 8; Unix.tm_year = 102;
 Unix.tm_wday = 0; Unix.tm_yday = 0; Unix.tm_isdst = false };;
val a : Unix.tm =
  {Unix.tm_sec = 0; Unix.tm_min = 0; Unix.tm_hour = 0; Unix.tm_mday = 13;
   Unix.tm_mon = 8; Unix.tm_year = 102; Unix.tm_wday = 0; Unix.tm_yday = 0;
   Unix.tm_isdst = false}
# Unix.mktime a;;
- : float * Unix.tm =
(1031842800.,
{Unix.tm_sec = 0; Unix.tm_min = 0; Unix.tm_hour = 0; Unix.tm_mday = 13;
 Unix.tm_mon = 8; Unix.tm_year = 102; Unix.tm_wday = 5; Unix.tm_yday = 255;
 Unix.tm_isdst = false})
2002/ 9/13は金曜日。2002/ 1/ 1から数えると255日目である。
difftime.ml
localtime.ml
current_date_string.ml

サンプル
  readdir.ml
  listen.ml
  listen2.ml
  easy_server.ml
  fork.ml
  pipe.ml
  read.ml 端末操作。エコーを止めるとか
  ipv6_addr.ml ocaml 3.08.0 から IPv6 に対応した。ちょっとテスト
  ipv6_wget.ml ipv6でローカルに たっているWebサーバーからGET!

Labltk library

Tk Module

create でWidgetを生成して (create では初期値を設定できることが多い ) configure_get, configure で状態を取得 / 設定する。 これらは Widget 毎にだいたい定義されている。
Tk.pack で親 window に配置する。 Tk.mainLoop を呼び出すと実行が始まる。
Winfo モジュールに Widget の情報を取り出す関数が定義されている。 Widget の親 Winfo.parent など。

Clipboard
Labltk 内部のローカルなクリップボードを操作する。

  clipboard_test.ml

Selection
今度は X レベルに及ぶ copy & paste

  ラベルが selection を得るとどうなるのだろう? selection_test.ml
  テキストエントリとかテキストボックスも同様!? entry_selection.ml

Listbox

  insert で要素を挿入する。挿入にはいろいろあるようで。。 listbox_test.ml

Scrollbar
javaのSwing だと ScrollPane というパネルにスクロールバーでスクロールする 物を「入れる」記述で簡単に書けるようになっている。Labltkでは command ラベルが ついた引数で関連づけているようである。

  otherlibs/labltk/browser/ の中のソースを参考にした。 scrollbar_test.ml

Balloon
触ってしばらくするとひょこっとでてくるあれ。黄色しかないのだろうか? (涙)

  whatsballoon.ml

Frame
  配置のための Widget のようだ。 GTKでいう hbox, vbox のような箱。
  listbox_with_filter.ml

Radiobutton
  GUIのAPIのセンスはここで見えてくると僕は思っている。labltkはなかなかナイス。 同じ変数 (Textvariable.textVariable) に関係付ける。確か GTK ではラジオボタンのグループ に突っ込んでいく。まぁグループを変数と言い替えただけですが。。。。
でも、一般的にRadioボタンって使いますが、 なんでradioボタンと呼ばれるのかは知らないですねぇ。なんでだろ〜。

  radio.ml

Timer
  コールバックをさくさくっと登録できていいなぁ。

  labltk_timer_test.ml
  multiple_timer.ml

Canvas/Imagephoto
  ただただサンプルの tetris.ml を参考にして張り付けてみました。ってだけ。

  image.ml

サンプルアプリケーション
ラベルをはるだけ。最も簡単なテストプログラム labeltest.ml
これも多分簡単なの labltktest.ml
Frameを用いてlayout layout.ml
Canvasのサンプル can.ml
TextEntryとボタン。あとクリック時の動作 (7に反応) entry_and_event.ml
Double ButtonPress <: ButtonPress ... move_dragged_oval.ml
マウスの移動は `Motion で捕捉できる。 whatsmotion.ml
ちょっとかいてみた graph_drawer.ml
パスワード入力ダイアログ get_password.ml
もう一つ window を開いてみる例。そのwindowがcloseされるまで親windowが待つ (Tkwait.window) easy_biff.ml
  おまけ。 pop.ml mytcp.ml
  パスワードを入れるチャンスは1回!! (間違えたら再び立ち上げよう)
  メールの数が前回から変化したら色を変える。クリックで戻る
geometryはXの形式の文字列で指定する。where_am_i.ml
日本語のテスト。ただそれだけ。 japanese.ml
しょわしない(標準語?)ticker ticker.ml
labltkサンプルプログラム集。
アンテナ mbiff がいろいろ使っている。 Entry、Balloon、Label。
以前書いたような気がするけど。。。。キーの名前を出力 keytest.ml

メモ
  ocaml-3.06/otherlibs/labltk/examples_labltk/ にサンプルプログラムがある。

その他

整数リテラル
  普通の数字は10進数
  頭に 0xか0X を付けると16進数
  0o,0O で8進数
  0b,0B で2進数が書ける

if文はelseを省略することもできる。その際if文全体の型はunitになる。
(あたりまえ)
文字列は開始位置、長さを指定して扱う。
コードサイズがおおきいと、Stack Overflowでコンパイルできない。
モジュールについて知りたければocamlbrowserを使う。
ocamlbrowserのオプション-Iでパスを指定することで新たなモジュール のAPIをブラウズできる。
おまけ
  ocaml 3.07 patch 2 で ocamlbrowser のネイティブバージョンをつくるための Makefile のパッチ。ocamlbrowser.opt-3.07-2.patch
patch -p0 < ocamlbrowser.opt-3.07-2.patch
して ./configure && make world && make opt
すると、otherlibs/labltk/browser/ocamlbrowser.opt ができる

コンパイル

コンパイル単位

一つのファイルはファイル名のprefix一文字目を大文字にしたstructureとして 扱われる。set.ml等とつけるとライブラリのSet moduleと重なって良くない。 ライブラリのUnixLabelsはunixLabels.* のように命名されている。

コンパイラ

ocamlには、ocamlrun上で動くバイトコードを生成するocamlcと、プラットホーム によってはネイティヴコードをはくocamloptがある。分割コンパイルもサポートしている。 ocamlc,ocamloptはどちらもocamlrunというバイトコードインタプリタ上で動作しているが ネイティブ上で動作するコンパイラを生成することもできる。(make opt.opt) ocamlc.opt,ocamlopt.optという名前のプログラムがそれである。
リンクは依存される側を先に書かなければならない。例えばUnix moduleを使用している quick.mlというプログラムがあるとすると、

ocamlcopt.opt /usr/local/lib/ocaml/unix.cmxa quick.ml -o quick

という風にコンパイルする。(ネイティブの場合) ちなみにバイトコードの場合は

ocamlc.opt /usr/local/lib/ocaml/unix.cma quick.ml -o quick

オプション

-cコンパイルのみ。オブジェクトコード生成
-o出力ファイル名の指定
-I [dir].cmiファイルのあるディレクトリを指定
-i定義された関数名、名前を出力

拡張子

.mliインターフェイス(input)
.ml実装(input)
.cmiコンパイルされたインターフェイス
.cmoコンパイルされた実装(オブジェクトバイトコード)
.cmxコンパイルされた実装(ネイティブオブジェクトコード)
.cmxaライブラリ
.c,.o,.a,.so等も使えるらしい。

OCamlMakefile

OcamlのためのMakefileの雛型。これを使うとMakefileが簡単に書ける。
例) quick.mlのコンパイル。内部ではUnixモジュールを使用している。ネイティブ コードを生成する。
OCAMLMAKEFILE = /home/tak/lib/ocaml/OcamlMakefile
SOURCES = quick.ml
RESULT = quick
LIBS = unix
all: native-code
include $(OCAMLMAKEFILE)
SOURCE,RESULT
ソースファイルと結果ファイルの名前。デフォルトでそれぞれ foo.ml,fooになる。

LIBS
ライブラリの名前。拡張子はいらない。

all: (コンパイル結果の指定)
native-code,byte-codeはもちろん、debug-codeも簡単にコンパイルできる。makeの引数 にbc(バイトコード)、nc(ネイティブコード)、dc(デバッグコード -gオプションを付けた コンパイル)を与えることで、コンパイル結果を変えることができる。

include
OcamlMakefileを最後にincludeしておく。

使用するコンパイラ
使用するコンパイラはデフォルトでバイトコードバージョン(ocamlc,ocamlopt)が 使用される。変数 OCAMLC,OCAMLOPTを変更することで使用するコンパイラを変更 できる。(ocamlc.opt,ocamlopt.optなど)

複数のターゲットがある場合
export OCAMLMAKEFILE = ../OcamlMakefile

EX1 = SOURCES=ex1.ml RESULT=ex1 THREADS=yes
EX2 = SOURCES=ex2.ml RESULT=ex2 THREADS=yes

all:    ex1 ex2

ex1:
        $(MAKE) -f $(OCAMLMAKEFILE) $(EX1)

ex2:
        $(MAKE) -f $(OCAMLMAKEFILE) $(EX2)
引数を定義して、makeに渡す。OcamlMakefileをmakeの引数として渡す。

プライベートで使うライブラリの構築例
  Makefile
  mylib.ml , mylib.mli
  コンパイル
    > make ncl
  ライブラリの使い方
    > ocamlopt.opt -I /home/tak/lib/ocaml mylib.cmx useLibraryTest.ml -o use
  /home/tak/lib/ocaml はライブラリへのパス。 findlibを使えば簡単になるのか?わからないけど。

メモ

"Files /usr/local/lib/ocaml/unix.cmxa and /usr/local/lib/ocaml/unix.cmxa both define a module named Unix" というエラーメッセージがでる

THREAD変数を定義しているときはunixとthreadsライブラリがリンクされるので、LIBS変数には含めないようにする。

ocamlc

バイトコードコンパイラ

オプション
-customocamlrun無しで走るコードを生成

-dlambda, -drawlambda など dからはじまるオプションのほとんどは ダンプオプション。Ocamlで使われる中間言語をダンプする。utils/clflags.ml にオプションによってセット、クリアされるフラグや変数がが定義されている。

実行順序について考えてみる
  ファイル fst.ml , snd.ml
ケース1
  > ocamlc -c fst.ml
  > ocamlc -c snd.ml
  > ocamlc -o app fst.cmo snd.cmo
  > ./app
fst initialized
snd initialized

ケース2
  > ocamlc -o app snd.cmo fst.cmo
  > ./app
snd initialized
fst initialized
リンク順は大事

ocamlmktop

指定したオブジェクトファイルが起動時に読み込まれたトップレベル を作成する。

オプション
-customCのオブジェクトファイルのリンクを可能にする

対話的に関数を呼んだり型を確認できる。

 # バイトコードオブジェクトファイルをまとめる (個々をloadするのが面倒なので)
> ocamlc -a -o modML.cma modules.cmo miniML.cmo miniMLparser.cmo miniMLlexer.cmo miniMLmain.cmo
> ocaml
        Objective Caml version 3.04

# #load "modML.cma";;
# MiniMLmain.enter_val ;;
- : string -> MiniML.MLEnv.Mod.Core.val_type -> unit = <fun>
ocamlc -a -o modML.cma modules.cmo miniML.cmo miniMLparser.cmo miniMLlexer.cmo miniMLmain.cmo

ocamldebug

ocamlのデバッガ。

使い方
  コマンドプロンプトから
  > ocamldebug [options] [program] [arguments]

オプション
-I [dir] include directoryに [dir]を加える
-s [filename]ソケットの名前を付ける
-c [count]チェックポイントの最大値を決定する。
-cd [dir]カレントディレクトリを変更する
-emacsEmacs上でデバッガを走らせる
コマンド
helpデバッガのコマンドについてのヘルプを表示
showデバッガの情報表示。コマンドライン引数、デバッグ中のプログラム名など
goto与えられたTimeに飛ぶ
backstep前のイベントに達するまでプログラムを巻きもどす。引数Nを あたえると、これをN回繰り返す

プロファイリング

gcovのようにソースにコール回数をコメントとしていれたものを得るには ocamlcpでコンパイルし(バイトコード)、コンパイルされたコードを実行し、ocamlprof で結果を見る。実行時間に関する情報はGNUのツールgprofを使う。-pオプションをつけ てネイティブコードコンパイルし、実行、gprofで結果を見る。

libSequence.mlでの実行例
  > make pbc ## OcamlMakefileでは pbc=prifiling byte code pnc=profiling native code
  > ./prime 100
  > ocamlprof libSequence.ml > ocamlprof_result.txt
  > make pnc
  > ./prime 100
  > gprof prime > gprof_result.txt.gz

逆アセンブル

ocamlのバイトコードを読みたいときは、ocamlc の -dinstr オプションを使用するか、 ocaml の tools ディレクトリにある dumpobj を使うと読める。
オブジェクトファイルからそのオブジェクトファイルの情報を得る objinfo も tools ディレクトリにある。
dumpobj, objinfo ともにデフォルトでは生成されないようである。 tools ディレクトリで make dumpobj ; make objinfo すれば得られる
参考
  Re: [Caml-list] Is there a "disassembler" for Caml?

Cの関数を呼ぶ

byterunにあるCのコードを参考にして書くとよい。 引数は全てvalue型。具体的にはCのlong型になっている。CAMLprimはコンパイラに 必要な指示をあたえる。Windowsに関係あるようで。
Ocaml側からはexternal宣言でCの関数にOcamlの名前と型を付ける。

サンプル(とりあえず呼び出せることだけ確認)
  prim.c
  p.ml
コンパイルは
  > gcc -I/usr/local/lib/ocaml -c prim.c
  > ocamlc -c p.ml
  > ocamlc -custom prim.o p.cmo -o p
または
  > gcc -I/usr/local/lib/ocaml -c prim.c
  > ocamlopt -c p.ml
  > ocamlopt prim.o p.cmx -o P
  caml_and_c.tar.gz

CamlIDLを使用する
CamlIDL は Ocaml から C の 関数を呼ぶためのスタブコードを生成するプログラムである。 Ocaml と C の 関数の間でやりとりされるデータの変換を行う。

使用例
parsedate-2003-08-06.tar.gz
The InterNetNews library の parsedate を呼ぶスタブを書く。スタブ自体は 単純である。 sys/types.h をインクルードしなければいけないので、それを quote で入れておく。
libinn.a のようなライブラリとのリンクを簡単に行うには ocamlmklib を 用いて ocaml のライブラリを作っておくと楽である。 ocamlmklib は .o , .cmo , .a ファイルを引数にとって、それを一つのライブラリにまとめる。
Makefile は clean しか定義してないです (汗)
コンパイルするには build.sh スクリプトを使ってください。
おまけ。 CamlIDL CamlIDL を使いながら書いたメモ

Ocamlでscript

ocaml自身バイトコードでありocamlrun上で走る。ocamlが単独で走る ようにするために、ocamlmktopを次のように実行する。
ocamlmktop -custom -o mocaml
このmocamlにソースを流せばよい。
#! ./mocaml
print_string "mamewo\n";;
exit 0;;
参考 Pratical hints for using Ocaml (mostly Unix-specific)

lablgtk

コンパイルするにはgtkInit.cm{o,x}とlablgtk.cm{a,xa}とリンクする。 -w sは複文中の文がunitでない値を返すときにでる警告を出さないようにする。

例) example/内のprogressbar.mlのネイティブコードコンパイル
> ocamlopt -I /usr/local/lib/ocaml/lablgtk -w s -c progressbar.ml
> ocamlopt -I /usr/local/lib/ocaml/lablgtk -w s lablgtk.cmxa gtkInit.cmx progressbar.cmx -o progress
> ./progress
GWindow,GButtonなどG*がWidgetに対応する。それぞれはクラスで定義されている。 GMain.main () がgtk_main()に対応している。
小さいサンプル(gtktest.ml)

lablgtk2

lablgtk2のサンプル(gtk2_hello.ml)
TreeViewを使うがListStoreでリストを表示するサンプル (gtk2_treeview_list.ml)
string listを表示するように関数化(show_string_list.ml)
ラベルつき木を定義し、それを表示するように関数化(show_string_tree.ml)
TreeViewで2列表示してみる (two_column_tree.ml)
xml-lightを使ってXML(slashdot.rdf)のパーズ結果を表示(show_xml_tree.ml)
show_xml_tree.ml のTreeViewをScrolledWindowに入れるようにしてスクロールを可能にしたもの (gtk2_scrollwindow.ml)
Pango markupを使って装飾 (gtk2_treeview_with_tag.ml)
pango markup
HTMLを表示してみる(show_html_tree.tar.gz)
GtkSignal.connect_by_name を利用してみる (signal_connect.ml)
GtkWidget.S など S という名前のmoduleにGtkSignalの引数 sgn に与えるsignalを表す 値が定義されている。 (例 GtkWidget.S.key_press)
key-press-eventをとらえる。GtkSignal.connectを使ってみる (gtk2_keypressed.ml)

ocamlyacc / ocamllex

字句解析器(lexer)、構文解析器(parser)を生成する。

メモ
ocamlyaccからパーザープログラムのソースとインターフェイス(ml,mli)ファイルが 生成される。mlファイルをコンパイルする前にmliファイルをコンパイルする。
lexbufをLexing.from_*から生成。
ocamllexにはネイティブ版ocamllex.optがある。

ocamlyaccのオプション
-v構文解析の表を出力する(拡張子は.output)
-b[prefix]出力するプログラムのprefixを指定

参考
  Lexing Module

Standard MLとの比較

型を等価にするか隠蔽するかは signature の適用の仕方による ":" か ":>" か?
+,-はreal,intについてoverloadされている。 real同士かint同士。型を付けずに関数定義すると intに型付けされる。
- val f = fn x => fn y => x + y;;
val f = fn : int -> int -> int
- val f = fn x:real => fn y:real => x + y;;
val f = fn : real -> real -> real

Emacs Lispたち

Ocamlのエラージャンプ
OcamlのAPIサーチ(リージョンで指定、完全一致)
search_ocaml_type.el, search_type.ml, type_server.ml, type_server_with_type_annotation.ml
まとめてみた。ちょっと改良 searchtype-2004-01-08.tar.gz

Ocamlのインストール

コマンドは configure; make world; make opt; make opt.opt; make install; make installopt。emacsディレクトリにEmacs lispのためのMakefileがある。 EMACSDIRを指定して、 make install; make ocamltags; make install-ocamltags。
run-camlをEmacsで実行するとinf-caml.elにエラーがあるらしく、うまくいかない。 (inf-caml.elの367行目 (lookup-key caml-mode-map [menu-bar caml])がnilを返す) のでこのlet文をコメントアウトしたらrun-camlがうまく起動した。

info で書かれたマニュアルのインストール
  info ってどういう仕組みでジャンプしているんだろう...?
tar xfvz ocaml-3.07-refman.info.tar.gz ; cd infoman ; cp /usr/share/info
/usr/share/info/dir の適当なところに以下の行を追加する。
* Ocaml 3.07: (ocaml).          Ocaml Language Reference Manual
この括弧の中の文字列を元にしてジャンプ先を決めているのだろうか? なぞ。 ん、Topノードに戻れないよぉ (涙)

趣味の世界

比較演算子 (>),(<),(>=),(<=),(=)はオーバーロードされている。
ライブラリはstdlibにソースがある。Cで定義された関数はbyterunにあったりする。
# let x = 1.2 in let y = 1.2 in x == y;;
- : bool = false
# let x = 1.2 in let y = x in x == y;;
- : bool = true
# let x = 1 in let y = 1 in x == y;;
- : bool = true
let recでデータ定義することで循環するデータ構造を簡単に定義できる。
# type mrec = { name:string ; mutable ptr:mrec };; (* 再帰的なデータ構造 *)
type mrec = { name : string; mutable ptr : mrec; } 
# let rec me = { name = "takashi"; ptr = me };; (* 自分を入れる *)
val me : mrec =
  {name = "takashi";
   ptr =
    {name = "takashi";
     ptr =
      {name = "takashi";
       ptr =
 ... 
# type graph = Graph of string * graph;; 
type graph = Graph of string * graph
# let rec a = Graph("tak",a);;
val a : graph =
  Graph ("tak",
   Graph ("tak",
...
# let rec a = Graph("tak",b) and b = Graph("mamewo",a);;
val a : graph =
  Graph ("tak",
   Graph ("mamewo",
...
val b : graph =
  Graph ("mamewo",
   Graph ("tak",
... 
graph_session.txt (いろいろ書いてみる)
let-bindingpattern [: typexpr] = expr 。代入の左辺はパターン なので例えばこんなことができる。
# let a,b = 1,2;;
val a : int = 1
val b : int = 2

ocamlfind
remove [packagename]ライブラリの削除
install [packagename] [filelist]ライブラリの追加
cmxaはaとともにインストール

getopt
  by Alain Frisch
  まず、findlibをインストールしなければならない。
  parse_cmdline Getopt.opt list -> (string -> unit) -> unit
  Getopt.optはタプル。短いコマンド、長いコマンド、引数無しのハンドラ、引数つきのハンドラ をタプルにする。ハンドラはオプション。このオプションの使い方で引数を許すかどうかを決める。
オプションは短いオプションと長いオプションの2種類ある。
短いオプション
  -o file
  -ofile
一つの'-'に続いてオプションの文字を入れる。引数は直後にあたえても、スペースを はさんで与えてもよい。
長いオプション
  --option=value
二つの連続した'-'に続きオプション名を指定する。スペースを入れずに=で区切り 引数を指定する。

サンプルプログラム get.ml

ツール

ocamlweb

  Ocamlのソースをtexのソースに変換する。なんと最後にキーワードの 索引までついてしまうという!!すごい。コメントは地の文になるようだ。

  > ocamlweb --ps restrictHashValueType.ml -o restrict.ps

ocamldoc

  3.05 からocamlに標準で入るようになったとか。使ってみた。(しかも日本語で 汗)
  ソース drawMakefile.ml
  Content-type などを加えるスクリプト add_japanese_tags.pl
  結果
  一部ライブラリにしてみた drawMakefile.ml , mylib.ml
  -dot オプションでモジュールの依存関係を表した dot ファイルがはける。 ファイル名は ocamldoc.oot である。 これはGraphviz の入力 となるファイルである。 たとえば、
> dot -Tpng ocamldoc.out
を実行すると、png 形式の画像で依存性をみることができる。

リンク

The Caml language
Archives of Caml Weekly News
  Ocamlのライブラリのリリース状況やメーリングリストで流れた質問に対する答えなど。役にたつ。 LWN.net でも読める (つながらない時 or 検索したいとき)
Developing Applications With Objective Caml
Using, Understanding, and Unraveling The OCaml Language (by Didier Remy)
Programming with Objective Caml
  ocamlfind, Shell などのツール、アプリケーションを配布している。
Dimitri Ara's homepage: ocaml
  ocamldsort!! モジュールを依存性をもとに topological sort する
  debianのパッケージにもなっている?
The Caml Hump: Latest updates
  ライブラリやツールへのリンク集
MLgraph
  Ocaml を使って落書っ!!
Camlserv web-server written in ocaml
  HTMLを処理できる!
  (basics.ml と html.ml をコンパイルして unix.cma をリンクすれば使える。testhtml.ml)
bibtex2html: BibTeX to HTML converter
  参考文献データベースファイル(.bib)からHTMLに変換する Ocaml でかかれたプログラム
Ocaml Curl Library
  いろんな通信ができる HTTP, FTP, SSL..
Ocaml プログラミング入門
  丁寧でわかりやすいです。
OCamlチュートリアル
OCaml.JP
  ocamlkf (ocamlでかかれた日本語文字コード変換プログラム) にはしびれました。 (サンプルコード ocamlkf (ocamlでかかれた日本語文字コード変換プログラム) にはしびれました。 (サンプルコード ocamljcode.ml)
数理科学的バグ撲滅方法論のすすめ:ITpro
Ocamlでscript
モジュール
  functorの例(abstract/manifest型を中心にした例)
Description of Standard ML
  SMLの文法。リンクが多くて読みやすい
Ocamlのソースコードを眺める
Ocaml Internal
  ocaml 3.06 の内部をみてみた
Programs (このサイトのプログラム一覧)
OcamlでXML処理

CWNメモ

Caml Weekly Newsをもとにしたメモです。実際に自分で使ってみてないものも載せることにしてみました。

サイズ可変の配列がほしい

    Ocaml ExtLib

ウェブアプリケーションを書きたい

    WDialog Homepage

Makefile を書くのが面倒だ

    OCamake

例外がどこから投げられたかを表示させたい

Stop at exception
  ocamlrun -b で実行
Line number for index out of bounds
  環境変数 OCAMLRUNPARAMをつかってocamlrunにオプションを渡す
サンプルプログラム exception_trace.ml

ライブラリを簡単にインストールしたい

    GODI: The source-code Objective Caml distribution

ocamlのstatic call-graphを描くには?

    Generating a call-graph
    関数の引数に関数をわたすことができるので、引数にどの 関数が渡されうるかを調べるために Control-flow のみならず Data-flow の解析も必要。
    0-CFA, K-CFA (Shivers, O., Control-flow analysis in Scheme), polymorphic splitting って何だろう?
    Principles of Program Analysis ←ほしいな

Ocamlコンパイラの実装を知りたい

  Inner workings of Caml compiler

C のパーザーのプロトタイプ

  A 3D animation of Linux source code development
  linux kernel のファイルの間の依存性を 3D で描画してビデオで見せるっていう プログラムがあって、そこでパーザーを書くのに ocaml が使われた。
  このパーザーをちょっと使ってみるテスト c_parser_driver-2003-12-27.tar.gz
  いつものようにVCGと併せて遊ぶ c_parser_driver-2004-01-01.tar.gz

Ocamlの値の可視化

  Jean-Christophe Fillietre : Programming の display

SMLからOcamlへのお手軽変換

  SML -> Ocaml
  caml4pを利用したおてがる変換なので変換後のコードは正しいとは限らないらしい

OcamlでかかれたWebサーバー

  Ocsigen
  pcre pcre-ocaml ocaml-ssl ocamlnet などを入れねばならずインストールはちょっと面倒

メモ

# Printf.printf ;;
- : ('a, out_channel, unit) format -> 'a = <fun>
# Printf.printf "%d";;
- : int -> unit = <fun>
これを実現する心は? (前の引数の値そのものによって出力の型が変化する)
formatと値を動的に変化させる (only_printf.ml)
このプログラムは型エラーを生じる。 第一引数は format型であり、string型 ではないのだ。じゃぁ、format型ってなんだろう?
# Printf.scan_format;;
- : string ->
    int ->
    (string -> int -> 'a) ->
    ('b -> 'c -> int -> 'a) -> ('d -> int -> 'a) -> 'a
= <fun>
が怪しげ。っていっても関数名にformatって付いているだけで、型にはformatなんて 一言も書いてない。もう少し調べていくと、もっと怪しげなformatモジュールがある。 でも、format型のものを返す関数がないようなきがする......。formatって何?
# ("%s": (string -> 'a, out_channel, unit) format);;
- : (string -> unit, out_channel, unit) format = <abstr>
んんんんん?
# ("%d": (string -> 'a, out_channel, unit) format);;
Characters 1-5:
  ("%d": (string -> 'a, out_channel, unit) format);;
   ^^^^
This expression has type (int -> 'a, 'b, 'a) format
but is here used with type (string -> 'c, out_channel, unit) format
# ("%d": (int -> 'a, out_channel, unit) format);;
- : (int -> unit, out_channel, unit) format = <abstr>
型システムに組み込まれているのかな? (format文字列から型を推論)
うーん、こういうものは切り離して考えてみたい気がする。
もちろん、このリテラルはデフォルトで string 型
# "%s";;
- : string = "%s"

Marshal と同じノリでいってみようか。とおもっても駄目らしい。
型におもいをはせる
# let head::tail = 
    (match Str.split (Str.regexp ":+") str with [] -> raise Not_found | x -> x);;
Characters 4-14:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]
  let head::tail = (match Str.split (Str.regexp ":+") str with [] -> raise Not_found | x -> x);;
      ^^^^^^^^^^
Strモジュールの「暗に大域的な状態(マッチした位置など)をもつこと」におもいをはせてみる
  threadTest.ml 他のThreadのマッチ結果が見える。(泣)
  rightThreadTest.ml ロックを使ってみた
  processTest.ml Strモジュールの「副作用」の部分もforkするから大丈夫。

openとuse
  openはパスを省略するために使用。includeはモジュールの内容を そのまま展開。

一部だけ変更したrecordを返す式
# type point = { x: int ; y: int };;
type point = { x : int; y : int; } 
# let p1 = { x = 1; y = 2 };;
val p1 : point = {x = 1; y = 2}
# let p2 = { p1 with x = 1000 };;
val p2 : point = {x = 1000; y = 2}
# match p2 with { x = xx } -> xx;;
- : int = 1000
ついでにrecordのマッチ。(しかも一部)

レコードのフィールド名

{ Unix.tm_sec = 0;
  Unix.tm_min = 0;
  Unix.tm_hour = 0;
  Unix.tm_mday = 1;
  Unix.tm_mon = 1;
  Unix.tm_year = 70;
  Unix.tm_wday = 0;
  Unix.tm_yday = 0;
  Unix.tm_isdst = false };;
- : Unix.tm =
{Unix.tm_sec = 0; Unix.tm_min = 0; Unix.tm_hour = 0; Unix.tm_mday = 1;
 Unix.tm_mon = 1; Unix.tm_year = 70; Unix.tm_wday = 0; Unix.tm_yday = 0;
 Unix.tm_isdst = false}
型はsignatureで制限するからいいやと思ってみる
  cast.ml

名前のついたsignatureじゃなくていいじゃんと思ってみる
  reveal.ml

exitの返り値は多相型
# if true then "tak" else exit 1 ;;
- : string = "tak"
# exit;;
- : int -> 'a = <fun>
いつでもexitはできる。

aliasってぇ
型に別名を付けるとは!?
type t = a as 'moge
- : Parsetree.signature =
[{Parsetree.psig_desc =
   Parsetree.Psig_type
    [("t",
     {Parsetree.ptype_params = []; Parsetree.ptype_cstrs = [];
      Parsetree.ptype_kind = Parsetree.Ptype_abstract;
      Parsetree.ptype_manifest =
       Some
        {Parsetree.ptyp_desc =
          Parsetree.Ptyp_alias
           ({Parsetree.ptyp_desc =
              Parsetree.Ptyp_constr (Longident.Lident "a", []);
             Parsetree.ptyp_loc =
              {Location.loc_start = 59; Location.loc_end = 60;
               Location.loc_ghost = false}},
           "moge");
         Parsetree.ptyp_loc =
          {Location.loc_start = 59; Location.loc_end = 69;
           Location.loc_ghost = false}};
      Parsetree.ptype_variance = [];
      Parsetree.ptype_loc =
       {Location.loc_start = 54; Location.loc_end = 69;
        Location.loc_ghost = false}})];
  Parsetree.psig_loc =
   {Location.loc_start = 50; Location.loc_end = 69;
    Location.loc_ghost = false}}]

インタプリタocamlにバイトコードライブラリを読み込ませて遊ぶ
バイトコードライブラリに入っている関数の型を知りたいなら、ちゃんと対応する cmiファイルがあるパスを -I オプションで教えてあげないといけない。というよりも Unbound とかいわれるので、使えるようにするにはちゃんと教えなきゃならん。

## 作成
> ocamlc -I utils -I utils -I parsing `tsort_cmo .depend -g typing/typemod.ml -setE .cmo -e` -a -o typemod.cma 
> ocaml
        Objective Caml version 3.06

# #load "typemod.cma";;
# Typemod.type_module;;
Characters 0-19:
  Typemod.type_module;;
  ^^^^^^^^^^^^^^^^^^^
Unbound value Typemod.type_module 
## ↑そう、cmiファイルがないと分かってくれないのであった 
## (cmi ファイルはtyping ディレクトリ内にある)
> ocaml -I typing
        Objective Caml version 3.06

# #load "typemod.cma";;
# Typemod.type_module;;
- : Env.t -> Parsetree.module_expr -> Typedtree.module_expr = <fun>

あれ、そうなの
# let (f, g) = (fun x -> x, 1);;
Characters 14-27:
  let (f, g) = (fun x -> x, 1);;
                ^^^^^^^^^^^^^
This expression should not be a function, the expected type is
'a * 'b
ちがうぅ。,の結合力が -> より強いのであった
# let (f, g) = ((fun x -> x), 1)
  ;;
val f : 'a -> 'a = <fun>
val g : int = 1


疑問
Map.Make の結果の signature 内での型の宣言
# type key = String.t and + 'a t;;
type key = String.t
type +'a t
+ ってなんだろう?
マニュアルによると、型変数の前には + か - をつけることができて、それぞれ covariant , contravariant な変数であることを表している。もうちょっと型を勉強しよう (汗) manual016.html

あとで考えたいこと
  whynot_crawler.ml の型エラー

Caml-listより
# let ring l = let rec r = l @ r in r;;
Characters 25-30:
  let ring l = let rec r = l @ r in r;;
                           ^^^^^
This kind of expression is not allowed as right-hand side of `let rec'


あぁ、時間の無駄
ソースコードのファイル名は小文字で始めること。別のファイルで定義されたモジュールの名前を探すとき モジュールの名前の先頭を小文字にかえた文字列 にサフィックス .cmi をつけた名前のファイルを探す。 (マニュアルの 8.3)
ocamlyacc で action の かっこの閉じ忘れは良く分からないエラーメッセージを生む。 後ろの行にエラーがあるかのように報告される。

例外処理
finally がある
# try raise Not_found with finally -> print_endline "hello";;
hello
- : unit = ()
# try () with finally -> print_endline "hello";;
- : unit = ()

Value restriction
友人の説明をメモ。
そもそもリファレンスと多相型は相性が悪い。例えば

 let x = ref None;;

と書いたときに x の型を普通の多相型 'a option ref にしてしまって良いかというと そうではない。
 x := Some "mamewo" (* x : string option ref と思って代入 *)
 x := Some 1 (* x : int option ref と思って代入 *)

のように各代入時に型が具体化 (instantiate) されてしまったら、 x に代入 される値の型が静的に決まらないから。そこで、xには一つの型でしか使えない型 を与える (この場合'_a option ref 型を与えて、'_a はプログラム中一つの 型しか与えない) 。ここまではすぐに分かる。
さらに
 let f = List.map (fun x -> x) ;;

としちゃうと f には '_a list -> '_a list 型がつく。型多相じゃないの!? このプログラムはこんな↓プログラムがあるがために多相型になれないという被害に あっている。
 (* let の例 *)
 let g =
   let x = ref None in
     fun y -> let old = !x in x := Some y; old;;
 (* 関数適用の例 *)
 let h y = (fun x -> g x) y;;
この g は内部状態として リファレンスに触ってしまうので、'_a -> '_a option 型がつく。このような問題が起きるのは let で束縛する式が let や 関数適用のときに 起こりうる のでそのような場合には、型多相な部分をとりあえず単相型を与える という制限 (value restriction) を与えている。 (つまり制限が強くて、多相型 で使えるものをも制限してしまう) 束縛するものが定数、fun でつくった関数定数 だと多相型として扱えるので List.map の例では
let f y = List.map (fun x -> x) y
と書けば良い (η展開というらしい) 参考 ML演習 第 3 回

他のMLを使った感想

SML/NJ
  signatureの適用の仕方でopaqueかmanifestか決まる。 つまり ":>" を使うか? ":" を使うか? opaque_manifest.sml

使い分けをする(というかmanifestなsignature適用の)意義(自分勝手な解釈)
  1. 始めから抽象型にしてしまうと、インタプリタで読み込んだ時にデータの 中身がみえなくて困るから。デバッグ時はmanifestな制限を書けて中身は 見ながらデバッグしたい場合に使う(OcamlでModule言語を学んだ時にdebugの妨げに なるのでsignatureはうっとおしいという印象をもっていた)
  2. functorの引数として渡すためだけに作るstructureの型(signature)が functorの引数の型のsubtypeとなっているかを定義時にチェックしたい場合に使う。

Moscow ML
  help という関数に感動した。関数を名前で検索できる。moduleに期待。 ソースとドキュメントを含むtar.gzファイルを展開して example/modules/sieve.sml をみてみると、 first order module を見ることができる。Eratosthenesの篩を用いた素数表の例である。感動!! まだ 再帰的な structure の定義が可能。これらは Moscow ML の拡張。

SMLはキーワードが多くて書きにくい感じがする。structure に絡んでくるキーワードは とってもおおい。それにくらべ、関数定義も変数定義もletでできて、 module 内の みえ方を適用する signature の中見にまかせた (適用の仕方ではなくて) ocamlは すっきりしている。表現力の差はまだよくわかってないです。

OcamlのソースをNamazuに食べさせる
  mknmz --allow=".*.mli?" ~/download/ocaml-3.07/otherlibs/labltk ~/project/gui /usr/local/lib/ocaml/labltk/

ocamldebug
Emacs上でocamldebug を走らせられる camldebug.el という Emacs Lisp がある。自動的に エラーが出たところのファイルを開いてくれて便利。

サンプル

もちろん、無保証
  1. バグありレイトレ(球)
  2. ちょっとはまし 本体(proto.ml) / 数字のLexer(num_lexer.mll) / Makefile
  3. ガウスの消去法で連立方程式を解く。solve.ml
  4. かけ算表 mult_table.ml (imperative featuresを使った)
  5. なぞのプログラム scale_point.ml
  6. 状態をもつ関数 random.ml
  7. 式っていいよね random_round.ml
  8. Makefile の依存性をグラフ描画してみよう。オプションは未整備
      oldDrawMakefile.ml,
      Argモジュールを使ってちょっとオプションを整備した。拡張子を取り除いて 一つのノードにまとめる機能をつけた(-gオプション).depend,
      結果 グラフ
      コマンドライン
      > ocamlopt.opt str.cmx drawMakefile.ml -o drawMakefile
      > ./drawMakefile > result.vcg
      > xvcg result.vcg
    
      ディレクトリ構造をサブグラフに置き換えているので、ディレクトリを折り畳む(fold) ことができる!! vcgさまさま。
    もっとオプション整備したもの drawMakefile.ml
  9. 単純な再帰 hanoi.ml
      説明がたりない。(汗
  10. 個人的なライブラリ mylib.mlMakefile (要 OcamlMakefile)
  11. ocamlfindとgetoptのサンプル get.ml
  12. functionalではないが
    graph.mlmain1.ml, main2.ml 拡張アイデア
    ちょっとは実用的な実行例 使用方法
    tsort_cmo.ml オプションを整備。
    tsort コマンドをつかうともっと簡単に実現できるが、アルゴリズムを復習したかったので あった。
    c.f. ocamldoc & Graphviz
  13. 簡単な vcg ファイルのリーダー ocaml-vcg.tar.gz
      ocamlyacc, ocamllexのサンプル。
      デバッグするには lexbufを変数にバインドしておいて、このバッファの内部 状態を見ればよい
  14. とっても中途半端に書いたプログラム get_links.ml
  15. 謎ってことにして treat_type.ml
  16. パターンマッチ match.ml
  17. パターンマッチ2 match2.ml
  18. trie を書いてみた。ただのラベル付き木 trie.ml
  19. trie をちょっとつかってみる。ちょっとおもったのと違う。(汗 general_trie.ml
  20. いや、 'a list も 'a StringMap.t も同じことですよ。とあとで気づく。型多相なデータ型 isinstance.ml
  21. 自分でちょこっとつかう自家製ライブラリ ocaml.tar.gz
    make bcl してみよう。要 OcamlMakefile (tmpっていうディレクトリに展開されます)
  22. これは使う前に準備が必要。ocamlの parsing/printast.ml を用いて (Ocamlの) 構文木をダンプする。 printparsetree.ml
      mlファイル(実装)を渡す
  23. Emacs Lispを観察するためのアドホック (ちょっとだけドキュメントみつつ、ほとんどEmacs Lisp ソースをみてつくった) パーザー と static な call graph (make して ./main.bc ~/.xemacs.el とか) lisp-analysis.tar.gz
    lisp-analysis-2003-01-30.tar.gz
    例えばこんなグラフが描けるかもね
      mew で mew-summary-buffer-string に強く依存 (書き込み、読み込みする関数とそれが呼ぶ関数 mew-sumamry-buffer-string からの距離(枝の本数で定義)が2以内) する関数をグラフ化
       mewを通してみたところ、なんとか通った。ただエラーがでないだけで、 でもアドホック。マクロとか、日本語とか無視するので結果は正確でない。
  24. 頭が再帰的にまわらない瞬間 dfs.ml, typing.ml
  25. 意外と書いていなかったクライアントプログラム HttpClient-2003-02-08.tar.gz
  26. いつも通りの正規表現を用いたHTML処理 (リンクあつめ)linkCollecter-2003-02-08.tar.gz
  27. ネットなプログラムを中途半端にいくつか書いてみた POP, HTTP crawler-2003-07-01.tar.gz
  28. uncarry ってこういうことかぁ uncarry.ml
  29. ローカルなディレクトリと、URLとのアドホックな対応付け web_pwd.ml
  30. graphvizとvcgを比較してみた tools-2003-02-16.tar.gz
  31. だって、ほしかったんだもん。cmaのファイルの中身をちょっとみるプログラム。要 emitcode.cmi readlib.ml
      2003/ 3/ 6 あるモジュールがどのライブラリに入っているか検索する機能を追加した
      ん、 tools/objinfo.ml ってのと似てる。。。
  32. やっぱ役立つ readlib.ml Balloonがどのライブラリファイルに入っているか調べるのに使った。
  33. labltkサンプルプログラム集。biff+アンテナ と ticker gui-2004-04-05.tar.gz / gui-2004-04-30.tar.gz
    XEmacsバージョン (XEmacs 21.4.6で開発中) embiff-2004-01-05.tar.gz mbiff
    biff+アンテナについてのメモ Balloonを使って差出人を表示する。なぜか前回のBalloonが一瞬表示されて見苦しい。
      HTTP 1.0 じゃだめなようで HTTP 1.1 のリクエストを追加した
      Base64のデコード -> Mayahさんの ocamlkf を使って JIS2EUC変換をして日本語で書かれた差出人の名前を表示するようにした。
      中ボタンクリックでアンテナに関連したURL (あれば) を選択 (コピー) するようにした。
      OSによって性能に差があるので原因を調査中。とりあえず -exclusive オプションをつくってみた。これをつけて起動すると、socket を connect して close していない Thread が 高々一つになる。
      初期の biffだけバージョン mbiff.ml, pop.ml, mbiff.ml, pop.ml, mytcp.ml
    他のライブラリに依存するので autoconf マクロサンプルをいじって configure スクリプトを生成し、 configure でライブラリの存在確認をするようにした。

    tickerについてのメモ
      slashdot.jpなどののRSSをとってきてヘッドラインを表示する。(rdf/slash.ml, rdf/slash_with_time.ml)
      新しい記事は白い文字、更新前にもあった記事は灰色で表示するようにして新鮮さを表現した。
      配列アクセスに時々失敗するバグを修正した(多分) フォントを設定する -font オプションの追加。
      中ボタンクリックでいま表示している記事に関連したURLを選択する (コピーする) オプション -url の追加
      slashdot, asahi だけじゃなんなので CWN (Caml Weekly News) も加えてみた。
      記事一覧を表示してみた (マウスの右ボタンをおす)
      こんな感じ→ ticker image
  34. なるほど Base64のエンコードとデコード base64.ml
  35. expatを用いたXML処理 expat_test.ml
  36. なるほどUnicode。すごく遅いルーチン 要 JIS0208.TXT (変換表) utf.ml
    そりゃ、テーブル毎回つくってりゃおそいよ。というわけで、テーブル初期化を持ち上げて 速くした utf_fast.ml
      utfからEUCへの変換もかいた。どちらも変換テーブルの範疇に収まる 文字からなる文字列を想定していて完全ではない、んだろうな。
  37. not_permitted_in_right.ml
      なんで許されないのか聞いたような気がするけれど忘れた。調べてみよう
    というわけで、友達の value restriction の説明を聞いて納得。詳しくは ML 演習 第 3 回
  38. customized_parsing-2003-06-08.tar.gz (2.8M) (みつからない?)
      printast.mlをつかってAbstruct Syntax Treeを出力した (treat_implementation_debug.ml)
      make cleanしちゃうとちゃんとコンパイルできなくなるかも。
      要 mylib.cma (自作 graph ライブラリが必要)
      関数適用の関数の部分は式であることにふと気付き、修正した。 (treat_implementation.ml) がまだ完全ではない。
      ちょっとした整理。build.sh で drawOcamlGraph を作成可能にした customized_parsing-2003-09-27.tar.gz
  39. Ocamlファイルのprefixからそのprefixにマッチするソースファイル (ml, mliなど) の ファイル名を出力するプログラム getOcamlSources.pl
      と、その使い方
  40. VCGファイルから、そのファイル内で定義されているノードのタイトルまたはラベル名を出力する プログラム get_nodes.ml を追加した。graph-2003-06-09.tar.gz
    VCGから部分グラフをとってくるプログラムを書いた。必要なファイルを一つの ディレクトリにまとめた (汗) vcg-2003-10-03.tar.gz
      ちょっと拡張vcg-2003-12-31.tar.gz
  41. let rec!! scheme と ML の狭間 letrec.ml
      derive-y-combinator.scm
  42. 再帰的な Variant 型で再帰型を模倣。 counter.ml←これじゃ 型エラーなのでこうする→counter2.ml
      scheme では再帰型を許すのでさっくり書ける counter.scm
  43. mutable transparency.ml
  44. ocaml-3.07 が出てきたところで、 ocaml-3.06 の中身をじっくりみてみた。次はもちろん 3.07 を 見てみたいんだけど、このツール使えるかなぁ。ocamlXML.ml
      ocamlのソースを食わせるとなぞな XML を吐く。そのまえにビルドが大変.....。
      関連するツール OcamlFunctionViewer.jar, Web Start Version
  45. 簡単なリスト操作。高水準なプロセス起動!! commit_known_files.ml
      いまいちファイルのネーミングが良くない。
      cvs管理下で現在のディレクトリないのファイルで修正されたファイルを commit する。つまりまだcvsに追加されていないファイル(Unknown)の commit を避ける。
  46. 無意味に hello world hello.ml
  47. ocaml をダウンロード、ビルドする。中途半端〜。gui-*.tar.gz の中の http.ml url.ml などが必要 update_ocaml.ml
  48. Thread.delay と signal sig_and_sleep.ml
  49. HTMLの木構造の繰返し構造を見つけるための第一歩。ちょっとやってみたバージョン。HTMLの パーズもしている。 tree-2004-03-23.tar.gz
  50. connect でブロックするのを避ける。 nonblock.ml
      以下Unixのconnectと割り込みに関しての記事とかメモ
        Unix connect() and interrupted system calls まだ読んでません
        w3mは接続できない時にすぐにエラーを返すけど,lynxではそうではない
  51. char2line.ml
  52. そういえば epochから 2^30秒たってしまったのでした。これに影響されてしまった。。。(汗) ocamlのintの最大値は 2^30-1。ポインタと整数を見分けるGC用のタグに1ビットと符合に1ビット つかっているから(signed)
    float_of_int((int_of_string "1073946492") - (9*60*60)) なんてやってました (汗)
    対処。 (float_of_string "1073946492") -. (9.0 *. 60.0 *. 60.0) あはは
  53. labltkをしるにはTkのmanをダウンロードするのがいいのかぁ。Frx_after.idle は Tk の after idle に対応している。man after で説明が見られる。 tk_idle.ml
  54. ひさびさに書く。2年前の書きかけプログラムを完成させてみた。内容が同じファイルを一行にまとめて表示する samefile.ml
  55. 繰り返しをみつける。find_repetition.ml
  56. コンパイル出来ないプログラム。format4とは!? printf_test.ml
  57. ディレクトリ比較 & できるだけマージ merge_dir.ml
  58. Last modified: 日付 の部分を見つけて最終更新時刻を得る get_date.ml
  59. helloというテキストしか返さないHTTPサーバー。helloserver.ml
  60. ocaml-ssl (http://savonet.sourceforge.net/wiki/Savonet) を使ってみる (なくなった?) sslwget.ml
  61. helloというテキストを返すCGI hello_cgi.ml
  62. ファイルの比較 cmp.ml
  63. Talk Master IIの録音データのファイル名のつけ直し sort_timerrec.ml

作者: 増山隆 address
To Home
Valid HTML 4.01!