基本的なデータ、文法等
変数束縛
範囲を指定せず定義する「define」と、範囲を指定して定義する「let」「let*」「letrec」があります。
(letrecは再帰用です)
let式、let*式、letrec式による変数束縛はその式の中だけで参照可能です。
(let
(
(foo 100)
(bar 4)
)
(print_int (* foo bar))
)
(print_newline)
(define baz 3.2)
(print_float (*. baz 2.0))
(print_newline)
let式とlet*式は変数束縛が一つだった場合は実質的に同じ意味になりますが、
変数束縛が複数だった場合に異なる意味になります。
let*は2番目以降の変数束縛の対象となる式から、それ以前の変数を参照可能です。
;; 以下の例では変数bazはfooとbazの合計となる。
;; これはlet*式では問題ないが、let式では未定義の変数を参照しようとするためエラーとなる。
(let*
(
(foo 3)
(bar 5)
(baz (+ foo bar))
)
(print_int baz)
)
関数
「(fun (arg1 arg2 ...) exp1 exp2 ...)」という形式になります。
(define f1 (fun (x) (* x x)))
(print_int (f1 3))
(print_newline)
(List.iter
(fun (x)
(print_int x)
(print_string ", "))
'(1 2 3 4 5))
(print_newline)
関数の生成と名前付けを同時に行うOCamlの「let function_name arg1 arg2 ... -> exp1 exp2 ...」に
対応した省略形はありませんが、代わりにCommon Lisp風のdefunをマクロで定義してあります。
(common.scmで定義してあります。)
(require "common.scm")
(defun plus_xy (x y) (+ x y))
型を指定する場合は変数名の後ろに記述します。
let式の場合は型を指定するものと指定しないものの混在も可能です。
(define f "int -> int" (fun (x) (+ x x)))
(let (
(foo "int" 10)
(bar 20)
(baz "string" "test string")
)
(Printf.printf "foo:%d, bar:%d, baz:%s\n" foo bar baz)
)
また、let式に名前をつけることで関数化する機能(named let)もあります。
この例ではlet式にloopという名前をつけて呼び出しています。
「((i 10) (ls '()))」の部分は初期値の設定で、これらは呼び出す度に引数によって置き換えられます。
(List.iter
(fun (x) (print_int x) (print_string ", "))
(let
loop ((i 10) (ls '()))
(if (< i 0)
(List.rev ls)
(loop (- i 1) (:: i ls))
)
))
コメント
3種類のコメントが利用可能です。
伝統的なSchemeの一行コメント、R6RSで追加された複数行コメント、
直後の式をコメントアウトするS式コメントです。
; 一行コメントです、セミコロンから行末までがコメントとして処理されます。
;; セミコロンは機能的には一つで問題ありませんが、Lispでは慣習的に重要度に応じて
;; 複数並べることが多いそうです。
#|
複数行コメントです。#|から|#までがコメントとして処理されます。(SRFI 30)
複数行コメントはネスト可能なので入れ子になっていても問題ありません。
|#
;; S式コメントです。#;の直後の式をコメントアウトします。(SRFI 62)
;; 2つの式が並んでいますが、S式コメントは直後の式をコメントアウトするので、
;; 2番目の式はコメントアウトされません。
#;(print_string "s-expression comment") (print_string "hello world")
;; S式コメントはリテラルやシンボルに対しても有効です。
;; 以下は前者の文字列リテラルがコメントアウトされ、
;; 後者はコメントアウトされずに残ります。
(print_string #;"出力されない" "出力される")
データ構造
;; リスト
(list 1 2 3 4 5)
;; 以下は上記と等価
'(1 2 3 4 5)
;; 配列
(array 1 2 3 4 5)
;; 以下は上記と等価
'#(1 2 3 4 5)
;; タプル
(tuple 1 2)
;; let式で以下のように記述するとタプルの展開を行います
(let
(
((width height) (tuple 480 272))
)
(print_int width)
(print_string " : ")
(print_int height)
)
;; レコード
(type record_sample "{ name: string; mutable age: int; }")
(let
(
(user_data (record (name "foo") (age 28)))
)
(<- user_data.age 32)
(print_string "name: ")
(print_string user_data.name)
(print_newline)
(print_string "age: ")
(print_int user_data.age)
(print_newline)
)
;; レコードの生成はリーダーマクロを利用して以下のような記法も
;; 可能です。
;; (リーダーマクロはcommon.scmで定義されています)
(require "common.scm")
(let
(
(user_data { (name "foo") (age 28) })
)
(<- user_data.age 32)
(print_string "name: ")
(print_string user_data.name)
(print_newline)
(print_string "age: ")
(print_int user_data.age)
(print_newline)
)
特殊な式
OCamlコードの埋め込み
(ocaml "文字列リテラル")で埋め込まれた文字列はそのままOCamlコードとして扱われます。
場合に応じて使い分けてください。
(define ar (ocaml "[| 10; 20; 30; 40; 50 |]"))
(Array.iter
(fun (x)
(print_int x)
(print_newline)
)
ar)
実行前計算
(preprocess 式)で式を予め計算し、その結果を挿入します。
計算はSchemeインタプリタによって行われます。
;; 前者が実行時に(* 10 10)を計算するのに対し、
;; 後者はコンパイル前に(* 10 10)を計算済みとなります。
(print_int (* 10 10))
(print_int (preprocess (* 10 10)))
その他、コード例
とりあえずコード例だけ、後日追記します
例外
(exception Test_failure)
;; catch節にmatch
(try
(match
(Test_failure () (print_string "Test_failure!!\n"))
(_ () (print_string "unknown error\n"))
)
;; ~ 何らかの処理 ~
(raise Test_failure)
)
;; catch節に関数
(try
(fun (except)
(print_string "error\n")
)
;; ~ 何らかの処理 ~
(raise Test_failure)
)
class
(class test_class ()
(object (self)
(method output () (print_string "test method!!\n"))
)
)
(let ((obj (new test_class)))
(obj#output)
)
module
(module Foo (struct
(define bar (fun () (print_string "Foo.bar!!\n")))
))
(Foo.bar)
(module L List)
(L.iter
(fun (x) (print_int x) (print_newline))
'(1 2 3 4 5))
loop
;; 1から10まで表示 (for)
(for (i 1 10)
(print_int i)
(print_newline)
)
;; 1から10まで表示 (while)
(let ((i (ref 1)))
(while (<= !i 10)
(print_int !i)
(print_newline)
(:= i (+ !i 1))
)
)