2016-03-31 3 views
2

Ich habe eine allgemeine Frage zu Standardparametern und Nullwerten. Angenommen, ich habe zwei Funktionen. Man ruft den anderen an (was eine Hilfsfunktion ist). Beide haben einen optionalen Parameter.Elixier: Wie geht man mit optionalen/Standard-Parameter in Funktionen und Nullwerte?

Die Hilfsfunktion verbindet nur eine Liste mit einer Zeichenfolge mit einem Verbinder. Der Verbinder wird an die erste Funktion in einer opts-Schlüsselwortliste übergeben. Die Weitergabe des Tischlers ist optional, wird standardmäßig "AND"

defmodule ParamTest do 
    def func_1(list, opts \\ []) do 
    helper(list, opts[:joiner]) 
    # Do something else with the result 
    end 

    defp helper(list, joiner \\ "AND") do 
    Enum.join(list, " #{joiner} ") 
    end 
end 

# Example 1 
["el 1", "el 2"] 
|> ParamTest.func_1(joiner: "AND") 
# Result "el 1 AND el 2" 

# Example 2 
["el 1", "el 2"] 
|> ParamTest.func_1 
# Result: "el 1 el 2" 
# But it should be also "el 1 AND el 2" 

Das Problem ist: opts [: Schreiner] nil im zweiten Beispiel sein wird. Aber es ist immer noch vorhanden, so dass der Standardwert nicht verwendet wird.

Eine mögliche Lösung wäre die Verwendung case:

defmodule ParamTest do 
    def func_1(list, opts \\ []) do 
    case is_nil(opts[:joiner]) do 
     true -> helper(list) 
     false -> helper(list, opts[:joiner]) 
    end 
    # Do something else with the result 
    end 

    defp helper(list, joiner \\ "AND") do 
    Enum.join(list, " #{joiner} ") 
    end 
end 

Ein anderer Weg wäre für Helfer zwei Funktionsdefinitionen zu verwenden und Musterabgleich verwenden:

defmodule ParamTest do 
    def func_1(list, opts \\ []) do 
    case is_nil(opts[:joiner]) do 
     true -> helper(list) 
     false -> helper(list, opts[:joiner]) 
    end 
    end 

    defp helper(list, nil) do 
    Enum.join(list, " AND ") 
    end 

    defp helper(list, joiner \\ "AND") do 
    Enum.join(list, " #{joiner} ") 
    end 
end 

Aber ich habe das Gefühl, dass diese ist nicht sehr elegant und könnte in komplexeren Situationen unordentlich werden.

Was ist eine bessere Lösung für dieses Szenario?

Antwort

1

Keine bessere Lösung, was Sie meiner Meinung nach schon haben. Persönlich würde ich mir die folgenden Fragen stellen:

  • bin ich sicher, ich brauche ein Standardargument für die helper/2private Funktion? Ich bin nicht davon überzeugt, aber ich fühle mich wie Standard \\ Args zu privaten Funktion kann eine Art Code Geruch sein.
  • Wo möchte ich mit der Komplexität umgehen, wenn ich mit einem \\ Standardargument gehe? :)

Wenn ich wählen müsste, in diesem speziellen Fall würde ich wahrscheinlich gehen mit Aufruf helper/1 und helper/2 getrennt, basierend auf der Anwesenheit der :joiner Option:

defmodule ParamTest do 
    def func_1(list, opts \\ []) do 
    if joiner = opts[:joiner] do 
     helper(list, joiner) 
    else 
     helper(list) 
    end 
    end 

    defp helper(list, joiner \\ "AND") do 
    Enum.join(list, " #{joiner} ") 
    end 
end 

Allerdings habe ich als oben erwähnt, da helper/2 eine private Funktion ist, kann es sinnvoll sein (abhängig von Ihrem Anwendungsfall, ist dies zu klein für uns, um eine durchdachtere Entscheidung zu treffen: P) den optionalen Verbinder vollständig an die Grenze des "Systems" zu bewegen dh nur func_1/2, indem ein Standardwert für die Option

verwendet wird

Auch dies kann nicht gut in Ihrem Anwendungsfall skalieren, aber ich fühle mich wie es ist das Beste, was wir tun können mit den Informationen, die wir von der Frage haben :).

+0

Ich stimme dem Standardargument für private Funktionen zu. Und ich mag deine zweite Lösung. Vielen Dank. –

+0

Wenn der Helfer nur von der ersten Funktion aus aufgerufen wird, dann ist die doppelte Vorgabe peinlich. Die zweite Lösung ist peinlich, weil Sie die || haben innerhalb des Funktionsaufrufs. Sie sind besser dran, nur mit Ihrem Standard sein opts \\ [joiner: "UND"] – CaptChrisD

+0

@CaptChrisD wie andere Leute in den Kommentaren auf die andere Funktion hingewiesen, '[joiner:" UND "]' als Standard * in der Regel * ist keine gute Idee, da die Funktion viele Optionen akzeptieren kann (es ist selten, dass eine Funktion nur eine Option akzeptiert). – whatyouhide

5

Die beste Lösung wäre, den Joiner im Helper zu verpflichtend zu machen und Standardoptionen in func_1 bereitzustellen.

def func_1(list, opts \\ [joiner: "AND"]) do 
    helper(list, opts[:joiner]) 
    ... 
end 
defp helper(list, joiner) do 
    ... 
end 

Versuchen Sie immer, Ihre Bedenken zu trennen. helper ist nicht Teil Ihrer öffentlichen API, Sie können also immer alle Optionen übergeben. Lass es einfach seine Arbeit machen und mach dir keine Sorgen über Standardeinstellungen.

func_1 ist Ihre öffentliche API und es sollte sich um Standardeinstellungen kümmern. Sie möchten standardmäßig "AND" Joiner angeben, also tun Sie es anstelle der leeren Optionsliste. Wenn jemand Ihren Code liest, muss er nicht tiefer gehen, um zu überprüfen, woher das "UND" kommt und kann leicht herausfinden, dass er diese Option weitergeben kann, ohne Dokumente oder sogar Funktionskörper zu lesen.

Es ist in der Regel eine gute Idee, die Standardwerte nur aus Gründen der Bequemlichkeit auf Funktionen der obersten Ebene (der API) zu haben und nur alles explizit durchzulassen. Andernfalls müssten Sie auf jeder Ebene überprüfen, ob die Option wie in Ihrem Beispiel mit case übergeben wurde. Dies ist fehleranfällig.

+0

Ich mag sogar deine Version sogar mehr als die von @whatyouhide, da sie den Standard sehr klar für jemanden macht, der meinen Code zum ersten Mal liest. Danke –

+0

Ich muss meine vorherige Aussage zurückziehen. Sobald mehr Optionen mit dem Argument opts übergeben werden, wird alles überschrieben und diese Lösung funktioniert nicht mehr. '[" el 1 "," el 2 "] |> ParamTest.func_1 (irgendwas:" was auch immer ")' ergibt "" el 1 el 2 "' –

+0

Sie haben Recht. Für den Fall, dass Sie mehr Optionen haben und nur einige davon überschreiben wollen, 'opts [: joiner] || "UND" ist besser. Alternativ können Sie in den ersten Funktionszeilen 'opts = opts ++ [joiner:" AND "]' machen, aber das macht nur Sinn, wenn alle Optionen Standardwerte haben und es einige davon gibt. – tkowal