Last active
February 4, 2020 23:37
-
-
Save finalfantasia/0737f3c64add5a8f2e3593e2ef9ec2d0 to your computer and use it in GitHub Desktop.
An Example for Transduce with Completing Reducing Function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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