書き換え可能なデータ構造:文字列

実は,文字列は書き換えが可能。たとえば次のような文字列があったとして:

# let s = "life";;
val s : string = "life"
# s;;
- : string = "life"

次のように書き換えができる。

# s.[2] <- 'k';;
- : unit = ()
# s;;
- : string = "like"

新しい値ができるのではなくて書き換わっている。

構造的等価性と物理的等価性

2つのデータを比べたとき,「値として等しいこと」を構造的等価性(structural equality)という。「値として」だけでなく,メモリ上の同じ位置を占めていることを物理的等価性(physical equality)という。

OCaml には構造的等価性を調べる演算子 = と物理的等価性を調べる演算子 == がある。

たとえば:

# "life" = "life";;
- : bool = true
# "life" == "life";;
- : bool = false

2つの “life” は値としては等しい(構造的等価性)けど,メモリ上の同じ位置は占めていないので,== による比較(物理的等価性)では false になっている。

一方,いったん変数を束縛してやった場合には:

# s = "life";;
- : bool = false
# s = s;;
- : bool = true
# s == s;;
- : bool = true

両方とも true になる。

ふーむ,このあたりは Ruby と同じだと思っておけばいいか。

書き換え可能なレコード

レコードを宣言するときにフィールド名の前に mutable キーワードをつけることで,書き換え可能にすることができる。

# type teacher = {name : string; mutable office : string};;
type teacher = { name : string; mutable office : string; }

これで office フィールドを書き換えることができるレコードができた。

具体的な値を作って:

# let t = {name = "Igarash"; office = "140"};;
val t : teacher = {name = "Igarash"; office = "140"}

書き換えてみよう。書き換えは文字列の場合と似ていて,. (ドット)の後にフィールド名を書く。

# t.office <- "142";;
- : unit = ()
# t;;
- : teacher = {name = "Igarash"; office = "142"}

ちゃんと書き換わった。

参照

書き換え可能なレコードの特殊な場合で,フィールド1つだけを持つ場合を伝統的に参照(reference)という。参照には特別な書き方がある。

まず,参照の生成には ref 関数。

# let p = ref 4;;
val p : int ref = {contents = 4}

ref は初期値を引数にとって,参照を返す。ref は多相的な関数なので引数の型は何でもいい。

# let s = ref "foo";;
val s : string ref = {contents = "foo"}

int ref とか string ref が参照の型。

参照の値を取り出すには前置演算子 ! を使う。

# !p;;
- : int = 4
# !s;;
- : string = "foo"

参照の書き換えは代入(assignment)と呼ぶことが多い。:= 演算子を使う。

# p := 7;;
- : unit = ()
# p;;
- : int ref = {contents = 7}

参照は型が決まっているので,違う型の値は代入できない。

# s := 77;;
Characters 5-7:
s := 77;;
^^
This expression has type int but is here used with type string