2012-06-17 6 views

Antwort

14

Diese Art der Programmierung ist grundsätzlich in CL möglich, aber als Lisp-2 muss man mehrere s und funcall s hinzufügen. Im Gegensatz zu Haskell zum Beispiel werden Funktionen in CL nicht curiert, und es gibt keine implizite partielle Anwendung. Im Allgemeinen denke ich, dass ein solcher Stil nicht sehr idiomatisch wäre.

Zum Beispiel könnten Sie partielle Anwendung und Zusammensetzung wie folgt definieren:

(defun partial (function &rest args) 
    (lambda (&rest args2) (apply function (append args args2)))) 

(defun comp (&rest functions) 
    (flet ((step (f g) (lambda (x) (funcall f (funcall g x))))) 
    (reduce #'step functions :initial-value #'identity))) 

(Das sind nur kurze Beispiele, die ich aufgepeitscht - sie sind nicht wirklich getestet oder durchdachte für verschiedene Anwendungsfälle.

)

Mit denen, so etwas wie map ((*2) . (+1)) xs in Haskell wird:

CL-USER> (mapcar (comp (partial #'* 2) #'1+) '(1 2 3)) 
(4 6 8) 

Das sum Beispiel:

CL-USER> (defparameter *sum* (partial #'reduce #'+)) 
*SUM* 
CL-USER> (funcall *sum* '(1 2 3)) 
6 

(In diesem Beispiel könnten Sie auch die Funktionszelle eines Symbol setzen stattdessen die Funktion in der Zelle Wert der Speicherung, um rund um den FUNCALL zu erhalten.)

In Emacs Lisp, durch Der Weg, Teilanwendung ist als apply-partially eingebaut.

In Qi/Shen, sind Funktionen curried und implizite Teil Anwendung (wenn Funktionen mit einem Argument aufgerufen werden) unterstützt wird:

(41-) (define comp F G -> (/. X (F (G X)))) 
comp 

(42-) ((comp (* 2) (+ 1)) 1) 
4 

(43-) (map (comp (* 2) (+ 1)) [1 2 3]) 
[4 6 8] 

Es in Clojure auch syntaktische threading Zucker ist, der aus einem ähnlichen Gefühl gibt, "pipelining":

user=> (-> 0 inc (* 2)) 
2 
2

Ja, das ist generell mit den richtigen Funktionen möglich. Zum Beispiel, hier ist ein Beispiel in Racket sum aus der Wikipedia-Seite Implementierung:

#lang racket 
(define sum (curry foldr + 0)) 

Da Prozeduren standardmäßig nicht curried sind, hilft es curry zu nutzen oder Ihre Funktionen in einem explizit curried Stil schreiben. Sie könnten darüber mit einem neuen 10 Makro, das Currying verwendet, abstrahieren.

+2

Die Frage ist über Common Lisp, diese Antwort ist richtig für Scheme, aber nicht für CL –

+1

Nun, eigentlich war die Frage über Lisp im Allgemeinen. Ich habe CL als Tag hinzugefügt, da es der Lisp-Dialekt ist, den ich am meisten kenne, aber diese Antwort mit Schema ist ebenfalls nützlich. – paldepind

3

JA, es ist möglich und @danlei schon sehr gut erklärt. Ich füge einige Beispiele aus dem Buch ANSI Common Lisp von Paul Graham, Kapitel 6, zusammen.6 auf Funktion Bauer:

Sie eine Funktion Builder wie folgt definieren:

(defun compose (&rest fns) 
    (destructuring-bind (fn1 . rest) (reverse fns) 
    #'(lambda (&rest args) 
     (reduce #'(lambda (v f) (funcall f v)) 
       rest 
       :initial-value (apply fn1 args))))) 

(defun curry (fn &rest args) 
    #'(lambda (&rest args2) 
     (apply fn (append args args2)))) 

und es verwenden, wie diese

(mapcar (compose #'list #'round #'sqrt) 
     '(4 9 16 25)) 

kehrt

((2) (3) (4) (5)) 

Die compose Funktionsaufruf:

(compose #'a #'b #'c) 

zu

#'(lambda (&rest args) (a (b (apply #'c args)))) 

equlvalent Mittel Diese compose eine beliebige Anzahl von Argumenten entgegennehmen kann, ja.

eine Funktion abgeben, die 3 bis Argument hinzu:

(curry #'+ 3) 

Sehen Sie mehr in dem Buch.

+2

aber es macht nicht viel Sinn. Es führt zu schlechtem Code. Schwieriger zu lesen und zu debuggen. Plus - fast jeder CL-Compiler wird langsamen Code dafür generieren (Consed-Arg-Listen usw.). –

+0

@RainerJoswig Du hast recht. Dies könnte zeigen, wie flexibel CL ist und welche Schließung uns helfen kann ... o_O – juanitofatas

+2

Ihr 'compose' Beispiel funktioniert auch mit meiner Version. Ich hatte gerade einen kleinen Kopier- und Einfügefehler oder Tippfehler in "comp" (falsche Reihenfolge der Funktionsaufrufe in "Schritt", funktionierende Version in der REPL), die jetzt behoben ist. Außerdem denke ich, dass Rainer recht hat: Während es sicher schön ist, das in CL machen zu können, ist es nicht sehr idiomatisch. Haskell bietet sich viel mehr für diese Art der Programmierung an.(Grahams Stil, übrigens, wird von vielen CL-Programmierern als ziemlich eigenwillig angesehen.) – danlei

7

Sie so etwas wie verwenden könnte (dies tut ein wenig mehr als -> in Clojure):

(defmacro -> (obj &rest forms) 
    "Similar to the -> macro from clojure, but with a tweak: if there is 
    a $ symbol somewhere in the form, the object is not added as the 
    first argument to the form, but instead replaces the $ symbol." 
    (if forms 
     (if (consp (car forms)) 
      (let* ((first-form (first forms)) 
       (other-forms (rest forms)) 
       (pos (position '$ first-form))) 
      (if pos 
       `(-> ,(append (subseq first-form 0 pos) 
           (list obj) 
           (subseq first-form (1+ pos))) 
        ,@other-forms) 
       `(-> ,(list* (first first-form) obj (rest first-form)) 
        ,@other-forms))) 
      `(-> ,(list (car forms) obj) 
       ,@(cdr forms))) 
     obj)) 

(Sie müssen vorsichtig sein, auch $ in aus dem Paket, um das Symbol zu exportieren, die Sie -> platzieren - lassen Sie uns das Paket tacit nennen - und tacit in der use Klausel jedes Paket geschnürt, wo Sie planen -> zu verwenden, so -> und $ vererbt werden)

Anwendungsbeispiele:

(-> "TEST" 
    string-downcase 
    reverse) 

(-> "TEST" 
    reverse 
    (elt $ 1)) 

Dies ist mehr wie F # 's |> (und das Shell-Rohr) als Haskells ., aber sie sind so ziemlich die gleiche Sache (ich ziehe |>, aber das ist eine Frage der persönlicher Geschmack).

Um zu sehen, was -> tut, nur das letzte Beispiel dreimal macroexpand (in SLIME, dies, indem Sie den Cursor auf den ersten ( im Beispiel erreicht wird und die Eingabe C-c RET dreimal).