代码之家  ›  专栏  ›  技术社区  ›  mljrg

如何通过Elixir/Erlang中的指针等式比较两种结构

  •  4
  • mljrg  · 技术社区  · 6 年前

    (以长生不老药为例。)

    假设我有以下代码,

    x = {1, 2}
    a1 = {"a", {1, 2}}
    a2 = {"a", {1, 2}}
    a3 = {"a", x}
    

    据我所知,这创造了三个元组 {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
    

    但是打电话 Test.same?(a1, a3) 还返回 是的

    如何使用指针相等来比较两个结构,以便确定它们在内存中是否是相同的结构?

    谢谢

    4 回复  |  直到 6 年前
        1
  •  15
  •   legoscia    6 年前

    没有“官方”的方法可以做到这一点,如果你真的认为 需要


    有一个函数, erts_debug:size/1 ,它告诉您一个二郎/长生不老药术语占用了多少内存“单词”。 This table

    > :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
    

    看,我们省了三个字!那是因为 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
    

    系统工作?似乎是:

    > 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表中或从ETS表中检索时,术语的这种共享将丢失。看到了吗 the Process Messages section of the Erlang Efficiency Guide

        2
  •  8
  •   José Valim    5 年前

    Erlang/otp22(可能更早)提供 :erts_debug.same/2 ,这将允许您执行所需的内存指针测试。但是,请注意,该函数没有文档记录,位于名为 erts_debug ,所以您应该只依赖它进行调试和测试,而不应该依赖于生产代码。

    在我使用Erlang/Elixir将近9年的时间里,我只使用过一次,这是为了测试我们在exto中没有不必要地分配结构。这里是 the commit for reference

        3
  •  7
  •   mljrg    6 年前

    让我回答我的问题:

    开发人员不需要显式地进行指针比较,因为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}
    

    在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
    

    也就是说, x y 因为它内部共享子结构 s .

    总之, == ===

    a1 a2 被压缩成 b1 b2 .

        4
  •  2
  •   Aleksei Matiushkin    6 年前

    据我所知,它创造了三个元组 {1, 2} 在不同的记忆位置。

    值得一提的是,这是可能的,因为一切都是不变的。

    另外,如果你发现自己完成了上面的任务,那你就完全错了。

        5
  •  2
  •   Nathan Ripert Guillaume Milan    6 年前

    你好像到不了那个地方 memory location of a variable in erlang :我认为这是本专题的一个关键概念。因此, 只能比较数据,不能比较指向这些数据的指针 .