2016-04-14 16 views
1

Aşağıdaki marco hakkında sorularım var. kod parçacığı aşağıdaki düşünün:Makroyla dinamik işlev tanımı

defmodule Assertion do 

    defmacro __using__(_options) do 
    quote do 
     import unquote(__MODULE__) 
     Module.register_attribute __MODULE__, :tests, accumulate: true 
     @before_compile unquote(__MODULE__) 
    end 
    end 

    defmacro __before_compile__(_env) do 
    IO.puts "before compile" 
    quote do 
     def run, do: Assertion.Test.run(@tests, __MODULE__)  
    end 
    end 

    defmacro test(description, do: test_block) do 
    test_func = String.to_atom(description) 
    quote do 
     @tests {unquote(test_func), unquote(description)} 
     def unquote(test_func)(), do: unquote(test_block) 
    end 
    end 

    defmacro assert({operator, _, [lhs, rhs]}) do 
     IO.puts "assert" 
    quote bind_quoted: [operator: operator, lhs: lhs, rhs: rhs] do 
     Assertion.Test.assert(operator, lhs, rhs) 
    end 
    end 
end 

defmodule Assertion.Test do 
    def run(tests, module) do        
    Enum.each tests, fn {test_func, description} -> 
     case apply(module, test_func, []) do 
     :ok    -> IO.write "." 
     {:fail, reason} -> IO.puts """ 

      =============================================== 
      FAILURE: #{description} 
      =============================================== 
      #{reason} 
      """ 
     end 
    end 
    end              

    def assert(:==, lhs, rhs) when lhs == rhs do 
    :ok 
    end 
    def assert(:==, lhs, rhs) do 
    {:fail, """ 
     Expected:  #{lhs} 
     to be equal to: #{rhs} 
     """ 
    } 
    end 

    def assert(:>, lhs, rhs) when lhs > rhs do 
    :ok 
    end 
    def assert(:>, lhs, rhs) do 
    {:fail, """ 
     Expected:   #{lhs} 
     to be greater than: #{rhs} 
     """ 
    } 
    end 
end 

ve aşağıdaki modülü makro kullanın: hat üzerinde deney makro az

defmodule MathTest do 
    use Assertion 
    test "integers can be added and subtracted" do 
    assert 2 + 3 == 5 
    assert 5 - 5 == 10 
    end 
end 

Look

def unquote(test_func)(), do: unquote(test_block) 

Arayan modülüne burada bir işlevi enjekte integers can be added and subtracted adıyla ve daha önce bir atoma dönüştürülmüş.

Boşluklu bir işleve nasıl isim vermek mümkündür? Ve def unquote(test_func)'dan sonra () nedir?

cevap

2

quote ve unquote nedir, soyut bir sözdizimi ağacı oluşturur (AST). bir göz atın ve karşılaştırmak Sağlar

Biz adı atomun

olarak depolanır, yani fonksiyonun adı {:foo, [context: Elixir], []} olarak AST saklanır görebilirsiniz

iex(1)> quote do def foo(), do: :ok end 
{:def, [context: Elixir, import: Kernel], 
[{:foo, [context: Elixir], []}, [do: :ok]]} 

Normal fonksiyon tanımı burada unquote

iex(2)> foovar=:'foo var'    
:"foo var" 
iex(3)> quote do def unquote(foovar)(), do: :ok end 
{:def, [context: Elixir, import: Kernel], 
[{:"foo var", [context: Elixir], []}, [do: :ok]]} 

kullanarak fonksiyon tanımlama adı previousl olarak tuple adı kısmı ile AST saklanır Sen apply


() isteğe bağlıdır yerleşik işlevini kullanarak işlevlerin bu tür diyebiliriz :"foo var"

y tanımlı değişken. Kendim dahil olan bazı kişiler, bunları tüm işlev tanımlarında kullanmayı ve tutarlılık için işlev çağrılarını kullanmayı tercih ederler.


Not: Ben Erlang sözdizimi hakkında daha iyi gibi şeyler

Bir isim ve atomlar aynı görünmesini fonksiyondur ve onlar tür aynıdır. Yani Module.foo'un sadece apply(Module, :foo, []) için şeker olduğunu söyleyebiliriz. İksir işlev adlarında, isteğe bağlı parantez ile birleştirilen kodun, okunması ve hızlı bir şekilde anlaşılmasını zorlaştırabilen değişkenlere benzemesi.