Skip to content

Instantly share code, notes, and snippets.

@camsaul
Created March 28, 2019 01:55
Show Gist options
  • Save camsaul/0dbfbc917e65a223fd0e0614b8298878 to your computer and use it in GitHub Desktop.
Save camsaul/0dbfbc917e65a223fd0e0614b8298878 to your computer and use it in GitHub Desktop.
break-coins-with-core-logic.clj
(ns my-coins-project.core
(:require [clojure.core.logic :as l]
[clojure.core.logic.fd :as fd]))
(def ^:private coins
{:penny 1, :nickel 5, :dime 10, :quarter 25})
(defn- fd-sum
"Like fd/+ but for more than 2 lvars."
[[lv1 lv2 & more] total]
(if-not (seq more)
(fd/+ lv1 lv2 total)
(l/fresh [subtotal]
(fd/+ lv1 lv2 subtotal)
(fd-sum (cons subtotal more) total))))
(defn- change [amount]
;; [coin quantity value]
(let [coins+lvars (for [[coin-name coin-value] coins]
{:coin-name coin-name
:coin-value coin-value
:quantity (l/lvar)
:total-value (l/lvar)})]
(l/run* [q]
;; result == map of coin-name -> quantity
(l/== q (into {} (for [{:keys [coin-name quantity]} coins+lvars]
[coin-name quantity])))
;; coin-value * quantity == total-value
(l/everyg
(fn [{:keys [coin-value quantity total-value]}]
(fd/* coin-value quantity total-value))
coins+lvars)
;; each coin total value is between 0 and the `amount` we're making change for
(l/everyg
(fn [{:keys [total-value]}]
(fd/in total-value (fd/interval 0 amount)))
coins+lvars)
;; sum of values == amount
(fd-sum (map :total-value coins+lvars) amount))))
@camsaul
Copy link
Author

camsaul commented Mar 28, 2019

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