elixir - Elixir 中的所有内容都是引用类型吗?

标签 elixir

阅读有关 Elixir 不变性以及它如何尽可能避免内存复制的文章,这似乎是唯一可能的解释,但我还没有在任何地方看到它的明确说明。 例如,当将一个新元素附加到列表时,它被描述为该操作恰好需要 n 个步骤,其中 n 是列表的长度,但它只是浅拷贝原始元素。所以我的假设是:

假设我们有一个列表 [1, 2, 3, 4]。它由 4 个节点组成,但节点本身不包含值。 1、2、3、4 存储在其他地方,每个节点包含对相应值的引用,以及对下一个节点的引用。当我们将 10 添加到列表中时,不仅创建了一个新节点,而且实际上创建了五个新节点,因为在原始列表中,数字 4 的节点必须将“nil”作为其“下一个”引用,但在新列表中,节点因为数字 4 必须有 'next' 指向为数字 10 新创建的节点。所以它不能被重用。这反过来意味着数字 3 的节点也不能被重用,等等。所以创建了五个新节点,但前四个是浅拷贝,这意味着它们指向与原始节点完全相同的内存位置节点。

我刚才描述的有道理吗?

最佳答案

how it avoids memory copying wherever possible

我认为您指的是结构共享,这是 persistent data structures 的属性 不仅是不可变的,而且能够通过生成修改后的副本来有效地操作它们 它重用了底层结构的一部分。

链表就是一个例子,但只支持高效的前置。正如你所指出的, 附加需要从头开始完全重建列表。没有重用(结构共享) 可能。

l1 = [1, 2, 3] 实际上是 [1 | 的语法糖[2 | [3 | []]]].

l2 = l1++ [4] ([1, 2, 3, 4]) 实际上是 [1 | [2 | [3 | [4 | []]]]] 并且不包含/重复使用 l1

另一方面,l3 = [0 | l1][0]++ l1[0 | [1 | [2 | [3 | []]]]] 确实重用了 l1

可视化它如何指向引擎盖下的相同结构的一种方法:

iex> Enum.take(l2, 3)
[1, 2, 3]
iex> Enum.drop(l3, 1)
[1, 2, 3]
iex> :erts_debug.same Enum.take(l2, 3), l1  # no structural sharing
false
iex> :erts_debug.same Enum.drop(l3, 1), l1  # structural sharing
true

通过连续前置(在递归/归约中)构建列表并最终反转结果是很常见的。

# artifical implementation of Enum.map
Enum.reduce(enum, [], fn x, acc -> [f.(x) | acc] end) |> Enum.reverse()

section in the Erlang efficiency guide提供了很好的解释。

其他数据结构,例如 Maps:array:gb_trees 也利用结构共享: 例如在执行 Map.put(my_big_map, 0, 0) 时,引擎盖下的树结构(技术上是 hash array mapped trie ) 大部分都可以重复使用,无需对 my_big_map 进行深拷贝。

iex> my_big_map = Map.new 1..10000, fn x -> {x, x} end
iex> new_map = Map.put(my_big_map, 0, 0)
iex> :erts_debug.size my_big_map
37898
iex> :erts_debug.size [my_big_map, new_map]  # almost no copy needed
37958

关于elixir - Elixir 中的所有内容都是引用类型吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67556658/

相关文章:

Elixir - 单独替换特定键值的映射

Elixir:(FunctionClauseError)没有函数子句匹配

erlang - 转换包含抽象树的文件

elixir - 使用 Postgrex 和 Ecto 准备好的语句

测试嵌套文件夹中的 Phoenix Controller

elixir - 在后台发出 http 请求并更新客户端

elixir - Elixir 中的 is_module 保护子句

elixir - 作为参数的 map 列表

sockets - 我无法获得通过在不老长寿药, Phoenix 中的存在而加入 channel 的所有用户的列表

elixir - 迭代 map 并更新 Elixir 中的值(value)?