大規模なコレクションの非破壊的な操作が関数型プログラミングでどのように実装されているかを理解しようとしています。変更されていないものも含め、すべての要素がメモリ内で複製される完全に新しいコレクションを作成する必要なく、単一の要素を変更または削除する方法。(元のコレクションがガベージ コレクションされたとしても、そのようなコレクションのメモリ フットプリントと一般的なパフォーマンスはひどいものになると思います。)
これは私が今までに得た距離です:
insert
F# を使用して、変更されていない要素をすべて複製することなく、リストを 2 つの部分に分割し、間に新しい要素を導入する関数を考え出しました。
// return a list without its first n elements:
// (helper function)
let rec skip list n =
if n = 0 then
list
else
match list with
| [] -> []
| x::xs -> skip xs (n-1)
// return only the first n elements of a list:
// (helper function)
let rec take list n =
if n = 0 then
[]
else
match list with
| [] -> []
| x::xs -> x::(take xs (n-1))
// insert a value into a list at the specified zero-based position:
let insert list position value =
(take list position) @ [value] @ (skip list position)
次に、.NET を使用して、元のリストのオブジェクトが新しいリストで「リサイクル」されているかどうかを確認しましたObject.ReferenceEquals
。
open System
let (===) x y =
Object.ReferenceEquals(x, y)
let x = Some(42)
let L = [Some(0); x; Some(43)]
let M = Some(1) |> insert L 1
次の 3 つの式はすべて に評価されtrue
、 によって参照される値がリストおよびのx
両方で再利用されることを示します。メモリ内にこの値のコピーが 1 つしかないこと:L
M
L.[1] === x
M.[2] === x
L.[1] === M.[2]
私の質問:
関数型プログラミング言語は通常、値を新しいメモリの場所に複製するのではなく、値を再利用しますか?それとも、F# の動作で幸運だったのでしょうか? 前者を仮定すると、関数型プログラミングでコレクションのメモリ効率の高い編集をどのように実装できるのでしょうか?
(ちなみに、私はChris Okasaki の著書Purely Functional datastructuresについては知っていますが、まだ十分に読む時間がありませんでした。)