Skip to content

Instantly share code, notes, and snippets.

@bjeanes
Last active October 11, 2015 18:47
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bjeanes/3902930 to your computer and use it in GitHub Desktop.
Save bjeanes/3902930 to your computer and use it in GitHub Desktop.
Change Maker in clojure
(ns katas.change-maker)
(defn num-pairs
"Returns all combinations of [x y] and (<= x y) where (= cents (+ x y))"
[cents]
(for [x (range 1 cents)
:let [y (- cents x)]
:when (<= x y)]
[x y]))
;; Not tail recursive so can blow the stack
(defn change-maker
([cents] (change-maker cents '(25 10 5 1)))
([cents coins]
(cond
(zero? cents) []
(some #{cents} coins) [cents]
:else (let [pairs (num-pairs cents)
stacks (for [[x y] pairs]
(concat
(change-maker x coins)
(change-maker y coins)))
optimal-stack (apply min-key count stacks)]
(reverse (sort optimal-stack))))))
(alter-var-root #'change-maker memoize)
(ns katas.change-maker)
;; tail recursive but naive — will miss some test cases
(defn change-maker
([cents] (change-maker cents '(25 10 5 1) []))
([cents coins] (change-maker cents coins []))
([cents coins change]
(if (or (zero? cents) (empty? coins))
change
(let [coin (apply max (filter (partial >= cents) coins))]
(recur (- cents coin) coins (conj change coin))))))
(ns katas.change-maker-test
(:use clojure.test
katas.change-maker))
(deftest change-maker-tests
(are [cents coins] (= (change-maker cents) coins)
26 [25 1]
24 [10 10 1 1 1 1]
49 [25 10 10 1 1 1 1]
0 [])
(are [cents in-coins out-coins] (= (change-maker cents in-coins) out-coins)
26 [10 25 1 5] [25 1]
;; the following two won't work with a greedy change maker:
24 [10 7 1] [10 7 7]
14 [10 7 1] [7 7]))
@gfredericks
Copy link

I bet changing line 7 to (reverse (range 1 cents)) would cause it to not blow the stack on any reasonable input.

@gfredericks
Copy link

Also the second set of tests can be a little cleaner since are supports more than two bindings:

(are [cents in-coins out-coins] (= (change-maker cents in-coins) out-coins)
  26 [10 25 1 5] [25 1]
  ...)

@bjeanes
Copy link
Author

bjeanes commented Oct 17, 2012

Oh duh... I didn't even think about having more than two bindings. That's pretty fricking obvious :P. Damn it >.<

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment