2014-07-16 26 views
12

Bazı tuşları isteğe bağlı olan bir çok tuşa sahip bir harita alan bir fonksiyonum var. İsteğe bağlı anahtarların bir şeylere varsayılan olarak izin verirken haritayı anlama işlev imzasını nasıl yazabilirim?İsteğe bağlı tuşları ile desen eşleştirme haritası

def handle_my_map(%{text: text, 
        print_times: print_times, # this I want to default to 2 
        color: color # this I want to default to "Blue" 
    }) do 
    Enum.each(1..print_times, fn (_) -> IO.puts ["(", color, "): ", text] end) 
end 

Test.handle_my_map(%{text: "here", print_times: 5, color: "Red"}) 
# (Red): here 
# (Red): here 
# (Red): here 
# (Red): here 
# (Red): here 

handle_my_map(%{text: "there"}) 
# => MatchError! 

ben olmak istiyorum: Ruby'nin anahtar kelime argümanlar gibi

handle_my_map(%{text: "where", print_times: 3}) 
# (Blue): where 
# (Blue): where 
# (Blue): where 
handle_my_map(%{text: "there"}) 
# (Blue): there 
# (Blue): there 

şey:

def handle_my_map(text: nil, print_times: 2, color: 'Blue') 
+0

bu 'handle_my_map/1' bir parametre alır? –

+0

@ Зелёный - evet, tek bir harita parametresi –

cevap

10

Sen Map.merge/2 kullanabilirsiniz:

defmodule Handler do 
    @defaults %{print_times: 2, color: "Blue"} 

    def handle_my_map(map) do 
    %{text: text, print_times: times, color: color} = merge_defaults(map) 
    Enum.each(1..times, fn (_) -> IO.puts ["(", color, "): ", text] end) 
    end 

    defp merge_defaults(map) do 
    Map.merge(@defaults, map) 
    end 
end 

Eğer nils izin vermek istiyorsanız şunları yapmanız merge_defaults/1Map.merge/3 kullanabilir ve değiştirebilirsiniz:

defp merge_defaults(map) do 
    Map.merge(@defaults, map, fn _key, default, val -> val || default end) 
end 
+0

Teşekkürler, deneyeceğim. Derin birleştirme için bir öneriniz var mı? –

+2

Tüm değerler haritalarsa, burada yapılanlara benzer bir şey yapabilirsiniz: https://github.com/elixir-lang/elixir/blob/master/lib/mix/lib/mix/config.ex#L192- L196 Değilse, her iki değerin de harita olup olmadığını kontrol etmeli ve ardından Map.merge/2'yi çağırmalısınız. –

+1

Açıklamak gerekirse, "izin verilsin" ifadesi, varsayılan olarak üzerine yazılacak olan nils anlamına gelir. Btw, bu cevap henüz Map.merge için güzel bir doküman gibi davranıyor çünkü dokümanı henüz Elixir kurulumunda mevcut değil. – Kelvin

0

Eh. Ben %{a: a, b: b} argümanında başka bir handle_my_map işlevini yazmanız gerektiğini düşünüyorum. Şunun gibi:

def handle_my_map(%{a: a, b: b, optional_c: c}) do 
    a + b + c 
    end 

    def handle_my_map(%{a: a, b: b}) do 
    a + b 
    end 

    YourModule.handle_my_map %{a: 1, b: 2} 
    #=> 3 
    YourModule.handle_my_map %{a: 1, b: 2, optional_c: 3} 
    #=> 6 

İksir

+0

Evet, ancak 5 isteğe bağlı parametre varsa ne olur? 32 ('2^5') uygulamalarına ihtiyacım var ... –

+0

Böylece desen eşleştirmesi kullanamazsınız. Bunun yerine şöyle bir şey yapabilirsiniz: 'def handle_my_map (map), yap: map [: a] + map [: b] + zero_if_nil (map [: optional_c])', burada 'zero_if_nil/1 'başka bir yardımcı fonksiyon . – vforvova

3

Herhalde böyle bir şey ile gider Arity 1 ucuyla fonksiyonlar kadar argümanları eşleşen bir fonksiyonu handle_my_map arayacaktır:

defmodule Handler do 
    @defaults %{print_times: 2, color: "Blue"} 

    def handle_my_map(map) do 
    %{text: text, print_times: times, color: color} = Dict.put_new(map, @defaults) 
    Enum.each(1..times, fn (_) -> IO.puts ["(", color, "): ", text] end) 
    end 
end 

ise Mevcut anahtarlarla nil değerlerini işlemek zorundasınız, şunları yapabilirsiniz:

defmodule Handler do 
    @defaults %{print_times: 2, color: "Blue"} 

    def handle_my_map(map) do 
    %{text: text, print_times: times, color: color} = @defaults 
    |> Dict.merge(map) 
    |> Enum.into %{}, fn 
     {key, nil} -> {key, @defaults[key]} 
     {key, val} -> {key, val} 
    end 

    Enum.each(1..times, fn (_) -> IO.puts ["(", color, "): ", text] end) 
    end 
end 
+0

Düzenlenmiş sorunuzu gidermek için düzenlenmiş :) –

+0

Teşekkürler, ben deneyeceğim. Ancak, bu biraz daha ayrıntılı görünüyor, belki birden fazla konum için gerekli ise, bu makro için iyi bir adaydır ... –

+0

Bu tür bir görev için kod oluşturma gerekli değildir. Bunu yeniden kullanılabilirlik için bir 'merge_with_defaults (my_map ,_defaults) 'func' a çıkartacağım. –