diff --git a/.gitignore b/.gitignore index 8b52762..ea04284 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,17 @@ +/target +/classes +/checkouts +profiles.clj /pom.xml -*jar +/pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port +.hgignore +.hg/ /lib -/classes /native /.lein-failures -/checkouts /.lein-deps-sum -*.DS_Store \ No newline at end of file +*.DS_Store diff --git a/README.md b/README.md index f8e03b3..8e67031 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ -coding-exercises +Coding Exercises ================ +**Worki in progress** + +Currently refactoring this code to write tests for each challenge. The challenges are going to be empty functions which will fail the test. When the refactoring is completed, the solutions wil exist in a different branch as a reference. + Coding exercises, handy practice for technical interview questions. I will put up my solutions to some popular questions and exercises in this repo, and welcome any feedback or suggestions! All of the exercises are coded in my favorite language, Clojure. @@ -12,3 +16,4 @@ Most of the exercises are taken from "Cracking the Coding Interview, 4th Edition Copyright (C) 2012 David Petrovics Distributed under the Eclipse Public License, the same as Clojure. + diff --git a/project.clj b/project.clj index a114e5a..5699316 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,10 @@ (defproject coding-exercises "1.0.0-SNAPSHOT" :description "FIXME: write description" - :dependencies [[org.clojure/clojure "1.3.0"] - [org.clojure/math.numeric-tower "0.0.1"]]) + :url "http://example.com/FIXME" + :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" + :url "https://www.eclipse.org/legal/epl-2.0/"} + :dependencies [[org.clojure/clojure "1.10.0"] + [org.clojure/math.numeric-tower "0.0.1"]] + :main ^:skip-aot coding-exercises.core + :target-path "target/%s" + :profiles {:uberjar {:aot :all}}) diff --git a/src/coding_exercises/core.clj b/src/coding_exercises/core.clj index 2805aa2..f55b93f 100644 --- a/src/coding_exercises/core.clj +++ b/src/coding_exercises/core.clj @@ -1,38 +1,51 @@ (ns coding-exercises.core - (:require [clojure.string :as str] + (:require [clojure.set :as set :only [union]] [clojure.math.numeric-tower :as math])) (defn basic-fib ^{:doc "Returns the n-th number in the fibonacci sequence."} [num] - (if (= num 0) + (if (<= num 0) 0 (if (= num 1) 1 (+ (basic-fib (- num 2)) (basic-fib (- num 1)))))) -(defn lazy-fib - ^{:doc "Returns a lazy sequnence of the fibonacci sequence. Use with 'take' or 'nth'"} - [] - (map first - (iterate (fn [[a b]] [b (+ a b)]) [0 1]))) - -(def map-example - "You have a collection of 2-tuples (user, follower). You want to get out (user, [followers]). Solution: put them all into a map, and use merge-with.") +;(defn basic-fib +; ^{:doc "Returns the n-th number in the fibonacci sequence."} +; [num]) -(def inputs - '(("Angie" "Dave") ("Angie" "Max") ("Bill" "Joe") ("Joe" "Moe") ("Bill" "Dave") ("Mike" "Dave") ("Angie" "Bill"))) +(defn user-followers + "You have a collection of 2-tuples (user, follower). You want to get out (user, [followers]). Solution: put them all into a map, and use merge-with." + [input] + (reduce (fn [r n] + (assoc r + (first n) + (set (conj (r (first n)) (last n))))) + {} input)) (defn user-followers [uf-list] - (let [usr-map (map #(apply hash-map %1) uf-list)] + (let [usr-map (map #(hash-map (first %1) #{(last %1)}) uf-list)] (apply merge-with - #(if (seq? %1) - (conj %1 %2) - (list %1 %2)) + #(set/union %1 %2) usr-map))) +;(defn user-followers +; "You have a collection of 2-tuples (user, follower). You want to get out (user, [followers]). Solution: put them all into a map, and use merge-with." +; [input]) + +(defn lazy-fib + ^{:doc "Returns a lazy sequnence of the fibonacci sequence. Use with 'take' or 'nth'"} + [] + (map first + (iterate (fn [[a b]] [b (+ a b)]) [0 1]))) + +;(defn lazy-fib +; ^{:doc "Returns a lazy sequnence of the fibonacci sequence. Use with 'take' or 'nth'"} +; []) + (defn clock-angle ^{:doc "Finds the angle in degrees between the hour hand and minute hand."} [hr min] @@ -42,18 +55,70 @@ hr-angle (* (+ (mod hr 12) (/ min 60)) (/ 360 12))] (float - (math/abs (- hr-angle min-angle))))) + (math/abs (- hr-angle min-angle))))) + +;(defn clock-angle +; ^{:doc "Finds the angle in degrees between the hour hand and minute hand."} +; [hr min]) (defn word-frequencies - ^{:doc "My implementation of the Clojure 'frequency' function. Takes in a sentence (string) and outputs a map with words as keys and their frequency as values."} + ^{:doc "Create a function to accept a string and return a map of word (case insensitive) and its frequency"} [sentence] (reduce - (fn [res wrd] (assoc res wrd - (inc (get res wrd 0)))) - {} - (re-seq #"\w+" sentence))) + (fn [res w] + (let [wrd (clojure.string/lower-case w)] + (assoc res (clojure.string/lower-case wrd) + (inc (get res wrd 0))))) + {} + (re-seq #"\w+" sentence))) +; (defn word-frequencies; +; ^{:doc "Create a fun;ction to accept a string and return a map of word and its frequency"} +; [sentence]) (defn power [base exp] (nth - (iterate #(* %1 base) 1) exp)) + (iterate #(* %1 base) 1) exp)) + +;(defn power +; "Write a function to calculate the power of a number using iterate" +; [base exp]) + +(defn fizz-buzz + [number] + (let [multiple? (fn [n number] + (zero? (mod n number))) + res (str + (if (multiple? number 3) "Fizz") + (if (multiple? number 5) + "Buzz"))] + (if (clojure.string/blank? res) + (str number) + res))) + +;(defn fizz-buzz [number] +; "Write a program that prints the numbers from 1 to 100. But for +; multiples of three print \"Fizz\" instead of the number and for the +; multiples of five print \"Buzz\". For numbers which are multiples of +; both three and five print \"FizzBuzz\"" +; ()) + +(defn insert-letter + ^{:doc "Returns a collection of each new word produced by inserting the letter in all possible spots in the word."} + [word letter] + (let [length (count word)] + (for [num (range 0 (inc length))] + (str (subs word 0 num) + letter + (subs word num length))))) + +(defn word-permutations + ^{:doc "Returns a collection of all permutations of the given word."} + [s] + (if (= (count s) 1) + (list s) + (let [subword (subs s 0 (dec (count s))) ;;ex: 'bar', subword = 'ba' + letter (str (last (seq s)))] ;;the last letter, ie 'r' in 'bar' + (apply concat + (for [word (word-permutations subword)] + (insert-letter word letter)))))) diff --git a/src/coding_exercises/string-permutations.clj b/src/coding_exercises/string-permutations.clj deleted file mode 100644 index 8b38fdf..0000000 --- a/src/coding_exercises/string-permutations.clj +++ /dev/null @@ -1,45 +0,0 @@ -(ns string-permutations - (:require [clojure.string :as str])) - -(def problem-statement - "Example: Design an algorithm to print all permutations of a string.") - -(comment - "The algorithm checks to see if the word is of length one, if so, - then just return a list with that word. If the word is longer, chop - off the last letter, and insert it in every spot in all permutations - of the word minus the last letter. It is a recursive solution.") - -(defn insert-letter - ^{:doc "Returns a collection of each new word produced by inserting the letter in all possible spots in the word."} - [word letter] - (let [length (count word)] - (for [num (range 0 (inc length))] - (str (subs word 0 num) - letter - (subs word num length))))) - -(defn word-permutations - ^{:doc "Returns a collection of all permutations of the given word."} - [s] - (if (= (count s) 1) - (list s) - (let [subword (subs s 0 (dec (count s))) ;;ex: 'bar', subword = 'ba' - letter (str (last (seq s)))] ;;the last letter, ie 'r' in 'bar' - (apply concat - (for [word (word-permutations subword)] - (insert-letter word letter)))))) - -(defn factorial - ^{:doc "Returns the factorial of a number."} - [num] - {:pre [(>= num 1)]} - (if (= num 1) - 1 - (* num (factorial (dec num))))) - -(defn num-permutations - ^{:doc "Displays the number of permutations for a given word"} - [word] - (factorial - (count word))) diff --git a/test/coding_exercises/test/core.clj b/test/coding_exercises/test/core.clj index b01eb02..89f3064 100644 --- a/test/coding_exercises/test/core.clj +++ b/test/coding_exercises/test/core.clj @@ -1,6 +1,86 @@ (ns coding-exercises.test.core - (:use [coding-exercises.core]) - (:use [clojure.test])) + (:require [clojure.test :refer [is testing] :as t]) + (:use [coding-exercises.core])) -(deftest replace-me ;; FIXME: write - (is false "No tests have been written.")) +(defn test-func + "Creates a function that validate result against the result of applying func with input" + ([func pass?] + (fn [input result] + (let [actual (if (vector? input) (apply func input) (func input))] + (is (pass? actual result) + (format "(func %s) expected to result %s but found %s" input result actual))))) + ([func] + (test-func func =))) + +(defn run-tests! + "Funs all the test test cases by applying each element in tests" + ([tests test-func] + (run! #(apply test-func %1) tests)) + ([tests test-func desc] + (testing desc + (run-tests! tests test-func)))) + +(comment defmacro deftest + [desc tests func pass?] + (list macroexpand + '(t/deftest testing-something + (testing 'desc (run-tests! 'tests (test-func 'func 'pass?)))))) + +(t/deftest test-finonacii + ; nth fibonacii + (let [tests [[1 1] [2 1] [3 2] [4 3] [5 5] [14 377]] + tests-base-case [[0 0] [1 1] [-1 0] [-19900090090 0]] + func (test-func basic-fib)] + (run-tests! tests func "if the correct nth fibonacii is returned") + (run-tests! tests-base-case func "Test if the base case is returned for invalid input"))) + +(t/deftest test-user-followers + (let [tests [ + [(list '("user" "follower1") '("user" "follower2") '(:user-2 "follower1")) + {"user" #{"follower1", "follower2"} :user-2 #{"follower1"}}] + [(list [:user :follower] [:user :follower-2]) + {:user #{:follower :follower-2}}]] + func (test-func user-followers)] + (run-tests! tests func "Test tupples are mapped correctly"))) + +(t/deftest test-lazy-finonacii + (let [tests [[1 1] [2 1] [3 2] [4 3] [5 5] [14 377]] + lazy-fib-list (lazy-fib) + func (test-func #(nth lazy-fib-list %1))] + (run-tests! tests func "if the correct nth fibonacii is returned"))) + +(t/deftest test-test-word-frequencies + (let [tests [["this is a hello world example that is new." + {"this" 1 "that" 1 "a" 1 "hello" 1 "world" 1 "is" 2 "example" 1 "new" 1}] + ["This Is a Hello world example that is new." + {"this" 1 "that" 1 "a" 1 "hello" 1 "world" 1 "is" 2 "example" 1 "new" 1}]] + func (test-func word-frequencies)] + (run-tests! tests func "Testing if word frequencies"))) + +(t/deftest test-power + (let [tests [[[2 2] 4] [[3 2] 9] [[4 4] 256] [[100 3] 1000000] [[7 3] 343]] + func (test-func power)] + (run-tests! tests func "Testing if power is calculated correctly"))) + +(t/deftest test-clock-angle + (let [tests [[[0 0] 0.0] [[3 0] 90.0] [[6 0] 180.0]] + func (test-func clock-angle)] + (run-tests! tests func "Testing if clock angle is calculted correctly"))) + +(t/deftest test-fizz-buzz + (let [tests [[2 "2"] [3 "Fizz"] [4 "4"] [5 "Buzz"] [6 "Fizz"] [7 "7"] + [8 "8"] [9 "Fizz"] [10 "Buzz"] [11 "11"] [12 "Fizz"] [13 "13"] + [14 "14"] [15 "FizzBuzz"] [16 "16"] [17 "17"] [18 "Fizz"] [19 "19"] + [20 "Buzz"] [21 "Fizz"] [22 "22"] [23 "23"]] + func (test-func fizz-buzz)] + (run-tests! tests func "Fizz Buzz is returned correctly"))) + +(t/deftest test-insert-letter + (let [tests [[["hello" "i"] "hieililioi"] [["you" "j"] "yjojuj"]] + f (test-func insert-letter)] + (run-tests! tests f "testing letter insrtion"))) + +(t/deftest test-word-permutations + (let [tests [["hel" (list "hel" "hle" "elh" "ehl" "lhe" "leh" )]] + f (test-func word-permutations)] + (run-tests! tests f "testing letter insrtion")))