2016-04-18 7 views
1

Was ist eine saubere Möglichkeit in Clojure ein Funktionsargument zu unterstützen, das ein Schlüsselwort wie :name oder ein Pfad wie [:person :name] sein kann, so dass es in einem Aufruf verwendet werden kann get oder get-in?Clojure fn Unterstützung für Schlüsselwort oder Pfad Argument für Get-In

Jetzt habe ich (get-in m (flatten [path])) wo path eingegeben wird. Das funktioniert, aber es scheint, als ob es für häufige Verwendung oder in großen Datensammlungen langsam werden könnte - ich habe kein Benchmarking durchgeführt.

Alternativ denke ich, dass ich auf (coll? path) versenden konnte get oder get-in zu verwenden:

(let [path [:person :name] 
     m {:person {:name "John Smith"}}] 
    (if (coll? path) 
    (get-in m path) 
    (get m path))) 
=> {:name "John Smith"} 

Scheint, wie es effizienter sein könnten.

Antwort

2

Wenn Sie wirklich den Anrufer benötigen, um entweder ein Schlüsselwort oder eine Reihe von Schlüsselwörtern übergeben zu können, ist das eine vollkommen legitime Art, dies zu tun. Ich würde wahrscheinlich keyword? als Test anstelle von coll? verwenden, obwohl, um es ein bisschen klarer zu machen; coll? würde einige falsche positive geben (z. B. wenn die Eingabe eine Karte anstelle eines Vektors oder einer Sequenz ist), aber keyword? würde nicht.

(let [m {:person {:name "John Smith"}}] 
     path [:person :name] 
    (if (keyword? path) 
    (get m path) 
    (get-in m path))) 
;=> "John Smith" 

Eine weitere Möglichkeit könnte die Eingabe als einfach eine Funktion zu behandeln sein, was Ihnen die bedingten ganz zu vermeiden erlauben würde:

(let [m {:person {:name "John Smith"}} 
     f (comp :name :person)] 
    (f m)) 
;=> "John Smith" 

Das ist nicht ganz so schön wie nur Stichwörter oder Sequenzen von Schlüsselwörter, die es dem Aufrufer erlauben, eine beliebige Funktion zu übergeben, nur um in eine Karte zu gelangen, könnten möglicherweise Probleme verursachen.

Wahrscheinlich wäre die beste Option in diesem Fall jedoch, den Benutzer zu zwingen, eine Sequenz zu übergeben und immer get-in zu verwenden. Das würde die Komplexität dieser Art von Bedingungen vermeiden und auch allgemeinere Karten ermöglichen. Betrachten Sie folgendes Beispiel:

(let [m {[:foo :bar] "John Smith" 
     :foo {:bar "John Cena"}} 
     path [:foo :bar]] 
    (if (coll? path) 
    (get-in m path) 
    (get m path))) 
;=> "John Cena" 

In diesem Fall wird die path ist völlig zweideutig; es gibt einfach keine Möglichkeit zu bestimmen, was der Anrufer wirklich wollte. Wenn alle Schlüssel in Ihren Karten Keywords sind, ist das vollkommen in Ordnung. Sie müssen sich nicht mit diesem Problem befassen. Aber das zeigt nur, dass das Hinzufügen von impliziten Konvertierungen es dir möglicherweise erlauben kann, dich später in den Fuß zu schießen.

+0

Guter Punkt über die Mehrdeutigkeit. Eine andere Möglichkeit wäre, Korks zu unterstützen, z.B. '(my-fn: some: path)', die als '[& path]' analysiert werden, was sowohl '(my-fn [: foo: bar])' und '(my-fn: foo: bar)' unterstützt . –

+0

@pate Ja, wenn Sie variadic Argumente verwenden können, tun Sie es auf jeden Fall. Es ist im Grunde genommen nur syntaktischer Zucker für die Weitergabe einer Sequenz, weshalb es hier keine Unklarheiten gibt. –

+0

Es ist anders, weil Sie '(my-fn [: foo: bar]: mehr: bars)' machen könnten, was zu '(get-in m [[: foo: bar]: mehr: bars])', nein führen würde ? –