2012-06-27 6 views
15

Das heißt, wenn Sie eine Funktion mit> 1 Arity mit nur einem Argument aufrufen, sollte sie anstelle eines Fehlers dieses Argument curry und die resultierende Funktion mit reduzierter Arity zurückgeben. Ist das mit den Lisp-Makros möglich?Ist es möglich, Auto-Curry in die Sprachen der Lisp-Familie zu implementieren?

+0

Sie müssten eine typisierte Sprache über Lisp implementieren (was sehr einfach mit Makros ist), aber es wäre nicht einfach, diese Sprache mit dem Rest des Lisp zu mischen. –

Antwort

9

In Schema ist es möglich, eine Funktion mit dem curry Verfahren einzuzuschmeicheln:

(define (add x y) 
    (+ x y)) 

(add 1 2)   ; non-curried procedure call 
(curry add)   ; curried procedure, expects two arguments 
((curry add) 1)  ; curried procedure, expects one argument 
(((curry add) 1) 2) ; curried procedure call 

Von Racket des documentation:

[Curry] liefert ein Verfahren, das eine curried Version von proc. Wenn die resultierende Prozedur zuerst angewendet wird, ist das Ergebnis eine Prozedur, um zusätzliche Argumente zu akzeptieren, es sei denn, es wird die maximale Anzahl der Argumente, die es akzeptieren kann, angegeben.

Sie leicht ein Makro implementieren könnte, die curry automatisch verwendet, wenn neue Verfahren zu definieren, etwa so:

(define-syntax define-curried 
    (syntax-rules() 
     ((_ (f . a) body ...) 
     (define f (curry (lambda a (begin body ...))))))) 

Nun ist die folgende Definition von add curried werden:

(define-curried (add a b) 
    (+ a b)) 

add 
> #<procedure:curried> 

(add 1) 
> #<procedure:curried> 

((add 1) 2) 
> 3 

(add 1 2) 
> 3 
+2

Insgesamt gute Antworten, aber Sie haben dieses kleine Snipplet zur Verfügung gestellt, das sich als sehr nützlich erwies, so dass ich keine andere Antwort akzeptieren kann. – MaiaVictor

1

Lisp hat bereits Functional Currying:

* (defun adder (n) 
    (lambda (x) (+ x n))) 
ADDER 

http://cl-cookbook.sourceforge.net/functions.html

Hier ist, was ich über Lisp-Makros lese: https://web.archive.org/web/20060109115926/http://www.apl.jhu.edu/~hall/Lisp-Notes/Macros.html

Es ist möglich, dies in reiner Lisp zu implementieren. Es ist möglich, es auch mit Makros zu implementieren, aber es scheint, als würden Makros es für sehr einfache Dinge verwirrender machen.

+1

"Arity" ist eine gebräuchliche Bezeichnung in Prolog. Dort werden Funktionen (* Prädikate *) mit gleichem Namen und unterschiedlichen Aritäten als unterschiedlich betrachtet. –

2

Die kurze Antwort ist ja, aber nicht leicht.

können Sie dies als Makro implantieren, die jeden Anruf in partial gewickelt, obwohl nur in begrenztem Rahmen. Clojure hat einige Features, die das ziemlich schwierig machen würden, wie variable Arity-Funktionen und Dynamit-Aufrufe. Clojure fehlt ein formales System, um konkret zu entscheiden, wann der Aufruf keine Argumente mehr haben kann und eigentlich aufgerufen werden sollte.

13

Es ist möglich, aber nicht einfach, wenn Sie ein nützliches Ergebnis wünschen.

  • Wenn Sie eine Sprache möchten, die immer einfach currying, dann ist die Implementierung einfach. Sie konvertieren einfach jede Anwendung von mehr als einer Eingabe in eine verschachtelte Anwendung und das Gleiche für Funktionen von mehr als einem Argument. Mit Racket's Spracheinrichtungen ist dies eine sehr einfache Übung. (In anderen Lisps können Sie einen ähnlichen Effekt durch irgendein Makro um den Code erhalten, in dem Sie es verwenden möchten.)

    (Übrigens habe ich eine Sprache oben auf Schläger, der gerade dieses tut. Er erhält die volle Niedlichkeit von Auto-Curry-Sprachen, aber es ist nicht beabsichtigt, praktisch zu sein.)

    Allerdings ist es nicht zu nützlich, da es nur für Funktionen eines Arguments funktioniert.Sie könnten es mit etwas Hacking nützlich machen, zum Beispiel den Rest des Lisp-Systems um Ihre Sprache als Fremdsprache behandeln und Formulare zur Verwendung bereitstellen. Eine andere Alternative besteht darin, Ihrer Sprache Informationen über die Funktionen des umgebenden Lisp zu geben. Beide erfordern viel mehr Arbeit.

  • Eine andere Option ist, nur jede Anwendung zu überprüfen. Mit anderen Worten, schalten Sie alle

    (f x y z) 
    

    in Code, der die arity von f prüft und eine Schließung erstellen, wenn es nicht genügend Argumente sind. Dies ist an sich nicht allzu schwer, wird aber zu einem erheblichen Overhead-Preis führen. Sie könnten versuchen, einen ähnlichen Trick mit einigen Informationen über Aritäten von Funktionen zu verwenden, die Sie auf der Makroebene verwenden würden, um zu wissen, wo solche Schließungen erstellt werden sollten - aber das ist im Wesentlichen auf die gleiche Weise schwierig.

  • Aber es gibt ein viel ernsthafteres Problem, auf der hohen Ebene dessen, was Sie tun möchten. Die Sache ist, dass Variable-Arity-Funktionen einfach nicht gut mit automatischem Curry spielen. Nehmen wir zum Beispiel einen Ausdruck wie:

    (+ 1 2 3)

    Wie würden Sie entscheiden, ob dies aufgerufen werden soll wie es ist, oder ob es sich um ((+ 1 2) 3) übersetzt werden sollte? Es scheint so, als ob es hier eine einfache Antwort gibt, aber was ist damit? (Deutsch zu Ihrem Lieblings Lisp-Dialekt)

    (define foo (lambda xs (lambda ys (list xs ys)))) 
    

    In diesem Fall, dass Sie ein (foo 1 2 3) in einer Reihe von Möglichkeiten aufgespalten werden. Noch eine andere Frage ist, was tun Sie mit so etwas wie:

    (list +) 
    

    Hier haben Sie + als Ausdruck, aber man könnte sich entscheiden, dass dies ist das gleiche, wie es auf Null Eingänge Anwendung, die + s arity passt, aber dann Wie schreibst du einen Ausdruck, der die Additionsfunktion auswertet? (Randbemerkung: ML und Haskell "lösen" dies, indem sie keine Nullfunktionen haben ...)

    Einige dieser Probleme können gelöst werden, indem entschieden wird, dass jede "echte" Anwendung Parens dafür haben muss, so dass ein + von selbst wird niemals angewendet werden. Aber das verliert viel von der Niedlichkeit einer Auto-Curry-Sprache, und Sie haben immer noch Probleme zu lösen ...

    1

    Wie von Alex W, das Common Lisp Cookbook does give an example einer "Curry" -Funktion für Common Lisp erwähnt. Das spezifische Beispiel ist weiter unten auf dieser Seite:

    (declaim (ftype (function (function &rest t) function) curry) 
         (inline curry)) ;; optional 
    (defun curry (function &rest args) 
        (lambda (&rest more-args) 
        (apply function (append args more-args)))) 
    

    Auto-currying nicht so schwer zu implementieren sein sollte, so habe ich einen Riss an mich.Beachten Sie, dass die folgenden nicht ausgiebig getestet und überprüft nicht, dass es nicht zu viele Argumente (die Funktion vervollständigt nur, wenn diese Zahl sind oder mehr):

    (defun auto-curry (function num-args) 
        (lambda (&rest args) 
        (if (>= (length args) num-args) 
         (apply function args) 
         (auto-curry (apply (curry #'curry function) args) 
            (- num-args (length args)))))) 
    

    scheint, obwohl zu arbeiten:

    * (auto-curry #'+ 3) 
    #<CLOSURE (LAMBDA (&REST ARGS)) {1002F78EB9}> 
    
    * (funcall (auto-curry #'+ 3) 1) 
    #<CLOSURE (LAMBDA (&REST ARGS)) {1002F7A689}> 
    
    * (funcall (funcall (funcall (auto-curry #'+ 3) 1) 2) 5) 
    8 
    
    * (funcall (funcall (auto-curry #'+ 3) 3 4) 7) 
    14 
    

    Eine primitive (Griff nicht vollständig Lambda-Listen richtig, nur einfache Parameterlisten) Version eines Makro-Syntax Zucker über die oben:

    (defmacro defun-auto-curry (fn-name (&rest args) &body body) 
        (let ((currying-args (gensym))) 
        `(defun ,fn-name (&rest ,currying-args) 
         (apply (auto-curry (lambda (,@args) ,@body) 
              ,(length args)) 
           ,currying-args)))) 
    

    Scheint obwohl th zu arbeiten, e Notwendigkeit FUNCALL ist immer noch ärgerlich:

    * (defun-auto-curry auto-curry-+ (x y z) 
        (+ x y z)) 
    AUTO-CURRY-+ 
    
    * (funcall (auto-curry-+ 1) 2 3) 
    6 
    
    * (auto-curry-+ 1) 
    #<CLOSURE (LAMBDA (&REST ARGS)) {1002B0DE29}> 
    
    1

    Sicher, Sie nur genaue Semantik für Ihre Sprache entscheiden müssen, und dann Ihre eigenen loader implementieren, die Quelldateien in die Implementierungssprache übersetzen.

    Sie könnten z.B. Übersetzen Sie jeden Benutzer Funktionsaufruf (f a b c ... z) in (...(((f a) b) c)... z), und alle (define (f a b c ... z) ...) bis (define f (lambda(a) (lambda(b) (lambda(c) (... (lambda(z) ...) ...))))) oben auf einem Scheme, um ein Auto-Currying-Schema zu haben (das Varargs Funktionen natürlich verbieten würde).

    Sie müssen auch Ihre eigenen Primitiven definieren, indem Sie die varargs-Funktionen wie z. (+) binär, und ihre Anwendungen unter Verwendung von fold z. (+ 1 2 3 4) ==>(fold (+) (list 1 2 3 4) 0) oder etwas - oder vielleicht nur solche Anrufe als (+ 1 2 3 4) illegal in Ihre neue Sprache, in der Erwartung, dass der Benutzer fold Formen selbst schreiben.

    Das meinte ich mit "entscheiden ... Semantik für Ihre Sprache".

    Der Lader kann so einfach sein wie das Umschließen der Datei Inhalt in einen Anruf ein Makro - die Sie dann implementieren müssten, je nach Ihrer Frage.