2014-07-16 21 views
12

Mam funkcję, która otrzymuje mapę z wieloma kluczami, niektóre z nich są opcjonalne. W jaki sposób mogę napisać sygnaturę funkcji obejmującą mapę, jednocześnie pozwalając, aby opcjonalne klucze były domyślne?Dopasowywanie wzoru mapy za pomocą opcjonalnych klawiszy

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! 

chciałbym go mieć:

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

Coś argumentów kluczowych Ruby:

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

to 'handle_my_map/1 'pobiera jeden parametr? –

+0

@ Зелёный - tak, pojedynczy parametr mapy –

Odpowiedz

10

Można użyć Map.merge/2:

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 

Jeśli chcesz zezwolić Nils, można użyć Map.merge/3 i zmienić merge_defaults/1 do:

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

Dzięki, spróbuję. Czy masz również sugestię dotyczącą głębokiego scalenia? –

+2

Jeśli wszystkie wartości są mapami, możesz zrobić coś podobnego do tego, co zostało zrobione tutaj: https://github.com/elixir-lang/elixir/blob/master/lib/mix/lib/mix/config.ex#L192- L196 Jeśli nie, wystarczy sprawdzić, czy obie wartości są mapami, a następnie wywołać Map.merge/2. –

+1

Aby wyjaśnić, "allow nils" oznacza nadpisanie nils z wartością domyślną. Przy okazji ta odpowiedź działa jak ładny dokument dla 'Map.merge', ponieważ dokument nie istnieje jeszcze w instalacji Elixir. – Kelvin

0

również. Myślę, że musisz napisać następną funkcję handle_my_map z argumentem %{a: a, b: b}. Tak:

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 

Elixir wyszuka funkcji handle_my_map które są dopasowane do Twoich argumentów aż funkcji z arity 1 koniec

+0

Tak, ale co, jeśli jest 5 opcjonalnych parametrów? Potrzebowałbym 32 ('2^5') implementacji ... –

+0

Więc nie możesz użyć dopasowywania wzorców. Zamiast tego możesz zrobić coś takiego: 'def handle_my_map (map), do: map [: a] + map [: b] + zero_if_nil (map [: optional_c])', gdzie 'zero_if_nil/1' to kolejna funkcja pomocnika . – vforvova

3

I pewnie iść z czymś takim:

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 

If musisz obsłużyć wartości nil za pomocą istniejących kluczy, możesz to zrobić:

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

Edytowany w celu rozwiązania zmienionego pytania :) –

+0

Dzięki, spróbuję. Wygląda jednak trochę gadatliwie, być może, jeśli jest to potrzebne dla więcej niż jednej lokalizacji, jest dobrym kandydatem na makro ... –

+0

Generowanie kodu dla tego rodzaju zadania nie jest konieczne. Wyodrębniłbym to do 'merge_with_defaults (my_map, the_defaults)' func zamiast do ponownego użycia. –

Powiązane problemy