2012-05-21 4 views
8

Manchmal habe ich es in Clojure als bequem empfunden, reduzierte Arity-Versionen von Funktionen zu definieren, die eine Teilfunktion, z. DieseDefinieren von Partialfunktionen mit reduzierter Arit

(defn prefix 
    ([pre string] 
    (str pre ":" string)) 

    ([pre] 
    (fn [string] 
     (prefix pre string)))) 

bedeutet, dass Sie tun können, entweder:

(prefix "foo" 78979) 
=> "foo:78979" 

((prefix "foo") 78979) 
=> "foo:78979" 

Dies scheint ziemlich Haskell-ish und vermeidet die Notwendigkeit partial zu schaffen Teilfunktionen.

Aber ist es in Lisp gute Codierungsstil/API-Design?

+0

Das ist currying, ja? Scheint nützlich zu sein. Und ich denke, dass die Schnittstelle hier verbessert werden könnte. Es wäre schön, wenn Sie diese Technik generisch machen könnten; Das heißt, bei Funktionen mit allen Schlüsselwörtern wird beim Aufrufen dieser Funktion mit einem unvollständigen Satz von Schlüsselwörtern ein Abschluss zurückgegeben, der alle verbleibenden Schlüsselwörter akzeptiert. Wenn alle Schlüsselwörter verbraucht sind, erhalten Sie den Wert der Funktion. Ich könnte sehen, dass dies als eine Optimierungstechnik verwendet wird. –

+0

Ich denke, es ist immer noch ideomatisch, wenn Sie Ihre Partials für die Lesbarkeit benennen, wenn Sie sie so definieren: (def Präfix (partiell ...)) Also hat dies beides: Ein Name zu verweisen und es ist explizit.Beachten Sie, dass Sie defn jetzt nicht verwenden, def-partially aber die Funktion für Sie erstellt. – KIMA

Antwort

7

In Sprachen, in denen Funktionen standardmäßig curiert werden, werden die Funktionsaufrufe ebenfalls curiert. Wenn man (f a b c) schreibt, interpretiert die Sprache dies als (((f a) b) c). Dies ist in Clojure nicht der Fall.

Ich glaube, dass in einer Umgebung curried Funktionen machen, wo Anrufe werden nicht standardmäßig curried ein konzeptionelles Mismatch erzeugt - (. Ich meine menschliche Leser Ihres Codes) dieses Konstrukt wird wahrscheinlich in Leser verursachen Verwirrung

Wenn Ihre Funktion Hat mehr als 2 Argumente, wird die Definition schnell hässlich. Angenommen, eine Funktion hat 4 Argumente. Um den currying Anruf vollständig zu emulieren, müssen Sie Fälle wie ((f a b) c d) behandeln, wenn jemand zuerst 2 Argumente und dann die restlichen zwei Argumente übergibt. In diesem Fall muss die überladene Version der Zwei-Argument-Funktion eine überladene Funktion zurückgeben, die sich je nachdem, ob sie 1 oder 2 Argumente erhält, unterschiedlich verhält. Ich denke, es ist möglich, das mit Makros zu automatisieren, aber trotzdem.

Sie töten auch die Möglichkeit, Standardargumente und das & rest Konstrukt zu definieren.

+0

Standardargumente und variadic '& rest'-Funktionen – 6502

+0

@ 6502 Ja. Bearbeitet zur Vollständigkeit. –

6

hmmm ... persönlich würde ich lieber partial sehen, da das klar macht, was los ist.

Ich weiß nicht, ob es "gut" oder "schlecht" Codierungsstil ist, aber ich habe diesen Stil noch nie zuvor in bestehenden Clojure-Code gesehen und ich kann mir vorstellen, dass Menschen mit einer API sowohl (prefix "foo" 78979) als auch (prefix "foo") erwarten würden die gleiche Art von Objekt zurückgeben.

Um den Unterschied zwischen beiden Funktionen deaktivieren Sie so etwas wie dies stattdessen tun könnte:

partial
(defn prefix [pre string] 
    (str pre ":" string)) 

(defn prefix-fn [pre] 
    (fn [string] 
    (prefix pre string))) 

(prefix "foo" 78979)  ; => "foo:78979" 
((prefix-fn "foo") 78979) ; => "foo:78979" 
+2

"Ich habe diesen Stil noch nie zuvor in existierendem Clojure-Code gesehen" --- es ist alles über Hickey's neue Reduzierbibliothek. –

+0

Sie haben Recht, danke, dass Sie darauf hingewiesen haben. – Gert

9

Mit einer curried Funktion erstellen basiert auf dem Konzept der Explicit ist besser (in den meisten Fällen:)). Und ich habe festgestellt, dass dieses Konzept in dynamisch typisierten Sprachen wie Clojure, Python usw. besser anwendbar ist/verwendet werden kann, da es aufgrund der fehlenden Typ-Signatur/statischen Typisierung sinnvoller ist, Dinge explizit zu machen.

Verwandte Themen