2016-05-20 6 views
3

In Clojure gibt es eine höherwertige Funktion reductions, die Sie mit ähnlichen Argumenten wie reduce verwenden würden und eine Sequenz mit allen Zwischenergebnissen zurückgeben.Gemeinsame Lisp-Funktion für "Zwischenergebnisse als Sequenz reduzieren und zurückgeben"

Gibt es eine Entsprechung in Common Lisp? Ich konnte online keinen Verweis darauf finden, einschließlich der verschiedenen Bücher/Artikel unter https://common-lisp.net/tutorials/, aber angesichts Lisps Erbe als eine Familie von Listenverarbeitungssprachen stellte ich mir eine Listen-> Listenfunktion wie reductions in Dialekten vor.

+0

Beachten Sie, dass die Funktion ** Reduzieren ** von Common Lisp ein paar mehr Glöckchen hat als Clojures. Gemeinsame Lisp's können Sie die Traversierungsreihenfolge angeben (z. B. können Sie von rechts nach links neben dem Standard von links nach rechts gehen), können Sie den Anfangswert weglassen (in diesem Fall das "erste" Element (am weitesten links oder ganz rechts, abhängig auf der Traversierungsordnung) wird als der Anfangswert verwendet, usw. Einige dieser Merkmale machen dies nicht ganz so trivial, wie es sonst scheinen könnte. –

+0

@JoshuaTaylor Großer Punkt, danke. Ich muss mich immer noch an die Common-Lisp-Kultur gewöhnen, indem ich sehr mächtige Optionen in verschiedenen Funktionen eingebaut habe. – zehnpaard

+0

@JoshuaTaylor Sie können den Anfangswert in Clojures 'reduce' weglassen:' (reduzieren + ["whatever"])) => "whatever" '. – Thumbnail

Antwort

9

Es gibt keine Standardfunktion dafür. Sie könnte man leicht definieren:

(defun reductions (function sequence &rest args 
        &key key from-end (start 0) end initial-value) 
    (declare (ignore key from-end start end initial-value)) 
    "Return a list of intermediate values from reducing SEQUENCE with FUNCTION." 
    (let* ((reductions (list)) 
     (result (apply #'reduce 
         (lambda (&rest arguments) 
          (let ((result (apply function arguments))) 
          (push result reductions) 
          result)) 
         sequence 
         args))) 
    (values (or (nreverse reductions) 
       (list result)) 
      result))) 

(reductions #'+ '(1 2 3 4 5 6 7 8 9 10) :initial-value 0) 
;=> (1 3 6 10 15 21 28 36 45 55) 

Edit: Verwenden APPLY mit &REST ARGS statt REDUCE direkt aufzurufen. Eine Implementierung von REDUCE funktioniert möglicherweise nicht, wenn NIL s für Schlüsselwortargumente bereitgestellt werden.

Edit2: Die reduzierende Funktion kann mit 0 oder 2 Argumenten aufgerufen werden.

Edit3: Wenn REDUCE mit einer Liste von nur einem Element aufgerufen wird, wird das einzige Element zurückgegeben, wie es ist. Die Reduzierungsfunktion wird überhaupt nicht aufgerufen, was bedeutet, dass die Liste der Reduzierungen leer ist. Ich habe eine OR hinzugefügt, um das Endergebnis, das in einer Liste in dieser Situation enthalten ist, zurückzugeben (um Clojures-Verhalten zu entsprechen). Ich änderte auch den Code, um das Endergebnis als zweiten Rückgabewert zurückzugeben (möglicherweise nützlich und "warum nicht?").

+0

Können Sie Ihre erste Bearbeitung genauer ausführen, wo Sie erwähnen, dass "einige Implementierungen von REDUCE möglicherweise nicht funktionieren, wenn NILs für Schlüsselwortargumente bereitgestellt werden." [Die Dokumente] (http://www.lispworks.com/documentation/HyperSpec/Body/f_reduce.htm) zeigen, dass einige Null sein können, Start muss eine Zahl sein (und der Standardwert ist 0). Für den Anfangswert ist es nicht, dass es nicht null sein kann oder nicht, es ist, dass das Verhalten davon abhängt, ob es bereitgestellt wird oder nicht. Es sollte keine Variation in Implementierungen geben; Entscheidend ist, ob es überhaupt zur Verfügung steht. –

+0

@JoshuaTaylor Ich meinte, dass einige Implementierungen "IDENTITY" als Standard für 'KEY' oder' (1- (LENGTH SEQUENCE)) '' als Standard für 'END' und die Übergabe eines' NIL' angeben würden. Aber du hast Recht, die Spezifikation besagt, dass "NIL" die Standardeinstellung ist, also ist das eigentlich kein Problem. Der bearbeitete Code wird jedoch weiterhin benötigt, um 'INITIAL-VALUE' zu behandeln. – jkiiski

+1

Tatsächlich denke ich, dass die Verwendung von ** nil ** für Schlüsselargumente ("Wenn der Schlüssel nicht geliefert wird oder Null ist, wird das Sequenzelement selbst verwendet.") Festgelegt wird, um diese Art von Anwendungsfall zu machen einfacher. Die Auslösung ist in diesem Fall nur mit dem Anfangswert. Es ist schön, Lösungen zu sehen, die das richtig behandeln. :) –

Verwandte Themen