Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save finalfantasia/0737f3c64add5a8f2e3593e2ef9ec2d0 to your computer and use it in GitHub Desktop.
Save finalfantasia/0737f3c64add5a8f2e3593e2ef9ec2d0 to your computer and use it in GitHub Desktop.
An Example for Transduce with Completing Reducing Function
(def lines ["inside every large program"
"is a small program"
"struggling to get out"])
;; a stateful transducer that calculates running total
(defn xf-running-total
[]
(fn [rf]
(let [state (volatile! {})]
(fn
([] (rf))
([result] (rf result))
([result m]
(let [next (as-> m %
(vswap! state (partial merge-with (fnil + 0)) %)
(select-keys % (keys m))
(map vec %))]
(rf result next)))))))
(def count-words (comp (map #(clojure.string/split % #"\s+"))
(map frequencies)
(xf-running-total)))
(transduce count-words concat lines)
;; =>
;; (["inside" 1] ["every" 1] ["large" 1] ["program" 1] ["is" 1] ["a" 1] ["small" 1] ["program" 2] ["struggling" 1] ["to" 1] ["get" 1] ["out" 1])
;;; The following is a solution that gives us the undesirable result:
;;; {"every" 1, "is" 1, "small" 1, "a" 1, "out" 1, "inside" 1, "large" 1, "to" 1, "struggling" 1, "get" 1, "program" 2}
;;; instead of:
;;; (["inside" 1] ["every" 1] ["large" 1] ["program" 1] ["is" 1] ["a" 1] ["small" 1] ["program" 2] ["struggling" 1] ["to" 1] ["get" 1] ["out" 1])
(def lines ["inside every large program"
"is a small program"
"struggling to get out"])
(def count-words (comp (map #(clojure.string/split % #"\s+"))
(map frequencies)))
;; `clojure.core/completing` takes a reducing function f of
;; 2 arguments (`result` and `x`) and returns a function
;; suitable for transduce by adding a unary signature that
;; calls `cf` (default - `clojure.core/identity`) on the `result` argument.
(def f (completing (fn [result x] (merge-with + result x))))
(transduce count-words f {} lines)
;; =>
;; {"every" 1, "is" 1, "small" 1, "a" 1, "out" 1, "inside" 1, "large" 1, "to" 1, "struggling" 1, "get" 1, "program" 2}
;; Alternatively, since `clojure.core/merge-with` is a variadic function with regards
;; to map arguments and no other function needs to be called on the `result` argument
;; (effectively the same as calling the default `cf` function, `clojure.core/identity`,
;; as in the example for `clojure.core/completing`), `f` may be simplified as follows:
(def f (partial merge-with +))
;; and call `clojure.core/transduce` with it:
(transduce count-words f {} lines)
;; =>
;; {"every" 1, "is" 1, "small" 1, "a" 1, "out" 1, "inside" 1, "large" 1, "to" 1, "struggling" 1, "get" 1, "program" 2}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment