没有“官方”的方法来做到这一点,如果您认为您实际上需要这样做,我会说您正在做错事情,应该询问另一个关于如何实现您想要实现的目标的问题。因此,本答案以玩味和探索的精神提供,希望它能传播一些有关Erlang/Elixir VM的有趣知识。
有一个函数
erts_debug:size/1
,它告诉您一个Erlang/Elixir术语占用了多少内存“字”。
这张表格 告诉您各种术语使用多少字。特别是一个元组使用1个字,加上每个元素的1个字,再加上任何“非立即”元素的存储空间。我们正在使用小整数作为元素,它们是“立即数”因此是“自由”的。所以这个检查通过:
> :erts_debug.size({1,2})
3
现在让我们创建一个包含这两个元组的元组:
> :erts_debug.size({{1,2}, {1,2}})
9
有道理:两个内部元组各包含3个单词,而外部元组有1+2个单词,总共是9个单词。
但如果我们把内部元组放到一个变量中怎么办?
> x = {1, 2}
{1, 2}
> :erts_debug.size({x, x})
6
看,我们节省了3个单词!这是因为x
的内容只计算一次;外部元组在两次指向同一内部元组。
因此,让我们编写一个小函数来帮助我们实现这一点:
defmodule Test do
def same?(a, b) do
a_size = :erts_debug.size(a)
b_size = :erts_debug.size(b)
a_size == b_size and :erts_debug.size({a,b}) == a_size + 3
end
end
系统工作正常?看起来是的:
> Test.same? x, {1,2}
false
> Test.same? x, x
true
目标达成!
但是如果我们试图从已编译的模块中的另一个函数中调用此函数,而不是从iex shell中调用:
def try_it() do
x = {1, 2}
a1 = {"a", {1, 2}}
a2 = {"a", {1, 2}}
a3 = {"a", x}
IO.puts "a1 and a2 same? #{same?(a1,a2)}"
IO.puts "a1 and a3 same? #{same?(a1,a3)}"
IO.puts "a3 and a2 same? #{same?(a3,a2)}"
end
那会打印:
> Test.try_it
a1 and a2 same? true
a1 and a3 same? true
a3 and a2 same? true
那是因为编译器足够聪明,能够看到这些文字相等,并在编译时将它们合并为一个项。
请注意,当术语被发送到另一个进程或存储在/从ETS表中检索时,此共享术语会丢失。有关详细信息,请参见Erlang效率指南的进程消息部分。