Stefan Kamphausen
Fragen fesseln
Sie berühren uns
Sie regen unseren Denkapparat an
Dadurch werden wir aufmerksamer und weniger müde
Erfahrungen damit, gute Erfahrungen, ...
Gibt es nur noch einen Punkt pro Slide
Und die Vortragenden klicken sich viel zu schnell durch
So schnell, dass
es kaum noch
möglich ist
dem
Bild
und
Audiovortrag
gleichzeitig zu
folgen
Außerdem geraten bei InfoQ die Dinge so auseinander, dass wir raten müssen, was der Vortragende wohl gerade meint und die Zuschauer gerade sehen.
Vielen Dank auch.
Das deutsche Clojure-Buch wird in Kürze unter http://www.clojure-buch.de vollständig zu lesen sein.
Sollte es seitens der Gemeinschaft der deutschsprachigen Clojure-Freunde ein Interesse geben, auf dem Material aufzubauen, werden wir das Material gerne unter eine passende offene Lizenz stellen.
"In Kürze" ist dehnbar
first
)next, rest
)rest
liefert immer eine Seq, potentiell leer,
während next
am Ende nil
liefert und dafür ein Element
weiter vorausschauen mussmap
, filter
, reduce
, mapcat
,
partition
, every?
, some
, take
,
drop
, remove
, ...
(interleave [:a :b :c] (iterate inc 0))
;;=> (:a 0 :b 1 :c 2)
(map + [1 2 3] [10 20 30 40 50 60])
;;=> (11 22 33)
(def natz (iterate inc 1))
(time (nth natz 1000000))
;; "Elapsed time: 239.345539 msecs"
;;=> 1000001
(time (nth natz 1000000))
;; "Elapsed time: 15.664133 msecs"
;;=> 1000001
(defn pinc [i]
(print (str "<" i ">"))
(inc i))
(take 2 (map pinc (iterate inc 0)))
;; (<0><1>1 2)
;; Ausgabe und Rückgabewert vermischt
(take 2 (map pinc [1 2 3 4 5 6 7 8 9]))
;; (<1><2><3><4><5><6><7><8><9>2 3)
;; Vektoren implementieren IChunkedSeq
(map inc (range 10))
;;=> (1 2 3 4 5 6 7 8 9 10)
(map inc (range 10))
;;=> (1 2 3 4 5 6 7 8 9 10)
na und??
Zu Risiken und Nebenwirkungen ...
map
eigentlich wirklich?Was ist die Essenz?
Wende eine Funktion auf jedes Element an
Ist das inhärent nicht parallelisierbar?
Nö
Haben will
(defn podd? [i]
(print (str "(" i ")"))
(odd? i))
(take 2 (filter podd? (iterate inc 0)))
;; ((0)(1)(2)(3)1 3)
(take 2 (filter podd? (range 9)))
;; ((0)(1)(2)(3)(4)(5)(6)(7)(8)1 3)
... alles wie gehabt: lazy, sequentiell
Parallisierbar? Nein. Inhärent? Auch nicht.
Haben will? Aber sicher!
;; (reduce red-fn init? coll)
(reduce + [1 2 3 4])
;;=> 10
(reduce (fn [acc x]
(assoc acc x (inc (get acc x 0))))
{} "Hallo Welt")
;;=> {\t 1, \e 1, \W 1, \space 1, \o 1, \l 3, \a 1, \H 1}
(reductions + [1 2 3 4])
;;=> (1 3 6 10)
map
, filter
, etc kennen die Datenstrukturen nichtinc
reduce
ein Zugangspunkt in die
DatenstrukturenLassen sich map
, filter
& Co. alternativ auf Basis
von reduce
implementieren?
Fokus: im Speicher, Performanz
Anwendungsfall: danach wird reduce
verwendet.
(reduce + (map inc (filter odd? (range 10))))
Ja, das funktioniert
(require '[clojure.core.reducers :as r])
(def zahlen (doall (range 1e7)))
(defn standard [daten]
(reduce + (map inc
(filter odd? daten))))
(time (standard zahlen))
;; "Elapsed time: 1107.320599 msecs"
;;=> 25000005000000
(defn not-lazy [data]
(reduce + (r/map inc
(r/filter odd? data))))
(time (not-lazy zahlen))
;; "Elapsed time: 687.40897 msecs"
;;=> 25000005000000
Das hier ist doch der SourceTalk?
reduce
map
mapping
verschiebt die Auswertung in die
Reducing-Funktionfiltering
entsprechend
(defn mapping [map-fn]
(fn [red-fn]
(fn [acc element]
(red-fn acc (map-fn element)))))
(defn filtering [pred]
(fn [red-fn]
(fn [acc element]
(if (pred element)
(red-fn acc element)
acc))))
;; (reduce ((mapping inc +) 0 [1 2 3]))
;;=> 9
reduce
verwendet das Protocol CollReduce
CollReduce
kann für Datenstrukturen erweitert werdenr/map
liefert ein Objekt, das CollReduce
implementiert
(seq (.getInterfaces
(class (r/map inc [1 2]))))
;;=> (clojure.core.reducers.CollFold clojure.core.protocols.CollReduce
;; clojure.lang.IObj)
(defn reducer
([coll xf]
(reify clojure.core.protocols/CollReduce
(coll-reduce [_ f1 init]
(clojure.core.protocols/coll-reduce coll (xf f1) init)))))
Boilerplate-Code wird von passenden Makros hinweggefegt
reduce
seit Clojure 1.3 ein Protocolmap
auf die Reducing-Funktion:-/
Jetzt endlich
fold
(defn parallel [data]
(r/fold + (r/map inc
(r/filter odd? data))))
;; Zur Erinnerung
(time (not-lazy zahlen))
;; "Elapsed time: 699.521045 msecs"
(time (parallel zahlen))
;; "Elapsed time: 689.899531 msecs"
;; meh?
Wieso funktioniert das hier nicht?
(def zahlen-v (vec zahlen))
(time (standard zahlen)) ;; "Elapsed time: 1021.680964 msecs"
(time (not-lazy zahlen)) ;; "Elapsed time: 705.568693 msecs"
(time (parallel zahlen)) ;; "Elapsed time: 693.122998 msecs"
(time (standard zahlen-v)) ;; "Elapsed time: 1054.37094 msecs"
(time (not-lazy zahlen-v)) ;; "Elapsed time: 723.872857 msecs"
(time (parallel zahlen-v)) ;; "Elapsed time: 205.894815 msecs"
+
sind trivial, denn +
hat zwei wichtige
Eigenschaften
(+)
;;=> 0
(= (+ 4 (+ 5 6)) (+ (+ 4 5) 6))
;;=> true
reduce
unterstützt solche Reducing-Funktionen durch die passende
Arity+--------------------------------------------------------------+ | | +--------------------------------------------------------------+ +-----------------------------+ +------------------------------+ | | | | +-----------------------------+ +------------------------------+ +--------------+ +------------+ +-------------+ +--------------+ | | | | | | | | +--------------+ +------------+ +-------------+ +--------------+ +----+ +----------+ /-------\ +----+ +----------+ |Init| | data | -> | combine | <- |Init| | data | +----+ +----------+ \-------/ +----+ +----------+
Beispiel: zählen von Elementen (frequencies)
(defn freqs-fold [items]
(r/fold
(r/monoid (partial merge-with +)
(constantly {}))
(fn [acc el]
(assoc acc el (inc (get acc el 0))))
items))
WT.?
(merge-with + {:a 2} {:a 4})
;;=> {:a 6}
((partial merge-with +) {:a 2} {:a 4})
;;=> {:a 6}
;; mono-was?
(def mo (r/monoid + (constantly 0)))
(mo)
;;=> 0
(mo 1 2)
;;=> 3
reduce
; häufig?into
)
Copyright (c) 2013 Stefan Kamphausen
reveal.js, libraries etc. see LICENSE file