Skip to content

Instantly share code, notes, and snippets.

@semperos
Forked from gfmurphy/Div.hs
Last active September 11, 2015 22:12
Show Gist options
  • Save semperos/4e51aa26470c3120dfd5 to your computer and use it in GitHub Desktop.
Save semperos/4e51aa26470c3120dfd5 to your computer and use it in GitHub Desktop.
Readability
(defn digits
"Generate a list of digits contained in the number"
[number]
(loop [found-digits '() base (quot number 10) digit (rem number 10)]
(let [found-digits (conj found-digits (if (neg? digit) (- digit) digit))]
(if (zero? base)
found-digits
(recur found-digits (quot base 10) (rem base 10))))))
(defn divisible-digits
"Return the count of digits that evenly divide into the given number"
[number]
(let [divisible (filter (fn [n] (and (not (zero? n)) (= 0 (rem number n)))) (digits number))]
(count divisible)))
(map divisable-digits '(1024 2048 64))
def digits(base)
[].tap { |digits|
begin
base, digit = base.divmod(10)
digits << digit
end while base != 0
}
end
def divisible_digits(number)
digits(number).count { |d| !d.zero? && (number % d == 0) }
end
[1024, 2048, 64].map { |n| divisible_digits(n) }
;; See http://clojure.org/transducers for more information
(def digits
"A transducer that calls `Character/getNumericValue` on each item."
(map #(Character/getNumericValue %)))
(def not-zero?
"A transducer that drops items equal to zero."
(remove zero?))
;; NOTE the `defn` here as opposed to `def` above.
(defn divisible?
"A function which returns a transducer that filters by items evenly divisible by the given `number`."
[number]
(filter #(zero? (rem number %))))
;; USAGE
;; Transducers work with sequential things, which numbers are not:
(sequence digits 5430)
;=> EXCEPTION: IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:528)
;; But make that number a string and you have something you can work with:
(sequence digits (str 5430))
;=> (5 4 3 0)
;; Now lets combine our transducer for more interesting effects:
(sequence (comp digits not-zero?) (str 5430))
;=> (5 4 3)
;; If you're used to using `comp`, you'll notice that worked _in reverse_.
;; For transducers, `comp` reads from left-to-right instead of right-to-left.
;; How does our `divisible?` function work?
(sequence (divisible? 12) [3 4 5 6])
;=> (3 4 6)
;; Ok, it'll require one extra set of parentheses because instead of _being_ a transducer,
;; our `divisible?` function _returns_ a transducer that's custom-built for the `number` we input.
;; We can use `(divisible? number)` just like `digits` and `not-zero?`:
(sequence (comp digits not-zero? (divisible? 5430)) (str 5430))
;=> (5 3)
;; Now we can just call `count` on that result and we're done:
(count (sequence (comp digits not-zero? (divisible? 5430)) (str 5430)))
;=> 2
;; With all that, the `divisible-digits` function could be written like this:
(defn divisible-digits
[number]
(->> (str number)
(sequence (comp digits not-zero? (divisible? number)))
count))
;; You could also opt to make `divisible-digits` just like `divisible?`, that is a
;; function that just returns another transducer. That would allow you to keep composing
;; these transducers if you found you needed to add further constraints.
;; Read the Clojure documentation at http://clojure.org/transducers
;; to understand the benefits of using transducers in general. For this case,
;; I think the code is equally readable either way.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment