Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
lets print a triangle of stars for fun and profit
;;;;;;;;;Given this yucky implementation:
(defn print-row [num]
(loop [iter 0])
(if (< iter num)
(do (print "*")
(recur (inc iter)))
(println "")))
(defn print-a-triangle [num]
(loop [iter 0]
(if (< iter num)
(do (print-row iter)
(recur (inc iter)))
(print ""))))
;;;;Lets refactor this to be more functional, idiomatic Clojure, overall more lovely...your metrics may vary
;;;;First lets toss the notion that just because the end result is a side effect, in this case printing stars, we need to start off in that direction
;;;;instead of writing a function to print a row, lets write one to create a row
(defn make-row
"you give me a positive number and I'll give you a string of that many stars
you give me anything else and I'll send a swarm of wasps to your home when you least expect it"
[star-count]
(apply str (repeat star-count "*")))
;;lets give it a go at our hypothetical repl here:
;;(make-row 5) => "*****"
;;(make-row -1) => *dies of wasp attack when I least suspected it*
;;;good, that's working, now what? well a valid triangle follows a very strict set of rules regarding its sides:
;;;The sum of the lengths of any two sides of a triangle is greater than the length of the third side
;;;If we create our rows with consecutive numbers from 1 to n we should be good
;;;But it never hurts to validate
(defn make-triangle
"given a positive number will a triangle with that many rows
ex: (make-triangle 3) => (* ** ***)"
[max-rows]
(take max-rows (map make-row (iterate inc 1))))
;;;lets give it a test:
;;;(make-triangle 3) => ("*" "**" "***")
;;;Execellent now lets validate our triangle
;;;here is the general function for validating a triangle
(defn valid-triangle? [side-a side-b side-c]
(and (> (+ side-a side-b) side-c)
(> (+ side-b side-c) side-a)
(> (+ side-c side-a) side-b)))
;;;and here is the function for validating the triangle that we've produced
(defn valid-star-triangle?
"takes a collection of stars representing the star triangle and calculates the
length of each side and returns true if all sides follow the rules of a valid triangle
side-a is calculated by counting the number of rows
side-b is calculated by adding the distance between each row
side c is calculated by finding the length of largest row"
[triangle]
(let [a (count triangle)
b (reduce + (map #(apply - (reverse %)) (partition 2 1 (map count triangle))))
c (apply max (map count triangle))]
(valid-triangle? a b c)))
;;lets test it on a triangle we know is good
;;(valid-star-triangle? ["*" "**" "***"]) => true
;;and now lets test it on a square
;;(valid-star-triangle? ["***" "***" "***"]) => false
;;I'll feel more confident after generative tests are run but this is good for now
;;now lets write a function that prints the thing
(defn print-triangle
"takes a positive number, creates a triangle with that many rows,
and prints each row to *stdout* if the triangle is valid"
[max-rows]
(let [star-triangle (make-triangle max-rows)
valid? (valid-star-triangle? star-triangle)]
(if valid? (doseq [row star-triangle] (println row))
(throw (Exception. "cannot print invalid triangle")))))
;;Exercise left up to the reader: create specs for this code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.