我试图弄清楚如何在函数式编程中实现对大型集合的非破坏性操作,即。如何在不必创建一个全新的集合的情况下更改或删除单个元素,其中所有元素,即使是未修改的元素,都将在内存中复制。(即使原始集合会被垃圾回收,我预计此类集合的内存占用和一般性能也会很糟糕。)
这是我到现在为止的距离:
使用 F#,我想出了一个函数insert
,它将列表分成两部分并在中间引入一个新元素,似乎没有克隆所有未更改的元素:
// 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
以下三个表达式都计算为true
,表示 引用的值在列表和x
中都被重用,即。内存中只有此值的 1 个副本:L
M
L.[1] === x
M.[2] === x
L.[1] === M.[2]
我的问题:
函数式编程语言是否通常重用值而不是将它们克隆到新的内存位置,或者我只是对 F# 的行为感到幸运?假设前者,这是否可以在函数式编程中实现合理的内存高效编辑集合?
(顺便说一句:我知道Chris Okasaki 的书Purely functional data structures,但还没有时间彻底阅读它。)