2017-11-21 3 views
0

Ich versuche, die Best Practice der Clojure-Unveränderlichkeit zu verstehen, und ich habe dieses einfache Beispiel, wo ich immer wieder neue Befehle deklariere (update), aber ich bin mir nicht sicher, ob das richtig ist Weg.Clojure immutability practice

(defrecord Order [fplate splate]) 

(def new-orders clojure.lang.PersistentQueue/EMPTY) 

(defn add-order [orders order] 
    (conj orders order)) 

(defn cook [order]()) 

(defn cook-order [orders] 
(cook (first orders)) (pop orders)) 

;;order1 
(def o1 (->Order "Soup" "Fish&Chips")) 
(def new-orders (add-order new-orders o1)) 

;;order2 
(def o2 (->Order "Salad" "Hamburger")) 
(def new-orders (add-order new-orders o2)) 

;;order3 
(def o3 (->Order "Rice" "Steak")) 
(def new-orders (add-order new-orders o3)) 

;;cook order 
(def new-orders (cook-order new-orders)) 
(peek new-orders) 

Danke, R.

+0

Vielleicht könnten Sie Atom https://clojure.org/reference/atoms benutzen? Siehe dieses verwandte Problem: https://stackoverflow.com/questions/8938330/clojure-swap-atom-dequeuing – netchkin

Antwort

1

Hier ist eine typische Vorgehensweise für dieses Beispiel. Es nutzt die spyx-pretty Funktion from the Tupelo library, aber man konnte println ersetzen, wenn Sie wollen:

(ns tst.demo.core 
    (:require [tupelo.core :as t])) 

(defrecord Order [fplate splate]) 

(def orders-queue (atom [])) 

(defn add-order [order] 
    (swap! orders-queue conj order)) 

(defn cook [order] (println "cooking: " (pr-str order))) 

(add-order (->Order "Soup" "Fish&Chips")) ; order1 
(t/spyx-pretty orders-queue) 

(add-order (->Order "Salad" "Hamburger")) ; order2 
(t/spyx-pretty orders-queue) 

(add-order (->Order "Rice" "Steak")) ; order3 
(t/spyx-pretty orders-queue) 

; cook orders 
(newline) 
(doseq [order @orders-queue] 
    (cook order)) 

mit den Ergebnissen:

orders-queue => 
#<[email protected]: [{:fplate "Soup", :splate "Fish&Chips"}]> 

orders-queue => 
#<[email protected]: 
    [{:fplate "Soup", :splate "Fish&Chips"} 
    {:fplate "Salad", :splate "Hamburger"}]> 

orders-queue => 
#<[email protected]: 
    [{:fplate "Soup", :splate "Fish&Chips"} 
    {:fplate "Salad", :splate "Hamburger"} 
    {:fplate "Rice", :splate "Steak"}]> 

cooking: #tst.demo.core.Order{:fplate "Soup", :splate "Fish&Chips"} 
cooking: #tst.demo.core.Order{:fplate "Salad", :splate "Hamburger"} 
cooking: #tst.demo.core.Order{:fplate "Rice", :splate "Steak"} 
+0

Das ist sicherlich eine Verbesserung gegenüber dem Code in der Frage, obwohl ich lieber eine Erklärung sehen würde, wie man das tatsächlich funktional macht, da die Frage nach der Unveränderlichkeitspraxis fragt. Eine Sache, die ich nicht verstehe: Was ist der 'demo.core' Namensraum, den du in deine' ns' Klausel importierst? ("Tupelo" ist mir egal, da du es mit ': as 'nennst und auch erklärst, wie man stattdessen clojure.core benutzt) – amalloy

+0

Ich benutze normalerweise das Testen' ns' für Demos und habe vergessen, unbenutzte '' zu löschen : benutze Sachen. Diese Antwort enthält jedoch keine Komponententests und könnte sich im Hauptnamespace befinden. –

+0

Hallo Amalloy, Allan. Bitte korrigieren Sie mich, wenn ich falsch liege, aber von Ihrem Beispiel und Ihren Kommentaren verstehe ich, dass, wenn ich den Zustand jeder Art von Sammlung beibehalten und aktualisieren muss, ich atom und swap! Verwenden kann. In den Fragen ging es um Unveränderbarkeit, denn durch die Aktualisierung einer Sammlung mutierte ich, und in diesem Fall muss eine dynamische Liste von Aufträgen gepflegt werden. Vielen Dank. – razvan

2

Dies ist definitiv nicht der richtige Weg, die funktionale Programmierung in Clojure, oder jede andere FP Sprache zu tun. Ihr Code ist immer noch zwingend erforderlich, obwohl Sie Clojure als Implementierungssprache verwendet haben.

Der richtige Weg besteht darin, eine neue Datenstruktur mit geändertem Inhalt zu erstellen, wenn Sie eine neue Version einer Datenstruktur benötigen. Etwas wie:

In diesem Fall erstellt conj eine neue Datenstruktur durch Hinzufügen eines neuen Elements zur Struktur, die als Parameter für conj angegeben wird. Die alte Struktur wird in keiner Weise verändert, was bedeutet, dass sie unveränderlich ist.

Wenn Sie wirklich eine Art von Zustand haben müssen, hat Clojure dafür Primitive, wie zum Beispiel Atom. Aber zuerst versuchen, Funktionscode ohne einen änderbaren Zentralzustand zu schreiben.

+0

Hallo lokori, danke für Ihre Antwort. Ich verstehe deine Meinung. In einem realen Szenario (einem Restaurant) kommen und gehen Aufträge ständig und es muss einen Punkt geben, um die "tatsächlichen Bestellungen" zu behalten, so dass andere Funktionen wie "Vorbereiten" oder "Aktualisieren" sich darauf verlassen müssen. Was in einem Let passiert, bleibt in einem Let, also schätze ich, dass ich etwas vermisse. Danke – razvan