Skip to content

Instantly share code, notes, and snippets.

@gdeer81
Created November 29, 2017 17:56
Show Gist options
  • Save gdeer81/6d93e6938ef0ed1a0264d4a34e635d66 to your computer and use it in GitHub Desktop.
Save gdeer81/6d93e6938ef0ed1a0264d4a34e635d66 to your computer and use it in GitHub Desktop.
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