この演習のポイントは、クロージャーを使用できるようにすることだと思います。たとえば、ファイル内の次の OCaml 関数のペアを考えてみましょうfun-dict.ml
:
let empty (_ : string) : int = 0
let add d k v = fun k' -> if k = k' then v else d k'
次に、OCaml プロンプトで次の操作を実行できます。
# #use "fun-dict.ml";;
val empty : string -> int =
val add : ('a -> 'b) -> 'a -> 'b -> 'a -> 'b =
# let d = add empty "foo" 10;;
val d : string -> int =
# d "bar";; (* Since our dictionary is a function we simply call with a
string to look up a value *)
- : int = 0 (* We never added "bar" so we get 0 *)
# d "foo";;
- : int = 10 (* We added "foo" -> 10 *)
この例では、ディクショナリは値に対するstring
キーの関数int
です。このempty
関数は、すべてのキーを にマップする辞書です0
。add 関数は、1 つの引数 (キー) を取るクロージャを作成します。ここでのディクショナリの定義は、キーから値への関数であるため、このクロージャはディクショナリです。k'
(クロージャーパラメーター) が追加されたばかりのキーが= k
どこにあるかを確認します。k
存在する場合は新しい値を返し、そうでない場合は古い辞書を呼び出します。
チェーン内の次の辞書(関数)を閉じることにより、コンスセルではなくチェーンされたクロージャーのリストを効果的に持っています)。
余分な演習ですが、この辞書からキーを削除するにはどうすればよいですか?
編集:閉鎖とは何ですか?
クロージャは、それが作成されたスコープから変数 (名前) を参照する関数です。では、それはどういう意味ですか?
add
私たちの機能を考えてみましょう。関数を返します
fun k' -> if k = k' then v else d k
その関数だけを見ると、定義されていないd
、k
、およびの 3 つの名前がありv
ます。それらが何であるかを理解するには、囲んでいるスコープ、つまり のスコープを調べる必要がありadd
ます。私たちが見つけた場所
let add d k v = ...
そのため、新しい関数が返された後でも、add
その関数は追加する引数を引き続き参照します。したがって、クロージャーは、意味を持つために何らかの外部スコープによって閉じられなければならない関数です。