So I wrote a Python program that allows you to roll two dice and it will tell you the probabilitiy of obtaining a specific sum with those two dice. I found that writing it in clojure was so much more concise than in Python. Prehaps it is because I already wrote it in Python but the code itself in Clojure is shorter and it could even be shorter with some adjustments. When writing it in Python pretty much worked like a charm and there was functions to do everything already so considering that Clojure still ended up shorter with 30 lines of code while the Python code is 48 lines. I do think that the Python code could be adjusted to be shorter as well but it is pretty amazing that Clojure code can be written to do the same thing as Python which is extremely idiomatic.
This is the Clojure code below:
(ns dice.core)
(defn create-dice [low high]
(for [i (range low high)]
(map (fn [b] (+ i b)) (range low high))))
(defn outcome [arg]
(reduce + (for [i arg]
(count i))))
(defn count-occurrences [s list]
(count (filter #{s} (flatten list))))
(defn percentage [min max dice outcome]
(for [x (range min (+ max 1))]
(println x " " (count-occurrences x dice) " " (format "%.4f" (* 100 (float (/ (count-occurrences x dice) outcome)))) "%")))
(defn stats [low high]
(let [dice-pos (create-dice low high)
outcome (outcome dice-pos)
max (apply max (flatten dice-pos))
min (apply min (flatten dice-pos))]
(println "\n\nDice Possibilities: " dice-pos "\n\n")
(println "Total Number of Possibilities: " outcome)
(println "The Max Sum: " max)
(println "The Min Sum: " min "\n\n")
(println "Sum: #ofPossibilities: Percentage %:")
(percentage min max (flatten dice-pos) outcome)))
(stats 1 7)
One of the great things about Clojure is that performing mathmatical calculations feels so natural which is probably because of drawing from haskell. Its range, max, min functions go so well with apply along with using map to display all the sums in three lines.(defn create-dice [low high]
(for [i (range low high)]
(map (fn [b] (+ i b)) (range low high))))
To demonstrate this a little more graphically on what exactly is going on in this you can think of it like this where each element of the first list is being added to all of the elements in the second list and than moving on to the second element in the first list and than adding it to all the elements in the second list until all the elements in the first list are used up.Next we want to figure out how many elements there are or how many outcomes there can be when using two dice. We can use the count function to do this. However just using count will give us 6 since it only accounts for the lists which are elements. But we want the elements within those lists. This is where the function defination outcome comes into play which uses a for loop.
(defn outcome [arg]
(reduce + (for [i arg]
(count i))))
The argument that outcome will take the the list produced by the create-dice function and will count the amount of elements in each list. Since the list is from 1 to 6 that should give us 6 in each list. There is 6 lists with 6 elements in each so thus a total of 36 is produced using the outcome function. Why do we need the total amount of elements in the list? Well this is because to find probability we need to do the occurances of each sum over the total number of outcomes. (defn count-occurrences [s list]
(count (filter #{s} (flatten list))))
First let me talk about flatten and what it is doing to the list that will be given as an argument to the count-occurrences. Right now our sum possibilities list contains 6 lists within a list. In order to filter through that we need just a list of all the elements in the 6 lists. So in order to have just one list instead of having 6 lists within a list we use flatten. After flattening all the lists into one retaining all the elements in each one we can now use filter which takes a regex expression. In this case the regex expression is an argument that count-occurrences will take. In the next function you'll see how everything is put together and all the functions are used to display the final result.(defn percentage [min max dice outcome]
(for [x (range min (+ max 1))]
(println x " " (count-occurrences x dice) " " (format "%.4f" (* 100 (float (/ (count-occurrences x dice) outcome)))) "%")))
First yes I realize that the println section is hideous to look at with those long spaces. But I wasn't really focused as making the code appear nice as just to display the results in a sensible format. In the end we get this insanely long println line.(defn stats [low high]
(let [dice-pos (create-dice low high)
outcome (outcome dice-pos)
max (apply max (flatten dice-pos))
min (apply min (flatten dice-pos))]
(println "\n\nDice Possibilities: " dice-pos "\n\n")
(println "Total Number of Possibilities: " outcome)
(println "The Max Sum: " max)
(println "The Min Sum: " min "\n\n")
(println "Sum: #ofPossibilities: Percentage %:")
(percentage min max (flatten dice-pos) outcome)))
Finally you have stats which calls all of the functions which we just defined and prints a bunch of other information.