Created
November 29, 2017 17:56
-
-
Save gdeer81/6d93e6938ef0ed1a0264d4a34e635d66 to your computer and use it in GitHub Desktop.
lets print a triangle of stars for fun and profit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;;;;;;;;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