二进制匹配中的变量问题

时间:2016-07-13 20:51:59

标签: elixir

鉴于此代码:

defmodule MyModule do
  def my_func(<< part::binary-size(size), rest::binary >>, size) do
    IO.puts(part)
  end

  def my_func_2(size, << part::binary-size(size), rest::binary >>) do
    IO.puts(part)
  end

  def my_func_3(size, << part::binary-size(size-1), rest::binary >>) do
    IO.puts(part)
  end
end

该模块不会编译。如果我注释掉除了一个以外的所有函数,我会得到不同的错误:

  1. 使用my_func/1进行编译时出现此错误:

    warning: variable "size" does not exist and is being expanded to "size()", please use parentheses to remove the ambiguity or change the variable name
      my_script.exs:2
    
    ** (CompileError) my_script.exs:2: size in bitstring expects an integer or a variable as argument, got: size()
        (elixir) src/elixir_bitstring.erl:52: :elixir_bitstring.expand_bit_info/5
        (elixir) src/elixir_bitstring.erl:29: :elixir_bitstring.expand_bitstr/4
        (elixir) src/elixir_bitstring.erl:10: :elixir_bitstring.expand/3
        (stdlib) lists.erl:1354: :lists.mapfoldl/3
    
  2. 使用my_func_2/1进行编译会给我带来另一个错误:

    ** (CompileError) my_script.exs:6: variable size@1 is unbound
        (stdlib) lists.erl:1338: :lists.foreach/2
        (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
    
  3. 使用my_func_3/1进行编译会给我一个不同的错误:

    ** (CompileError) my_script.exs:10: size in bitstring expects an integer or a variable as argument, got: :erlang.-(size, 1)
        (elixir) src/elixir_bitstring.erl:52: :elixir_bitstring.expand_bit_info/5
        (elixir) src/elixir_bitstring.erl:29: :elixir_bitstring.expand_bitstr/4
        (elixir) src/elixir_bitstring.erl:10: :elixir_bitstring.expand/3
        (stdlib) lists.erl:1354: :lists.mapfoldl/3
        (stdlib) lists.erl:1355: :lists.mapfoldl/3
    
  4. 有人可以向我解释所有错误吗?在erlang编译器级别发生了什么以及为什么?

1 个答案:

答案 0 :(得分:2)

Erlang和Elixir欺骗我们相信我们可以在每个数据结构上进行嵌套模式匹配,但二进制文件是一个例外。 Here你可以阅读

  

二进制模式不能嵌套(...)段具有以下一般语法:

     

Value:Size/TypeSpecifierList

     

匹配Value时,value必须是变量或整数,或浮点文字。表达是不允许的。

     

大小必须是整数文字或先前绑定的变量。以下是不允许的:

foo(N, <<X:N,T/binary>>) ->
  {X,T}.
     

两次出现的N无关。编译器会抱怨size字段中的N是未绑定的。

     

编写此示例的正确方法如下:

foo(N, Bin) ->
  <<X:N,T/binary>> = Bin,
  {X,T}.

Elixir也是如此。

所以在my_func编译时,不会尝试在第一个参数之后查看函数头,并且说没有名为size的变量。它认为它可能是一个函数,然后编译器出现错误,说size in bitstring expects an integer or a variable as argument

my_func_2中,事情变得棘手,因为变量存在于第一个参数中,但它尚未绑定。这正是上面Erlang文档的情况。您无法在尺寸上进行模式匹配。它必须是先前绑定的变量。名称size@1暗示您编译器不会将这两个视为同一个变量。它们内部有不同的名称。

my_func_3编译器中看到有一个减法因此它不会检查变量,并立即报告你不能在二进制模式匹配中使用表达式。

解决此限制的正确方法也在引用部分中。