From ff51bc12f19d9fe75562757aa4ff89d740a976ae Mon Sep 17 00:00:00 2001 From: Kapil Reddy Date: Fri, 11 Jan 2019 09:25:03 +0530 Subject: [PATCH 1/2] Add solutions for 'Lazy Seq' chapter --- src/icw/data/process.clj | 87 ++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/src/icw/data/process.clj b/src/icw/data/process.clj index db9c5a3..4aee6c9 100644 --- a/src/icw/data/process.clj +++ b/src/icw/data/process.clj @@ -29,14 +29,20 @@ \"Rock\" [\"Rock & Roll\" \"Psychedelic Rock\"]" [line] - ) + (let [line-v (-> line + csv/read-csv + first) + subgenres (-> (last line-v) + csv/read-csv)] + (concat (butlast line-v) + subgenres))) (comment - (= (parse-line "1,1967,Sgt. Pepper's Lonely Hearts Club Band,The Beatles,Rock,\"Rock & Roll, Psychedelic Rock\"") + (= (parse-line "1,1967,Sgt. Pepper's Lonely Hearts Club Band,The Beatles,Rock,\"Rock & Roll,Psychedelic Rock\"") ["1" "1967" - "Sgt. Pepper's Lonely Hearts Club BandThe Beatles" + "Sgt. Pepper's Lonely Hearts Club Band" "The Beatles" "Rock" ["Rock & Roll" @@ -57,13 +63,19 @@ Output {:number \"1\" :year \"1967\" - :artist \"The beatles\"p + :artist \"The beatles\" :album \"Sgt. Pepper's Lonely Hearts Club BandThe Beatles\" :genre \"Rock\" :subgenre-xs [\"Rock & Roll\" \"Psychedelic Rock\"]}" [xs] - ) + (let [[number year album artist genre subgenre] xs] + {:number number + :year year + :artist artist + :album album + :genre genre + :subgenre subgenre})) (comment (= (line-vec->line-map ["1" @@ -81,18 +93,17 @@ :subgenre ["Rock & Roll" "Psychedelic Rock"]})) -(comment - (take 10 - (map #_FIXME - (map #_FIXME - album-lines)))) - (defn line-xs->album-xs [line-xs] ;; Use parse-line to convert list of strings to list of vectors ;; Use line-vec->line-map to convert list of vectors to list of map - ) + (map line-vec->line-map + (map parse-line + line-xs))) + + +(comment (take 1 (line-xs->album-xs album-lines))) (defn populate-db [] @@ -125,14 +136,15 @@ (defn line-xs->albums-xs-before "Lists all albums before 'year'" - [line-xs year] - (comment (filter #_FIXME - (map #_FIXME - #_FIXME)))) + [year line-xs] + (filter #(< (:year %) year) + (map (fn [album] + (update-in album [:year] #(Integer/parseInt %))) + line-xs))) (comment (= (take 5 (map (juxt :year :album) (line-xs->albums-xs-before 1987 - album-lines))) + (line-xs->album-xs album-lines)))) '([1967 "Sgt. Pepper's Lonely Hearts Club Band"] [1966 "Pet Sounds"] [1966 "Revolver"] @@ -165,11 +177,13 @@ (defn find-popular-year ;; Find top years for album releases [line-xs] - (sort-by #_FIXME - (reduce #_FIXME - {} - (map #_ME - line-xs)))) + (sort-by (comp - second) + (frequencies (map :year + (map line-xs->album-xs + line-xs))))) + +(take 5 (map line-xs->album-xs + album-lines)) (comment (= (take 5 (find-popular-year album-lines)) '(["1970" 26] @@ -205,7 +219,7 @@ data-gen/get-albums-xs ;; DONT evaluate the function in REPL it's a lazy sequence. -;; It's a list of albums generated from 2040 till infinity +;; It's a list of albums generated from year 2040 till infinity (take 10 (data-gen/get-albums-xs)) @@ -220,19 +234,34 @@ data-gen/get-albums-xs (comment ;; This will evaluate for infinity since filter does not stop evaluation ;; We will just get infinite list of nils after year 2045 - (line-xs->albums-xs-before (data-gen/get-albums-xs) - 2045)) + (line-xs->albums-xs-before 2045 + (data-gen/get-albums-xs))) ;; https://clojure.org/api/cheatsheet -;; Espeically look for seq in and seq out +;; Especially look for seq in and seq out + +(defn albums-until-year + [year] + (take-while identity + (line-xs->albums-xs-before year + (line-xs->album-xs + (take 1 (data-gen/get-albums-xs)))))) -(#_FIXME identity - (line-xs->albums-xs-before (data-gen/get-albums-xs) - 2045)) +(comment + (take 10 (albums-until-year 2041))) ;; Try applying functions we have created till now +(comment + (-> 2045 + albums-until-year + find-popular-year + (take 10))) + +(comment + (find-popular-artists (until-year 2045))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; END of chapter 1 From 0a120c2b6ff7028532f051fae43879cbd3a19474 Mon Sep 17 00:00:00 2001 From: "Ravindra R. Jaju" Date: Fri, 11 Jan 2019 09:40:41 +0530 Subject: [PATCH 2/2] Added solutions - async, search --- src/icw/async/albums_stream.clj | 11 +++--- src/icw/async/rlsc.clj | 65 ++++++++++++++++++-------------- src/icw/search/core.clj | 2 +- src/icw/search/reader.clj | 67 ++++++++++++++++++--------------- 4 files changed, 81 insertions(+), 64 deletions(-) diff --git a/src/icw/async/albums_stream.clj b/src/icw/async/albums_stream.clj index 8b492e5..470b51d 100644 --- a/src/icw/async/albums_stream.clj +++ b/src/icw/async/albums_stream.clj @@ -6,7 +6,7 @@ [icw.data.gen :as data-gen])) ;; Preamble:1 ends here -;; [[file:~/github/intermediate-clojure-workshop/content/async/albums_stream.org::*Outline][Outline:2]] +;; [[file:~/github/intermediate-clojure-workshop/content/async/albums_stream.org::*Solution][Solution:2]] (defonce counter (atom 0)) (def observing-mapper (map (fn [e] (swap! counter inc) @@ -19,15 +19,16 @@ (defonce generator-loop (go-loop [stream (data-gen/get-albums-xs)] - #_("Introduce an appropriate delay") + ; FIXME + ; (a/! in-ch #_(FIXME stream) :dummy)) - (recur #_(FIXME stream) :dummy))))) + (a/>! in-ch (first stream))) + (recur (rest stream)))))) (defn enable-stream! [] (reset! enabled? true)) (defn disable-stream! [] (reset! enabled? false)) -;; Outline:2 ends here +;; Solution:2 ends here diff --git a/src/icw/async/rlsc.clj b/src/icw/async/rlsc.clj index 6eb0bc5..a7cccc5 100644 --- a/src/icw/async/rlsc.clj +++ b/src/icw/async/rlsc.clj @@ -53,42 +53,51 @@ (shutdown! [this])) ;; The API:1 ends here -;; [[file:~/github/intermediate-clojure-workshop/content/async/rlsc.org::*Outline][Outline:1]] -(defrecord RLSC - [in-ch out-ch process-fn time-gap-ms burst-count] - ;; Any internal state to track?? - +;; [[file:~/github/intermediate-clojure-workshop/content/async/rlsc.org::*Solution][Solution:1]] +(defrecord RLSC [in-ch out-ch process-fn time-gap-ms burst-count + -tokens -shutdown?] RLSCController - ; We need two go processes - ; 1. One that tracks consumption and burst-rate limits - ; 2. The other processes messages per the policy (start! [_] + + ; An independent go process that tracks appropriate increments to the + ; burst-count (go-loop [] - #_("A periodic process that increments tokens")) - (go-loop [#_v #_("read a message")] - "If we have capacity process, else simply pass on to the output channel" - (recur #_("read next message if no shutdown signal")))) - - ; Policy change at run-time - ; This needs to be conveyed to the go-blocks - ; which help us conform to policy - (modify-burst! [this new-burst-count] - #_("update the burst-count") - #_("update tokens available")) - - ; Stop all transformation - ; Signal the go-block logic to clamp down on transformations. + (!! out-ch (process-fn v))) + (counter-- -tokens)) + (do + "No spare capacity. Short-circuit the inward message to the next sink." + (>! out-ch v))) + (if-not @-shutdown? + (recur (RLSC in-ch out-ch process-fn time-gap-ms - (atom burst-count))) -;; Outline:1 ends here + (atom burst-count) + (new-counter burst-count) + (atom false))) +;; Solution:1 ends here ;; [[file:~/github/intermediate-clojure-workshop/content/async/rlsc.org::*Test%20runs][Test runs:1]] (comment diff --git a/src/icw/search/core.clj b/src/icw/search/core.clj index e109daa..5994807 100644 --- a/src/icw/search/core.clj +++ b/src/icw/search/core.clj @@ -30,5 +30,5 @@ (r/search @index field query-term (standard-analyzer))) (comment - (search :subgenre "classic rock")) + (search :album "pepper's")) ;; The Preamble:1 ends here diff --git a/src/icw/search/reader.clj b/src/icw/search/reader.clj index bea5eba..3ad9f36 100644 --- a/src/icw/search/reader.clj +++ b/src/icw/search/reader.clj @@ -9,33 +9,40 @@ [org.apache.lucene.util QueryBuilder])) ;; Code Template:1 ends here -;; [[file:~/github/intermediate-clojure-workshop/content/search/reader.org::*Outline][Outline:1]] -(defn ^IndexReader index-reader [^Directory directory]) - -(defn ^IndexSearcher searcher [^Directory directory]) - -(defn field->kv [^Field f]) - -(defn doc->map [^Document doc]) - -(defn ^Query query [^clojure.lang.Keyword field - ^String term - ^Analyzer analyzer]) - -(defn score-docs->ids [^"[Lorg.apache.lucene.search.ScoreDoc;" score-docs]) - -(defn doc-ids->docs [^IndexSearcher searcher doc-ids]) - -; Create IndexSearcher given the index (Directory instance) -; Create a Query object given the field, term and analyzer -; .search on the IndexSearcher the generated Query -; Get .scoreDocs from the response -; ScoreDoc instances give you document-ids as handles -; Using the document-ids, get the Document instances from the IndexSearcher -; Convert the collection to maps with doc->map -; Enjoy the convenience of Clojure's support for Iterable -(defn search [^Directory directory - ^clojure.lang.Keyword field - ^String search-term - ^Analyzer analyzer]) -;; Outline:1 ends here +;; [[file:~/github/intermediate-clojure-workshop/content/search/reader.org::*Solution][Solution:1]] +(defn index-reader [directory] + (DirectoryReader/open directory)) + +(defn searcher [directory] + (IndexSearcher. (index-reader directory))) + +(defn field->kv [f] + [(-> f .name keyword) + (.stringValue f)]) + +(defn doc->map [d] + (into + {} + (map field->kv d))) + +(defn query [field term analyzer] + (let [qb (QueryBuilder. analyzer) + field (name field)] + (. qb + (createBooleanQuery field term)))) + +(defn score-docs->ids [^"[Lorg.apache.lucene.search.ScoreDoc;" score-docs] + (map (fn [score-doc] (.doc score-doc)) score-docs)) + +(defn doc-ids->docs [searcher doc-ids] + (map (fn [doc-id] (.doc searcher doc-id)) doc-ids)) + +(defn search [directory field search-term analyzer] + (let [q (query field search-term analyzer) + searcher (searcher directory) + search-results (.search searcher q 10) + score-docs (.scoreDocs search-results) + doc-ids (score-docs->ids score-docs) + docs (doc-ids->docs searcher doc-ids)] + (map doc->map docs))) +;; Solution:1 ends here