{:check ["true"], :ranks ["for" "for_advanced"]}
(require '[clojure.pprint :refer [pprint]])
(for ...)¶;
; (for ...) evaluates to a sequence
;
(for [i (range 10)]
    (str "i=" i))
(def students [{:name "Jack" :grade 90 :course "Math"}
               {:name "Jill" :grade 88 :course "English"}
               {:name "Joe" :grade 79 :course "English"}])
;
; (for [<var> <seq>]
;     ...)
; - creates a new scope with the additional symbol binding for <var>.
; - each element in seq is processed exactly once
(for [x students]
    (get x :name))
(for [x students]
    (format "%s received %d%% for %s" (:name x)  (:grade x) (:course x)))
;
; (for ...) is a data expression, so it can be used anywhere where
; a sequence is expected.
; 
(pprint (for [x students]
            (format "%s -> %d" (:name x) (:grade x))))
(println (clojure.string/join "\n" (for [x students]
                                       (format "%s -> %d" (:name x) (:grade x)))))
;
; We can iterate through a hash-map as sequence of [key/value] pairs.
;
(def people {"Albert Einstein" {:profession "Physicist"
                                 :date-of-birth {:month "March"
                                                 :day 17
                                                 :year 1879}}
              "Elvis Presley" {:profession "Musician"
                               :date-of-birth {:month "January"
                                               :day 8
                                               :year 1935}}
              "Martin Luther King Jr" {:profession "Minister",
                                       :date-of-birth {:month "January"
                                                       :day 15
                                                       :year 1929}}})
(count courses)
(for [[name info] courses]
    (format "%s worked as a %s" name (:profession info)))
;
; Idiomatic Clojure -- use functions
;
(defn pad-value [x width]
    (let [string (str x)
          padding (- width (count string))]
        (str x (clojure.string/join "" (repeat padding " ")))))
(pad-value 3.1415 10)
(defn format-info [name info]
    (let [prof (:profession info)
          month (get-in info [:date-of-birth :month])
          year (get-in info [:date-of-birth :year])]
        (str "| "
             (pad-value name 30)
             "| "
             (pad-value prof 20)
             "| "
             (pad-value (format "%d/%s" year month) 20)
             "|")))
(format-info "Albert Einstein" (get people "Albert Einstein"))
(println
 (clojure.string/join 
  "\n"
  (for [[name info] people]
     (format-info name info))))
;
; for supports iteration over
; multiple sequences using
; variable bindings
;
(for [x (range 5)
      y (range x)]
    [x y])
;
; (for ...) allows filtering
;
(for [x (range 5)
      y (range x)
      :when (odd? (+ x y))]
    [x y])
;; about 3 seconds
(time (apply + (for [i (range 1e8)] (Math/pow i 2))))
(time (def lots-of-squares (for [i (range 1e8)] (Math/pow i 2))))
(take 10 lots-of-squares)