Clojure

Clojure[1]
Paradigmfunktionell
Gavs ut2009
Skapat avRich Hickey
UtvecklareRich Hickey, m.fl.[2]
Senaste version1.8.0 (2016-01-19)
Datatypsdisciplinstarkt, dynamiskt
Influerat avLisp, Haskell, Java, Python [3]
PlattformJVM, CLR
LicensEclipse Public License
Webbplatsclojure.org

Clojure är en dialekt inom Lisp-familjen av programmeringsspråk. Clojure skapades av Rich Hickey och släpptes i sin första version i slutet av 2009. Det är ett programmeringsspråk för allmän användning, som stödjer interaktiv utveckling och uppmuntrar en funktionell programmeringsstil. Clojure (liksom vissa andra programmeringsspråk) körs på Java Virtual Machine, Common Language Runtime och kan kompileras till Javascript.

Bakgrund

Enligt skaparen, Rich Hickey, är språket framtaget för att vara lättanvänt och ha god prestanda, snarare än att vara ett akademiskt projekt.[4] Vidare ger Clojures API ett jämförelsevis enkelt förfarande för att inter-operera med existerande Java-bibliotek.

Kännetecken

Clojure är ett språk med fokus på funktionell programmering och samverkande ("concurrent" på engelska) programmering. På grund av denna inriktning stödjer Clojure följande funktioner:

  • STM (Software Transactional Memory) med inbyggda makron för att genomföra atomiska transaktioner på gemensamma data
  • Persistenta datastrukturer
  • Funktioner av första klass (funktioner är värden)

Syntax

Clojures syntax byggs på S-uttryck, något som är typiskt för ett Lisp-språk. Clojure är en Lisp-1, vilket innebär att funktioner och andra värden delar på en namnrymd.[5] Språket kan inte kodas om till andra Lisp-dialekter eftersom det inte är kompatibelt.

Det som i huvudsak gör Clojure inkompatibelt med andra dialekter är att Clojure använder specifika teckenpar för att ange olika sorters datastrukturer.[6]

I Clojure används [] för att ange vektorer, {} för att ange associativa vektorer och #{} för att ange associativa vektorer med unika värden.

Exempel

Hello world:

(println "Hello, world!")

Nedan definieras en funktion som tar en parameter (x) som anges som en vektor med ett element. Funktionen anropar multiplikationsfunktionen * med argumenten x, x. I Clojure, som med andra Lisp-dialekter, returneras det värde som den sist anropade funktionen returnerar automatiskt:

(defn square [x]
  (* x x))

Genom att använda Javas Swing bibliotek är det möjligt att rita grafiska gränssnitt (ytterligare ett Hello World-exempel):

(javax.swing.JOptionPane/showMessageDialog nil "Hello World" )

Nedan ges exempel på en trådsäker generator av unika serienummer:

(let [i (atom 0)]
  (defn generate-unique-id
    "Returns a distinct numeric ID for each call."
    []
    (swap! i inc)))

En anonym underklass av java.io.Writer som inte skriver till någonting, och ett makro som använder detta för att tysta alla 'prints' inuti det:

(def bit-bucket-writer
  (proxy [java.io.Writer] []
    (write [buf] nil)
    (close []    nil)
    (flush []    nil)))

(defmacro noprint
  "Evaluates the given expressions with all printing to *out* silenced."
  [& forms]
  `(binding [*out* bit-bucket-writer]
     ~@forms))

(noprint
  (println "Hello, nobody!"))

Tio trådar manipulerar en gemensam datastruktur, vilken består av 100 vektorer, där varje vektor använder 10 (initialt sekventiella) unika tal. Varje tråd väljer repetitivt två slumpvisa positioner i två slumpvisa vektorer och byter plats på dem. Alla ändringar till vektorerna händer i transaktioner genom att använda clojures software transactional memory system. Tack vare denna transaktionskontroll tappas inget data bort trots att man manipulerar vektorerna från flera trådar parallellt 100 000 gånger i exemplet.

(defn run [nvecs nitems nthreads niters]
  (let [vec-refs (vec (map (comp ref vec)
                           (partition nitems (range (* nvecs nitems)))))
        swap #(let [v1 (rand-int nvecs)
                    v2 (rand-int nvecs)
                    i1 (rand-int nitems)
                    i2 (rand-int nitems)]
                (dosync
                 (let [temp (nth @(vec-refs v1) i1)]
                   (alter (vec-refs v1) assoc i1 (nth @(vec-refs v2) i2))
                   (alter (vec-refs v2) assoc i2 temp))))
        report #(do
                 (prn (map deref vec-refs))
                 (println "Distinct:"
                          (count (distinct (apply concat (map deref vec-refs))))))]
    (report)
    (dorun (apply pcalls (repeat nthreads #(dotimes [_ niters] (swap)))))
    (report)))

(run 100 10 10 100000)

Utfall av föregående exempel:

([0 1 2 3 4 5 6 7 8 9] [10 11 12 13 14 15 16 17 18 19] ...
 [990 991 992 993 994 995 996 997 998 999])
Distinct: 1000
 
([382 318 466 963 619 22 21 273 45 596] [808 639 804 471 394 904 952 75 289 778] ...
 [484 216 622 139 651 592 379 228 242 355])
Distinct: 1000

Källor

Den här artikeln är helt eller delvis baserad på material från engelskspråkiga Wikipedia.

Referenser

Media som används på denna webbplats

Clojure logo.svg
Imaging of logo of programming language Clojure.