(在Elixir中给出的例子.)
假设我有以下代码,
x = {1, 2}
a1 = {"a", {1, 2}}
a2 = {"a", {1, 2}}
a3 = {"a", x}
Run Code Online (Sandbox Code Playgroud)
据我所知,{1, 2}在不同的内存位置创建了三个元组.
使用运算符==或===比较任何a变量总是返回true.这是可以预料的,因为这两个运算符仅在比较数字类型时有所不同(即,与之1 == 1.0不同1 === 1.0).
所以,然后我尝试通过模式匹配比较结构,使用以下模块(严格创建以测试我的情况),
defmodule Test do
def same?({x, y}, {x, y}), do: true
def same?(_, _), do: false
end
Run Code Online (Sandbox Code Playgroud)
但是召唤Test.same?(a1, a3)也会回来true.
如何使用指针相等性比较两个结构,以便我可以确定它们在内存中是否是相同的结构?
谢谢
leg*_*cia 14
没有"正式"的方法可以做到这一点,我会说,如果你认为你真的需要这样做,你做错了什么,应该问另一个问题,关于如何实现你想要实现的目标.因此,这个答案是以游戏和探索的精神提供的,希望它传播一些关于Erlang/Elixir VM的有趣知识.
有一个函数,erts_debug:size/1告诉你Erlang/Elixir术语占用多少内存"单词". 此表告诉您各个术语使用了多少个单词.特别是,元组使用1个字,每个元素加1个字,加上任何"非立即"元素的存储空间.我们使用小整数作为元素,它们是"即时的",因此是"自由的".所以这个检查:
> :erts_debug.size({1,2})
3
Run Code Online (Sandbox Code Playgroud)
现在让我们创建一个包含两个元组的元组:
> :erts_debug.size({{1,2}, {1,2}})
9
Run Code Online (Sandbox Code Playgroud)
这是有道理的:两个内部元组各3个字,外部元组是1 + 2个单词,总共9个单词.
但是如果我们将内部元组放在一个变量中呢?
> x = {1, 2}
{1, 2}
> :erts_debug.size({x, x})
6
Run Code Online (Sandbox Code Playgroud)
看,我们保存了3个字!那是因为内容x只计算一次; 外部元组指向两次相同的内部元组.
那么让我们写一个为我们做这个的小功能:
defmodule Test do
def same?(a, b) do
a_size = :erts_debug.size(a)
b_size = :erts_debug.size(b)
# Three words for the outer tuple; everything else is shared
a_size == b_size and :erts_debug.size({a,b}) == a_size + 3
end
end
Run Code Online (Sandbox Code Playgroud)
系统工作?似乎是:
> Test.same? x, {1,2}
false
> Test.same? x, x
true
Run Code Online (Sandbox Code Playgroud)
目标完成了!
但是,假设我们试图从编译模块中的另一个函数调用此函数,而不是从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
Run Code Online (Sandbox Code Playgroud)
打印:
> Test.try_it
a1 and a2 same? true
a1 and a3 same? true
a3 and a2 same? true
Run Code Online (Sandbox Code Playgroud)
这是因为编译器足够智能,可以看到这些文字是相同的,并在编译时将它们合并为一个术语.
请注意,当术语发送到另一个进程,或存储在ETS表中/从ETS表中检索时,这种术语共享将丢失.有关详细信息,请参阅"Erlang效率指南"的"处理消息"部分.
让我回答我的问题:
开发人员无需明确地进行指针比较,因为Elixir已经在内部,模式匹配和运算符==以及===(通过相应的Erlang运算符)执行此操作.
例如,给定
a1 = {0, {1, 2}}
a2 = {1, {1, 2}}
x = {a1, a2}
s = {1, 2}
b1 = {0, s}
b2 = {1, s}
y = {b1, b2}
Run Code Online (Sandbox Code Playgroud)
在IEx我们有
Interactive Elixir (1.7.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> a1 = {0, {1, 2}}
{0, {1, 2}}
iex(2)> a2 = {1, {1, 2}}
{1, {1, 2}}
iex(3)> x = {a1, a2}
{{0, {1, 2}}, {1, {1, 2}}}
iex(4)> s = {1, 2}
{1, 2}
iex(5)> b1 = {0, s}
{0, {1, 2}}
iex(6)> b2 = {1, s}
{1, {1, 2}}
iex(7)> y = {b1, b2}
{{0, {1, 2}}, {1, {1, 2}}}
iex(8)> :erts_debug.size(x)
15
iex(9)> :erts_debug.size(y)
12
iex(10)> x == y
true
iex(11)> x === y
true
Run Code Online (Sandbox Code Playgroud)
也就是说,x并且y内容相等,但内存不同,因为y占用的内存少于x内部共享子结构的内存s.
总之,==并且===做内容和指针比较.指针比较是Erlang避免在比较两侧遍历相同子结构的最有效方式,从而为大型共享子结构节省了大量时间.
现在,如果跨两个结构的结构复制是一个问题,就像它们是从具有相似内容的两个大文件加载时那么,必须将它们压缩成两个新的结构,共享它们内容相等的部分.这是的情况a1和a2压缩为b1和b2.