スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#入門第29回(命令型プログラミング(1)mutable とref cell)

今回のお題は「命令型プログラミング(1)mutable とref cell 」です。
 
まずは宿題の答えから
宿題
List.foldと同じ働きをする関数myListFoldを定義してみてください。
 
(答え)
let rec myListFold f accum lst =
    match lst with
    |[] -> accum
    | hd :: tl -> myListFold f (f accum hd) tl
    
種を「種と要素に関数fを適用したもの」に置き換えていくだけです。
 
ここで、値型と参照型の復習をしておきたいと思います。
まずは、メモリの復習から。
メモリは大きくスタックと呼ばれる部分と、ヒープと呼ばれる部分に分かれます。
スタックというのは、局所変数(関数内で使われる一時的変数で、関数の実行が終わると消える)が、記憶される部分です。(メモリ容量はそれほど大きくありません。)
ヒープはそれ以外のものが、記憶される部分で、メモリ容量はスタックに比べて巨大です。
 
値には、値自体がスタックにおかれるタイプと、場所情報(どこに本体のデーターがあるか)だけがスタックにおかれて、本体部分はヒープにおかれるタイプの2種類があります。
前者を値型、後者を参照型といいます。
例えばint型とかfloat型は値型で、string,list型は参照型です。
例えば次のコードを見てください。
 
let mutable a =3
let mutable s = "three"
printfn "%d" a //(1)
printfn "%s" s //(2)
 
プログラムする側にとっては今までのところ、int型もstring型も変わるところがないのですが、コンピューター側としては、(1)の部分では、「aは値型なので、スタックにおかれた値をそのまま表示すればよいぞ」、(2)の部分では、「sは参照型なので、スタックにおかれた値(場所情報)を調べて、ヒープ上の、その場所におかれた値を持ってきて、表示するぞ」。というように、異なる処理がなされているのです。
参照型は、場所情報をとって、さらにその場所にあるデーターをとってくるという「ひと手間」が、余分にかかっているのです。
 
 
値型であるか参照型であるかに関係なく、mutableにするには、mutableキーワードを識別子の頭につけます。
値を変更するには <- を使います。
 

 
let mutable a = 3
let mutable s = "abc"
a <- 4
s <- "de"
 
これで、aの値は4、sの値は"de"となります。
 
ただmutableな値には、「関数内部でキャプチャーできない」という制限があります。
 
関数内部でのキャプチャーについては、別の機会に書きたいと思いますが、上の問題を回避するにはmutableな値を使う代わりに、ref cell(参照セル)を使います。
言葉のイメージの通り、値を包みこみ(セルは細胞という意味です)参照型のように使えるようにしたものです。参照型と同様に、値はヒープに置かれます。
 
ref というのは関数で、次のように定義されています。
 
> ref ;;
val it : ('a -> 'a ref) = <fun:clo@0>
 
よって、
 
> let x = ref 7;;
val x : int ref = {contents = 7;}
 
とすると、int ref型の値がxに束縛されます。
x自体は、ヒープ上のセルを指し示すポインタです。(ヒープ上の位置を覚えています。)
指し示した先の中身を取り出すには、識別子の前に!をつけるか、識別子のあとに、.Valueをつけます。
 
> !x;;
val it : int = 7
 
> x.Value;;
val it : int = 7
 
指し示した先の中身の値を変更するには:=を使います。
 
> x := !x + 2;;
val it : unit = () 
 
中身の値を調べます。
 
> !x;;
val it : int = 9
 
ここで、別の識別子yを導入しxで束縛してみます。
 
> let y = x;; //x自体はポインタなので、こうするとyもxと同じところを
              //指し示すことになる
val y : int ref = {contents = 9;}
 
次に y := 11としてみます
 
> y := 11;;
val it : unit = ()
 
> !x;;
val it : int = 11 
 
上はxもyも指し示す先が同じことからの、帰結です。
 
少しまとめると、
 
let p = ref x とすると
関数ref xはxを包みこんでヒープ上に記憶させ、そこへのポインタpを返すので、let p =  で、それをxに束縛します。!pまたはp.Valueでxを返し、xを変るにはx:=の形を使います。 
 
ただメモリのところで紹介した参照型と混乱しやすいと思うので、一つ例を挙げます。
例えば参照型であるstring型に、上と同じようなことを試みてみます。
 
> let mutable s = "seven";;
val mutable s : string = "seven"
 
> let mutable y = s;; //(*)
val mutable y : string = "seven"
 
> y <- "nine";;
val it : unit = ()
 
> s;;
val it : string = "seven"
 
> y;;
val it : string = "nine"
 
ということで、参照型でmutableだからといっても、上のsとかyとかは、ポインタを表すのではなく、値を表すします。よって
 
let mutable y = s
 
でおこなわれるのは、「sの値と同じ値をもつmutableな値yを定義する」という作業で、ref cellの場合のような、「値を指し示すポインタを同じにする」という作業ではありません。
とかく、宣言型プログラムが混じって来ると、「識別子が値を表すか、ポインタを表すか」ということを常に意識していないと、バグを生み出しかねないので、注意が必要です。
また、複数の別名のポインタが、同じヒープ上のデータを指し示している状況を作り出してしまうと、(先ほどのref cell のxとyがこの例)、一つのポインタ経由で、指し示す先の内容を変更してしまうと、別名のポインタの先も同様に変更されたことになりますので、(同じものを指していることからの帰結です。)混乱の元になります。
 
宿題 次のプログラムを実行すると、なんと表示されるでしょうか?
 
let testSub rl =
    rl := 1 :: !rl
 
let test  () =
    let refLst = ref [2;3]
    let t = refLst
    testSub (t)
    printfn "%A" refLst.Value
 
test ()
 
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。