Last active
July 26, 2023 18:14
-
-
Save dane-johnson/cc89efe818db96cecbce43371c0655f8 to your computer and use it in GitHub Desktop.
My solutions to problems on https://www.4clojure.com
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
;; Get to any problem by navigating to 4clojure.com/problem/[problem number] | |
;; 1. Nothing but the Truth | |
true | |
;; 2. Simple Math | |
4 | |
;; 3. Intro to Strings | |
"HELLO WORLD" | |
;; 4. Intro to Lists | |
:a :b :c | |
;; 5. Lists: conj | |
'(1 2 3 4) | |
;; 6. Intro to Vectors | |
:a :b :c | |
;; 7. Vectors: conj | |
[1 2 3 4] | |
;; 8. Intro to Sets | |
#{:a :b :c :d} | |
;; 9. Sets: conj | |
2 | |
;; 10. Intro to Maps | |
20 | |
;; 11. Maps: conj | |
[:b 2] | |
;; 12. Intro to Sequences | |
3 | |
;; 13. Sequences: rest | |
[20 30 40] | |
;; 14. Intro to Functions | |
8 | |
;; 15. Double Down | |
(partial * 2) | |
;; 16. Hello World | |
#(str "Hello, " % "!") | |
;; 17. Sequences: map | |
'(6 7 8) | |
;; 18. Sequences: filter | |
'(6 7) | |
;; 19. Last Element | |
(comp first reverse) | |
;; 20. Penultimate Element | |
(comp second reverse) | |
;; 21. Nth Element | |
#(last (take (inc %2) %1)) | |
;; 22. Count a Sequence | |
(fn my-count | |
[s] | |
(reduce (fn [c _] (inc c)) 0 s)) | |
;; 23. Reverse a Sequence | |
#(reduce conj (list) %) | |
;; 24. Sum It All Up | |
(partial reduce +) | |
;; 25. Find the odd numbers | |
(partial filter odd?) | |
;; 26. Fibonacci Sequence | |
#(take % ((fn fib | |
([] (fib 0 1)) | |
([a b] | |
(lazy-seq (cons b (fib b (+ a b)))))))) | |
;; 27. Palindrome Detector | |
#(= (seq %) (reverse %)) | |
;; 28. Flatten a Sequence | |
(fn my-flatten | |
[s] | |
(mapcat #(if (coll? %) (my-flatten %) [%]) s)) | |
;; 29. Get the Caps | |
(defn get-caps | |
[s] | |
(clojure.string/join (filter #(Character/isUpperCase %) s))) | |
;; 30. Compress a Sequence | |
#(map first (partition-by identity %)) | |
;; 31. Pack a Sequence | |
#(partition-by identity %) | |
;; 32. Duplicate a Sequence | |
(fn [s] (reduce #(conj %1 %2 %2) [] s)) | |
;; 33. Replicate a Sequence | |
(fn [s n] (reduce #(apply conj %1 (repeat n %2)) [] s)) | |
;; 34. Implement range | |
#(take (- %2 %1) (iterate inc %1)) | |
;; 35. Local bindings | |
7 | |
;; 36. Let it Be | |
[z 1, y 3, x 7] | |
;; 37. Regular Expressions | |
"ABC" | |
;; 38. Maximum value | |
(fn[& s](reduce #(if (< %1 %2) %2 %1) s)) | |
;; 39. Interleave Two Seqs | |
;; #Note: strangely fails on the site, but will work in a more traditional Clojure environment. | |
(comp flatten seq zipmap) | |
;; 40. Interpose a Seq | |
(fn [v s](reduce #(conj %1 v %2) [(first s)] (rest s))) | |
;; 41. Drop Every Nth Item | |
#(apply concat (map butlast (partition %2 %2 [nil] %1))) | |
;; 42. Factorial Fun | |
#(apply * (range 1 (inc %))) | |
;; $43 | |
(fn rev-interleave | |
[s n] | |
(map #(map second %) (vals (group-by #(mod (key %) n) (zipmap (range) s))))) | |
;; 44. Rotate Sequence | |
#(let [r (mod %1 (count %2))] (concat (drop r %2) (take r %2))) | |
;; 45. Intro to Iterate | |
'(1 4 7 10 13) | |
;; 46. Flipping out | |
(fn [f] (fn [& args] (apply f (reverse args)))) | |
;; 47. Contain Yourself | |
4 | |
;; 48. Intro to some | |
6 | |
;; 49. Split a sequence | |
#(if (< (quot (count %2) 2) %1) | |
(partition-all %1 %2) | |
(reverse (map reverse (partition-all (- (count %2) %1) (reverse %2))))) | |
;; 50. Split by Type | |
#(vals (group-by class %)) | |
;; 51. Advanced Destructuring | |
[1 2 3 4 5] | |
;; 52. Intro to Destructuring | |
[c e] | |
;; 53. Longest Increasing Sub-Seq | |
;; Seems hard. I'll be back | |
;; 54. Partition a Sequence | |
(fn my-partition | |
([n s] (my-partition n s [])) | |
([n s g] | |
(if (< (count s) n) | |
g | |
(recur n (drop n s) (conj g (take n s)))))) | |
;; 55. Count Occurrences | |
(fn [s] (apply merge-with + (map #(hash-map % 1) s))) | |
;; 56. Find Distinct Items | |
#(reduce (fn [s n] | |
(if (>= (.indexOf s n) 0) | |
s | |
(conj s n))) [] %) | |
;; 57. Simple Recursion | |
'(5 4 3 2 1) | |
;; 58. Function Composition | |
(fn [& fs] (fn [& args] (reduce #(%2 %1) (apply (last fs) args) (reverse (butlast fs))))) | |
;; 59. Juxtaposition | |
(fn [& fs] (fn [& args] (reduce #(cons (apply %2 args) %1) [] (reverse fs)))) | |
;; 60. Sequence Reductions | |
(fn my-reductions | |
([f [first & rest]] (lazy-seq (my-reductions f first rest))) | |
([f red s] | |
(if (not (empty? s)) | |
(cons red (lazy-seq (my-reductions f (f red (first s)) (rest s)))) | |
(list red)))) | |
;; 61. Map Construction | |
(fn [ks vs] | |
(->> | |
(map vector ks vs) | |
(into {}))) | |
;; 62. Re-implement Iterate | |
(fn my-iterate | |
[f x] | |
(lazy-seq (cons x (my-iterate f (f x))))) | |
;; 63. Group a Sequence | |
(fn my-group-by | |
[f s] | |
(apply (partial merge-with (comp (partial into []) concat)) | |
(map #(sorted-map (f %) [%]) s))) | |
;; 64. Intro to Reduce | |
+ | |
;; 65. Black Box Testing | |
(fn get-type | |
[arg] | |
(cond | |
;; :map if you can add a kv pair and pull back out the val with the key | |
(= 1 (:a (conj arg [:a 1]))) :map | |
;; :set if adding 2 of the same arg has the same count as adding 1 | |
(= (count (conj arg :a)) (count (conj arg :a :a))) :set | |
;; :vector if conj adds to front in order | |
(= '(1 2) (take 2 (conj arg 1 2))) :vector | |
;; :list otherwise | |
:default :list)) | |
;; 66. Greatest Common Divisor | |
(fn gcd | |
[& vs] | |
(loop [div (apply min vs)] | |
(if (every? false? (map #(ratio? (/ % div)) vs)) | |
div | |
(recur (dec div))))) | |
;; 67. Prime Numbers | |
;; Sorry for ugliness, I combined 2 functions into 1 ¯\_(ツ)_/¯ | |
(defn get-primes | |
([n] (get-primes (sorted-set 2) (dec n))) | |
([primes n] | |
(if (= 0 n) | |
(reverse (into (list) primes)) | |
(recur (conj primes ((fn next-prime | |
[primes] | |
(loop [next (inc (last primes))] | |
(if (every? ratio? (map (partial / next) primes)) | |
next | |
(recur (inc next))))) primes)) (dec n))))) | |
;; 68. Recurring Theme | |
(list 7 6 5 4 3) | |
;; 69. Merge with a Function | |
(fn my-merge-with | |
[f & ms] | |
(->> ms | |
(map seq) | |
(reduce concat) | |
(group-by first) | |
(map (fn [[k [first & rest]]] (hash-map k (reduce #(f %1 (second %2)) (second first) rest)))) | |
(apply merge) | |
(into (sorted-map)))) | |
;; 70. Word Sorting | |
(fn sort-sentence | |
[sentence] | |
(sort-by clojure.string/lower-case (clojure.string/split sentence #"\W"))) | |
;; 71. Rearranging Code: -> | |
last | |
;; 72. Rearranging Code: ->> | |
reduce + | |
;; 73. Analyze a Tic-Tac-Toe Board | |
(fn tic-tac-toe | |
[board] | |
(letfn | |
[(three-in-a-row | |
[[first :as row]] | |
(if (and (not= :e first) (every? #{first} row)) first)) | |
(horizontal [n] (get board n)) | |
(horizontals [] (map horizontal (range 3))) | |
(vertical [n] (map #(get % n) board)) | |
(verticals [] (map vertical (range 3))) | |
(diagonals [] | |
[(map #(get-in %1 [%2 %2]) (repeat board) (range 3)) | |
(map #(get-in %1 [%2 %3]) (repeat board) (range 3) (range 2 -1 -1))])] | |
(some identity (map three-in-a-row (concat (horizontals) (verticals) (diagonals)))))) | |
;; 74. Filter Perfect Squares | |
(fn square-filter | |
[s] | |
(->> (clojure.string/split s #",") | |
(map #(Integer/parseInt %)) | |
(filter #(integer? (rationalize (Math/sqrt %)))) | |
(clojure.string/join ","))) | |
;; 75. Euler's Totient Function | |
(defn totient | |
[n] | |
(letfn [(gcd | |
[& vs] | |
(loop [div (apply min vs)] | |
(if (every? false? (map #(ratio? (/ % div)) vs)) | |
div | |
(recur (dec div))))) | |
(coprime? | |
[n1 n2] | |
(= (gcd n1 n2) 1))] | |
(if (not= 1 n) | |
(count (filter #(coprime? n %) (range 1 n))) | |
1))) | |
;; 76. Intro to Trampoline | |
[1 3 5 7 9 11] | |
;; 77. Anagram Finder | |
(fn [s] (into #{} (filter #(> (count %1) 1) (map (comp set second) (group-by frequencies s))))) | |
;; 78. Reimplement Trampoline | |
(fn my-trampoline | |
[f & args] | |
(loop [val (apply f args)] | |
(if (fn? val) (recur (val)) val))) | |
;; 79. Triangle Minimal Path | |
(fn triangle-path | |
[tri] | |
(let [tri (vec tri)] | |
(loop [score-tri [(first tri)] | |
depth 1] | |
(if (< depth (count tri)) | |
(recur | |
(conj | |
score-tri | |
(vec | |
(map-indexed | |
#(cond | |
(zero? %1) (+ %2 (first (get score-tri (dec depth)))) | |
(= %1 depth) (+ %2 (peek (get score-tri (dec depth)))) | |
:else (+ %2 (min (get-in score-tri [(dec depth) (dec %1)]) | |
(get-in score-tri [(dec depth) %1])))) | |
(get tri depth)))) | |
(inc depth)) | |
(apply min (peek score-tri)))))) | |
;; 80. Perfect Numbers | |
(fn perfect? | |
[n] | |
(letfn [(factor [x] (filter #(zero? (mod x %)) (range 1 (inc (/ x 2)))))] | |
(= (apply + (factor n)) n))) | |
;; 81. Set Intersection | |
#(clojure.set/difference %1 (clojure.set/difference %1 %2)) | |
;; 82. Word Chains | |
(fn word-chain? | |
([ws] | |
(true? (some true? (for [wd ws] (word-chain? (disj ws wd) wd))))) | |
([ws wd] | |
(if (empty? ws) | |
true | |
(letfn [(insertion? [w1 w2] | |
(loop [s1 (vec w1) | |
s2 (vec w2) | |
insert false] | |
(if (empty? s1) | |
true | |
(if (= (peek s1) (peek s2)) | |
(recur (pop s1) (pop s2) insert) | |
(if-not insert | |
(recur s1 (pop s2) true) | |
false))))) | |
(substitution? [w1 w2] | |
(= 1 (count (filter #(apply not= %) (map vector w1 w2))))) | |
(valid-trans? [w1 w2] | |
(case (- (count w1) (count w2)) | |
1 ;; w1 is a character longer than w2, may be deletion | |
(insertion? w2 w1) | |
-1 ;; w2 is a character longer than w1, may be insertion | |
(insertion? w1 w2) | |
0 ;; w1 and w2 are the same, may be substitution | |
(substitution? w1 w2) | |
false ;; Not allowed | |
))] | |
(some true? (for [wd2 ws] (and (valid-trans? wd wd2) (word-chain? (disj ws wd2) wd2)))))))) | |
;; 83. A Half-Truth | |
(fn [& v] (not (or (every? true? v) (every? false? v)))) | |
;; 85. Power Set | |
(fn power-set | |
[s] | |
(reduce #(into %1 (for [ss %1] (conj ss %2))) #{#{}} s)) | |
;; 86. Happy Numbers | |
(fn happy? | |
([num] (happy? num #{})) | |
([num seen] | |
(if (get seen num) | |
false | |
(let [sum (apply + (map #(* (Integer/parseInt (str %1)) (Integer/parseInt (str %1))) (str num)))] | |
(if (= num sum) | |
true | |
(recur sum (conj seen num))))))) | |
;; 88. Symmetric Difference | |
#(clojure.set/union (clojure.set/difference %1 %2) (clojure.set/difference %2 %1)) | |
;; 90. Cartesian Product | |
#(set (for [e1 %1 e2 %2] [e1 e2])) | |
;; 92. Read Roman numerals | |
(fn parse-rn | |
[s] | |
(let [val-map {\I 1 | |
\V 5 | |
\X 10 | |
\L 50 | |
\C 100 | |
\D 500 | |
\M 1000}] | |
(loop [count 0 | |
string (reverse s) | |
largest \I] | |
(cond | |
(empty? string) count | |
:default (recur (if (> (get val-map largest) | |
(get val-map (peek string))) | |
(- count (get val-map (peek string))) | |
(+ count (get val-map (peek string)))) | |
(pop string) | |
(if (> (get val-map largest) | |
(get val-map (peek string))) | |
largest | |
(peek string))))))) | |
;; 95. To Tree, or not to Tree | |
(fn binary-tree? | |
[tree] | |
(cond | |
(nil? tree) true | |
(or (not (coll? tree)) (not= 3 (count tree))) false | |
(first tree) (every? true? (map binary-tree? (rest tree))) | |
:else false)) | |
;; 96. Beauty is Symmetry | |
(fn symmetric-binary-tree? | |
[tree] | |
(letfn | |
[(mirror [tree] | |
(if (coll? tree) | |
(concat (list (first tree)) (reverse (map mirror (rest tree)))) | |
tree)) | |
(binary-tree? | |
[tree] | |
(cond | |
(nil? tree) true | |
(or (not (coll? tree)) (not= 3 (count tree))) false | |
(first tree) (every? true? (map binary-tree? (rest tree))) | |
:else false))] | |
(and (binary-tree? tree) (= tree (mirror tree))))) | |
;; 97. Pascal's Triangle | |
(fn pascal | |
[n] | |
(letfn [(next-col [i prev] | |
(if (and (> i 0) (< i (count prev))) | |
(+ (prev (dec i)) (prev i)) | |
1)) | |
(next-row [prev] | |
(vec (map next-col (range (inc (count prev))) (repeat prev))))] | |
(loop [row [1] | |
n (dec n)] | |
(if (>= 0 n) | |
row | |
(recur (next-row row) (dec n)))))) | |
;; 98. Equivalence Classes | |
#(set (map set (vals (group-by %1 %2)))) | |
;; 99. Product Digits | |
(fn [a b] (map #(Integer/parseInt (str %)) (seq (str (* a b))))) | |
;; 100. Least Common Multiple | |
(defn lcm | |
[& ns] | |
(reduce (fn [n1 n2] | |
(loop [a n1 | |
b n2] | |
(cond | |
(= a b) a | |
(< a b) (recur (+ a n1) b) | |
(> a b) (recur a (+ b n2))))) | |
ns)) | |
;; 101. Levenshtein Distance | |
(fn levenshtein | |
[w1 w2] | |
(loop [i 1 | |
table [(vec (range (inc (count w1))))]] | |
(if (= i (inc (count w2))) | |
(last (last table)) | |
(recur (inc i) | |
(conj table (vec | |
(loop [j 1 | |
row [i]] | |
(let [t (conj table row)] | |
(if (= j (inc (count w1))) | |
row | |
(recur (inc j) | |
(conj row (min (inc (get-in t [(dec i) j])) | |
(inc (get-in t [i (dec j)])) | |
(+ (get-in t [(dec i) (dec j)]) | |
(if (= (get w1 (dec j)) | |
(get w2 (dec i))) | |
0 | |
1)))))))))))))) | |
;; 102. intoCamelCase | |
(fn into-camel-case | |
[w] | |
(reduce #(cond | |
(= (second %2) \-) %1 | |
(= (first %2) \-) (str %1 (clojure.string/upper-case (str (second %2)))) | |
:default (str %1 (second %2))) | |
(str (first w)) | |
(map vector w (rest w)))) | |
;; 103. Generating k-combinations | |
(fn k-combs | |
[k S] | |
(cond | |
(> k (count S)) #{} | |
(= k (count S)) #{S} | |
(= k 1) (set (map #(set [%1]) S)) | |
:default | |
(into #{} (for [e S | |
S-prime (k-combs (dec k) (clojure.set/difference S #{e}))] | |
(conj S-prime e))))) | |
;; 106. Number Maze | |
(fn number-maze | |
[start end] | |
(loop [Q (-> (clojure.lang.PersistentQueue/EMPTY) (conj [start 1]))] | |
(let [[curr-n steps] (peek Q)] | |
(if (= end curr-n) steps | |
(recur (-> (pop Q) | |
(conj [(* 2 curr-n) (inc steps)] | |
[(+ 2 curr-n) (inc steps)]) | |
(as-> $ (if (even? curr-n) | |
(conj $ [(/ curr-n 2) (inc steps)]) | |
$)))))))) | |
;; 108. Lazy Searching | |
(fn lazy-search | |
[& ss] | |
(loop [ss ss] | |
(letfn [(same-first? [s] (= (first s) (apply min (map first ss))))] | |
(if (every? same-first? ss) | |
(ffirst ss) | |
(recur (map #(if (same-first? %) (rest %) %) ss)))))) | |
;; 110. Sequence of pronounciations | |
(fn [s] | |
(letfn [(pronounce [s] | |
(flatten (map | |
#(list (count %) (first %)) | |
(partition-by identity s))))] | |
(rest (iterate pronounce s)))) | |
;; 111. Crossword puzzle | |
(fn crossword [word board] | |
(letfn [(fits? [word prompt] | |
(and | |
(= (count word) (count prompt)) | |
(every? true? (map #(or (= %1 %2) (= %2 \_)) word prompt)))) | |
(horizontal-prompts [board] | |
(flatten (map #(clojure.string/split (clojure.string/replace % #" " "") #"#") board))) | |
(rotate-board [board] | |
(take-nth 2 (apply map str board))) | |
(vertical-prompts [board] | |
(horizontal-prompts (rotate-board board)))] | |
(or (some #(fits? word %) (concat (horizontal-prompts board) (vertical-prompts board))) false))) | |
;; 113. Making Data Dance | |
(fn [& is] (reify | |
Object (toString [_] (clojure.string/join ", " (sort is))) | |
clojure.lang.Seqable (seq [_] (if (empty? is) nil (distinct is))))) ;; nil ~= '() in clojure ig | |
;; 114. Global take-while | |
(fn taken-while [n p s] | |
(cond | |
(empty? s) '() | |
((comp not p) (first s)) (seq (cons (first s) (taken-while n p (rest s)))) | |
((comp not zero?) (dec n)) (seq (cons (first s) (taken-while (dec n) p (rest s)))) | |
:else '())) | |
;; 115. The Balance of N | |
(fn balanced [n] | |
(let [s (seq (str n)) | |
[h1 h2] (split-at (/ (count s) 2) s)] | |
(zero? (reduce + (map #(- (Integer/parseInt (str %1)) (Integer/parseInt (str %2))) | |
h1 h2))))) | |
;; 158. Decurry | |
(fn decurry | |
[curried] | |
(letfn [(inner [f & args] | |
(if (empty? args) | |
f | |
(apply inner (f (first args)) (rest args))))] | |
(partial inner curried))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment