diff --git a/de-de/clojure-de.html.markdown b/de-de/clojure-de.html.markdown new file mode 100644 index 00000000..1257b099 --- /dev/null +++ b/de-de/clojure-de.html.markdown @@ -0,0 +1,422 @@ +--- +language: clojure +filename: learnclojure-de.clj +contributors: + - ["Adam Bard", "http://adambard.com/"] +translators: + - ["Dennis Keller", "https://github.com/denniskeller"] +lang: de-de +--- + +Clojure ist ein Lispdialekt, der für die Java Virtual Maschine entwickelt worden ist. Sie hat eine stärkere Betonung auf reine [funktionale Programmierung](https://en.wikipedia.org/wiki/Functional_programming) als Common Lisp. Jedoch besitzt sie je nach Bedarf mehrere [STM](https://en.wikipedia.org/wiki/Software_transactional_memory) Werkzeuge zur Handhabung von Zustand. + +Diese Verknüpfung erlaubt es parallele Verarbeitung sehr einfach und häufig automatisch zu verarbeiten. + +(Du brauchst die Clojure Version 1.2 oder neuer) + +```clojure +; Kommentare starten mit einem Semikolon. + +; Clojure wird in "Forms" geschrieben, was nur Listen von Dingen +; in Klammern sind, getrennt durch Leerzeichen. +; +; Der Clojure Leser nimmt an, dass das Erste, was aufgerufen wird +; eine Funktion oder ein Makro ist, der Rest sind Argumente. + +; Der erste Aufruf in einer Datei sollte ns sein, um den Namespacezu setzen. +(ns learnclojure) + +; Weitere einfache Beispiele: + +; str erstellt einen String aus allen Argumenten +(str "Hallo" " " "Welt") ; => "Hallo Welt" + +; Mathe ist einfach +(+ 1 1) ; => 2 +(- 2 1) ; => 1 +(* 1 2) ; => 2 +(/ 2 1) ; => 2 + +; Gleichheit ist = +(= 1 1) ; => true +(= 2 1) ; => false + +; Du brauchst auch not für Logik +(not true) ; => false + +; Verschachtelte Forms funktionieren, wie erwartet +(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2 + +; Typen +;;;;;;;;;;;;; + +; Clojure verwendet Javas Objekt Typen für Booleans, Strings und Zahlen. +; Verwende `class` um sie zu inszipieren. +(class 1) ; Integer Literalte sind standardmäßig java.lang.Long +(class 1.); Float Literale sind java.lang.Double +(class ""); Strings sind immer in doppelten Anführungszeichen notiert und sind java.lang.String +(class false) ; Booleans sind java.lang.Boolean +(class nil); Der "null" Wert heißt nil + +; Wenn du ein literale Liste aus Daten erzeugen willst, verwendest du ' um +; zu verhindern dass es evaluiert wird +'(+ 1 2) ; => (+ 1 2) +; (Kurzform für (quote (+ 1 2))) + +; Du kannst eine zitierte Liste evaluieren +(eval '(+ 1 2)) ; => 3 + +; Kollektioenn & Sequenzen +;;;;;;;;;;;;;;;;;;; + +; Listen sind Linked-Lists Datenstrukturen, während Vektoren arraybasierend sind. +; Vektoren und Listen sind auch Java Klassen! +(class [1 2 3]); => clojure.lang.PersistentVector +(class '(1 2 3)); => clojure.lang.PersistentList + +; Eine Liste würde nur als (1 2 3) geschrieben, aber wir müssen es zitieren +; damit der Leser aufhört zu denken, es sei eine Funktion. +; Außerdem ist (list 1 2 3) das Selbe, wie '(1 2 3) + +; "Kollektioen" sind nur Gruppen von Daten +; Listen und Vektoren sind Kolllektionen: +(coll? '(1 2 3)) ; => true +(coll? [1 2 3]) ; => true + +; "Sequenzen" (seqs) sind abstrakte Beschreibungen von Listen von Daten. +; Nur Listen sind seqs. +(seq? '(1 2 3)) ; => true +(seq? [1 2 3]) ; => false + +; Ein seq muss nur einen Eintrittspunkt bereitstellen, wenn auf ihm zugegriffen wird. +; Das heißt, dass seqs faul sein können -- Mit ihnen kann man unendliche Serien beschreiben. +(range 4) ; => (0 1 2 3) +(range) ; => (0 1 2 3 4 ...) (eine unendliche Serie) +(take 4 (range)) ; (0 1 2 3) + +; Verwende cons um ein Item zum Anfang einer Liste oder eines Vektors hinzuzufügen. +(cons 4 [1 2 3]) ; => (4 1 2 3) +(cons 4 '(1 2 3)) ; => (4 1 2 3) + +; Conj fügt ein Item auf die effizienteste Weise zu einer Kollektion hinzu. +; Für Listen fügt er sie an den Anfang hinzu. Für Vektoren fügt er sie an das Ende hinzu. +(conj [1 2 3] 4) ; => [1 2 3 4] +(conj '(1 2 3) 4) ; => (4 1 2 3) + +; Verwende concat um Listen und Vektoren miteinander zu verbinden +(concat [1 2] '(3 4)) ; => (1 2 3 4) + +; Verwende filter, map um mit Kollektionen zu interagieren +(map inc [1 2 3]) ; => (2 3 4) +(filter even? [1 2 3]) ; => (2) + +; Verwende reduce um sie zu reduzieren +(reduce + [1 2 3 4]) +; = (+ (+ (+ 1 2) 3) 4) +; => 10 + +; Reduce kann auch einen Initalwert als Argument verwenden +(reduce conj [] '(3 2 1)) +; = (conj (conj (conj [] 3) 2) 1) +; => [3 2 1] + +; Funktionen +;;;;;;;;;;;;;;;;;;;;; + +; Verwende fn um neue Funktionen zu erstellen. Eine Funktion gibt immer ihr +; letztes Statement zurück. +(fn [] "Hallo Welt") ; => fn + +; (Du brauchst exta Klammern um sie aufzurufen) +((fn [] "Hallo Welt")) ; => "Hallo Welt" + +; Du kannst eine Variable mit def erstellen +(def x 1) +x ; => 1 + +; Weise eine Funktion einer Variable zu +(def hello-world (fn [] "Hallo Welt")) +(hello-world) ; => "Hallo Welt" + +; Du kannst den Prozess verkürzen indem du defn verwendest +(defn hello-world [] "Hallo Welt") + +; [] ist die Liste der Argumente für die Funktion +; The [] is the list of arguments for the function. +(defn hello [name] + (str "Hallo " name)) +(hello "Steve") ; => "Hallo Steve" + +; Du kannst diese Kurzschreibweise verwenden um Funktionen zu erstellen: +(def hello2 #(str "Hallo " %1)) +(hello2 "Julie") ; => "Hallo Julie" + +; Du kannst auch multi-variadische Funktionen haben +(defn hello3 + ([] "Hallo Welt") + ([name] (str "Hallo " name))) +(hello3 "Jake") ; => "Hallo Jake" +(hello3) ; => "Hallo Welt" + +; Funktionen können auch extra Argumente in einem seq für dich speichern +(defn count-args [& args] + (str "Du hast " (count args) " Argumente übergeben: " args)) +(count-args 1 2 3) ; => "Du hast 3 Argumente übergeben: (1 2 3)" + +; Du kannst reguläre und gepackte Argumente mischen +(defn hello-count [name & args] + (str "Hallo " name ", Du hast " (count args) " extra Argumente übergeben")) +(hello-count "Finn" 1 2 3) +; => "Hallo Finn, Du hast 3 extra Argumente übergeben" + + +; Maps +;;;;;;;;;; + +; Hash maps und Array maps teilen sich ein Interface. Hash maps haben eine schnellere Zugriffszeit, +; aber behalten keine Schlüsselreihenfolge. +(class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap +(class (hash-map :a 1 :b 2 :c 3)) ; => clojure.lang.PersistentHashMap + +; Arraymaps werden durch die meisten Operatioen automatisch zu Hashmaps, +; sobald sie groß genug werden. Das heißt du musst dich nicht darum sorgen. + +; Maps können einen beliebigen Hash-Typ als Schlüssel verwenden, in der Regel +; sind jedoch Keywords am besten. Keywords sind wie Strings, jedoch besitzen sie +; Performancevorteile +(class :a) ; => clojure.lang.Keyword + +(def stringmap {"a" 1, "b" 2, "c" 3}) +stringmap ; => {"a" 1, "b" 2, "c" 3} + +(def keymap {:a 1, :b 2, :c 3}) +keymap ; => {:a 1, :c 3, :b 2} + +; Übrigens werden Kommas als Leerzeichen behandelt und machen nichts. + +; Rufe einen Wert von einer Map ab, indem du sie als Funktion aufrufst +(stringmap "a") ; => 1 +(keymap :a) ; => 1 + +; Keywords können auch verwendet werden um ihren Wert aus der Map zu bekommen! +(:b keymap) ; => 2 + +; Versuche es nicht mit Strings. +;("a" stringmap) +; => Exception: java.lang.String cannot be cast to clojure.lang.IFn + +; Das Abrufen eines nicht vorhandenen Keys gibt nil zurück +(stringmap "d") ; => nil + +; Verwende assoc um einen neuen Key zu einer Hash-map hinzuzufügen +(def newkeymap (assoc keymap :d 4)) +newkeymap ; => {:a 1, :b 2, :c 3, :d 4} + +; Aber denk daran, Clojure Typen sind unveränderlich! +keymap ; => {:a 1, :b 2, :c 3} + +; Verwende dissoc um Keys zu entfernen +(dissoc keymap :a :b) ; => {:c 3} + +; Sets +;;;;;; + +(class #{1 2 3}) ; => clojure.lang.PersistentHashSet +(set [1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3} + +; Füge ein Element mit conj hinzu +(conj #{1 2 3} 4) ; => #{1 2 3 4} + +; Entferne ein Element mit disj +(disj #{1 2 3} 1) ; => #{2 3} + +; Teste auf Existenz, indem du das Set als Funktion verwendest: +(#{1 2 3} 1) ; => 1 +(#{1 2 3} 4) ; => nil + +; Es gibt mehr Funktionen in dem clojure.sets Namespace. + +; Nützliche Forms +;;;;;;;;;;;;;;;;; + +; Logische Konstrukte in Clojure sind nur Makros und sie sehen, wie alles +; andere aus +(if false "a" "b") ; => "b" +(if false "a") ; => nil + +; Verwende let um temporäre Bindungen aufzubauen +(let [a 1 b 2] + (> a b)) ; => false + +; Gruppiere Statements mit do zusammen +; Group statements together with do +(do + (print "Hallo") + "Welt") ; => "Welt" (prints "Hallo") + +; Funktionen haben ein implizites do +(defn print-and-say-hello [name] + (print "Sage Hallo zu " name) + (str "Hallo " name)) +(print-and-say-hello "Jeff") ;=> "Hallo Jeff" (prints "Sage Hallo zu Jeff") + +; let macht das auch +(let [name "Urkel"] + (print "Sage Hallo zu " name) + (str "Hallo " name)) ; => "Hallo Urkel" (prints "Sage Hallo zu Urkel") + + +; Verwende die Threading Makros (-> and ->>) um Transformationen von +; Daten deutlicher auszudrücken. + +; Das "Thread-zuerst" Makro (->) fügt in jede Form das Ergebnis des +; Vorherigen als erstes Argument (zweites Element) ein. +(-> + {:a 1 :b 2} + (assoc :c 3) ;=> (assoc {:a 1 :b 2} :c 3) + (dissoc :b)) ;=> (dissoc (assoc {:a 1 :b 2} :c 3) :b) + +; Dieser Ausdruck kann auch als so geschrieben werden: +; (dissoc (assoc {:a 1 :b 2} :c 3) :b) +; and evaluates to {:a 1 :c 3} + +; Der Doppelpfeil macht das Selbe, aber er fügt das Ergebnis von jeder +; Zeile an das Ende der Form, Das ist vor allem für Operationen auf +; Kollektionen nützlich: +(->> + (range 10) + (map inc) ;=> (map inc (range 10) + (filter odd?) ;=> (filter odd? (map inc (range 10)) + (into [])) ;=> (into [] (filter odd? (map inc (range 10))) + ; Result: [1 3 5 7 9] + +; Wenn du in einer Situation bist, in der du mehr Freiheit willst, +; wohin du das Ergebnis vorheriger Datentransformationen in einem Ausruck +; platzieren möchtest, kannst du das as-> Macro verwenden. Mit diesem Macro +; kannst du einen speziellen Namen auf die Ausgabe einer Transformationen geben. +; Du kannst es als Platzhalter in verketteten Ausdrücken verwenden: + +(as-> [1 2 3] input + (map inc input);=> Du kannst die letzte Ausgabe der Transformation in der letzten Position verwenden + (nth input 2) ;=> und auch in der zweiten Position, im selben Ausdruck verwenden + (conj [4 5 6] input [8 9 10])) ;=> oder auch in der Mitte! + + + +; Module +;;;;;;;;;;;;;;; + +; Verwende "use" um alle Funktionen aus einem Modul zu bekommen +(use 'clojure.set) + +; Nun können wir set Operationen verwenden +(intersection #{1 2 3} #{2 3 4}) ; => #{2 3} +(difference #{1 2 3} #{2 3 4}) ; => #{1} + +; Du kannst auch auswählen nur ein Subset von Funktionen zu importieren +(use '[clojure.set :only [intersection]]) + +; Verwende require um ein Modul zu importieren +(require 'clojure.string) + +; Verwende / um eine Funktion aus einem Modul aufzurufen +; Hier verwenden wir das Modul clojure.string und die Funktion blank? +(clojure.string/blank? "") ; => true + +; Du kannst auch einem Modul einem kürzerern Namen beim Import geben +(require '[clojure.string :as str]) +(str/replace "Das ist ein Test." #"[a-o]" str/upper-case) ; => "DAs IsT EIN TEsT." +; (#"" bezeichnet einen regulären literalen Ausdruck) + +; Du kannst require aus einem Namespace verwenden (auch use ist möglich, aber nicht zu empfehlen) +; indem du :require verwendest. +; Du brauchst keine Zitierzeichen für deine Module verwenden, wenn du +; es auf diese Weise machst. +(ns test + (:require + [clojure.string :as str] + [clojure.set :as set])) + +; Java +;;;;;;;;;;;;;;;;; + +; Java hat eine riesige und nützliche Standardbibliothek, +; du möchtest lernen wie man sie verwendet. + +; Verwende import um ein Java modul zu laden. +(import java.util.Date) + +; Du kannst auch von einem ns importieren. +(ns test + (:import java.util.Date + java.util.Calendar)) + +; Verwende den Klassennamen mit einem "." am Ende, um eine neue Instanz zu erstellen +(Date.) ; + +; Verwende . um Methoden aufzurufen oder verwende die ".method" Abkürzung +(. (Date.) getTime) ; +(.getTime (Date.)) ; Genau das Selbe + +; Verwende / um statische Methoden aufzurufen +(System/currentTimeMillis) ; (system ist immer da) + +; Verwende doto um mit veränderliche Klassen besser umzugehen +(import java.util.Calendar) +(doto (Calendar/getInstance) + (.set 2000 1 1 0 0 0) + .getTime) ; => A Date. set to 2000-01-01 00:00:00 + +; STM +;;;;;;;;;;;;;;;;; + +; Software Transactional Memory ist der Mechanismus, den Clojure verwendet +; um mit persistenten Zuständen umzugehen. Es gibt ein Paar Konstrukte in +; Clojure die es verwenden. + +; Ein Atom ist das Einfachste. Gebe es einen Initalwert +(def my-atom (atom {})) + +; Update ein Atom mit swap!. +; swap! nimmt eine Funktion und ruft sie mit dem aktuellen Zustand des +; Atoms auf und alle nachfolgenden Argumente als das Zweite +(swap! my-atom assoc :a 1) ; Setzt my-atom zu dem Ergebnis von (assoc {} :a 1) +(swap! my-atom assoc :b 2) ; Setzt my-atom zu dem Ergebnis von (assoc {:a 1} :b 2) + +; Verwende '@' um das Atom zu dereferenzieren und den Wert zu bekommen +my-atom ;=> Atom<#...> (Gibt das Atom Objekt zurück +@my-atom ; => {:a 1 :b 2} + +; Hier ist ein einfacher Zähler mit einem Atom +(def counter (atom 0)) +(defn inc-counter [] + (swap! counter inc)) + +(inc-counter) +(inc-counter) +(inc-counter) +(inc-counter) +(inc-counter) + +@counter ; => 5 + +; Andere STM Konstrukte sind refs und agents. +; Refs: http://clojure.org/refs +; Agents: http://clojure.org/agents +``` + +### Weiterführende Literatur + +Das ist alles andere als erschöpfend, aber hoffentlich ist es genug, um dich auf die Beine zu stellen. + +Clojure.org hat eine Menge von Artikeln: +[http://clojure.org/](http://clojure.org/) + +Clojuredocs.org hat eine Dokumentation mit Beispielen für die meisten Kernfunktionen +[http://clojuredocs.org/quickref/Clojure%20Core](http://clojuredocs.org/quickref/Clojure%20Core) + +4Clojure ist eine gute Möglichkeit um deine clojure/FP zu verbessern: +[http://www.4clojure.com/](http://www.4clojure.com/) + +Clojure-doc.org (ja, wirklich) hat eine Reihe von Artikeln zum Starten: +[http://clojure-doc.org/](http://clojure-doc.org/)