Skip to content

Instantly share code, notes, and snippets.

@viebel
Last active May 24, 2022 07:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save viebel/1662c34536a099e591380ba2e43172c4 to your computer and use it in GitHub Desktop.
Save viebel/1662c34536a099e591380ba2e43172c4 to your computer and use it in GitHub Desktop.
10 Clojure challenges to teach in a fun way how fun is Clojure http://app.klipse.tech/?eval_only=1&cljs_in.gist=viebel/1662c34536a099e591380ba2e43172c4
;; 1. hello worlds
;; 1.1. Write a function that receives a name and prints to the console Hello <name>!
;; 1.2. Write a function that receives several names and prints to the console Hello <name>! for each name
;; 1.3. Write of code that checks your functions work properly (layman unit tests)
(defn hello [name]
(str "Hello, " name "!"))
(defn multiple-hellos [names]
(let [greetings (map hello names)]
(clojure.string/join "\n" greetings)))
(println (hello "Yehonathan"))
(println (multiple-hellos ["Martin" "Bert"]))
(= (hello "Yehonathan") "Hello Yehonathan!")
;; 2. 99 bottles of beers
;; Write a function that prints the lyrics of 99 bottles of beers
;; See http://www.99-bottles-of-beer.net/lyrics.html
(defn paragraph [num]
(str num " bottles of beer on the wall\n"
num " bottles of beer\n"
"Take one down, pass it around\n"
(dec num) " bottles of beer on the wall.\n"))
(defn lyrics []
(let [numbers (range 99 0 -1)
paragraphs (map paragraph numbers)]
(clojure.string/join "\n" paragraphs)))
(print (lyrics))
;; pyramid of ages
;; input in a CSV file [gender, year of birth] e.g [m, 1978]
;; output {:m {"0-5" 100000
;; "5-10" 2000}}
(require '[clojure.tools.reader :refer [read-string]])
(defn birth-year->age [current-year birth-year]
(max 0 (int (- current-year birth-year))))
(defn round [bucket-size num]
(* bucket-size (int (/ num bucket-size))))
(defn age->bucket [age]
(let [low (round 5 age)
high (+ low 5)]
(str low "-" high)))
(defn persons->pyramid [year persons]
(reduce (fn [pyramid [gender birth-year]]
(let [age (birth-year->age year birth-year)
bucket (age->bucket age)]
(update-in pyramid [gender bucket] (fnil inc 1))))
{}
persons))
(defn bucket->low [a]
(read-string (first (clojure.string/split a #"\-"))))
(defn sort-age-map [age-map]
(sort-by (comp bucket->low first)
age-map))
(def persons (repeatedly 1000 #(vector (rand-nth ["men" "women"]) (rand-nth (range 1936 2019)))))
(defn sort-pyramid [pyramid]
(into {}
(for [[gender age-map] pyramid]
[gender (sort-age-map age-map)])))
(def pyramid (persons->pyramid 2019 persons))
(sort-pyramid pyramid)
;;; file system search
(->>
(tree-seq (constantly true)
#(if (coll? %) (seq %) [])
{:a {:b 1 :c {:d 2}} :e "lll"})
(filter map-entry?)
(remove (fn [[k v]] (coll? v))))
;; 3 dices: 4, 6, 8
;; what is the freq of each combination
(def combinations
(for [a (range 1 5)
b (range 1 7)
c (range 1 9)]
[a b c]))
(def num-of-comb (count combinations))
(defn pair? [c]
(= 2 (count (set c))))
(defn triple? [c]
(= 1 (count (set c))))
(defn straight? [c]
(let [[x y z] (sort c)]
(and (= 1 (- z y))
(= 1 (- y x)))))
(defn value [c]
(cond
(pair? c) :pair
(triple? c) :triple
(straight? c) :straight
:else :any))
(->> (group-by sort combinations)
(map (fn [[k v]] [k (* 100 (/ (count v) num-of-comb))]))
(sort-by second)
(group-by (comp value first)))
;; generate a poem sentence
(def nouns ["bird", "clock", "boy", "plastic", "duck", "teacher", "old lady", "professor", "hamster", "dog"])
(def verbs ["kicked", "ran", "flew", "dodged", "sliced", "rolled", "died", "breathed", "slept", "killed"])
(def adjectives ["beautiful", "lazy", "professional", "lovely", "dumb", "rough", "soft", "hot", "vibrating", "slimy"])
(def adverbs ["slowly", "elegantly", "precisely", "quickly", "sadly", "humbly", "proudly", "shockingly", "calmly", "passionately"])
(def prepositions ["down", "into", "up", "on", "upon", "below", "above", "through", "across", "towards"])
(defn random-sentence []
(str
"The " (rand-nth adjectives) " " (rand-nth nouns) " " (rand-nth adverbs) " " (rand-nth verbs)
" because some " (rand-nth nouns) " " (rand-nth adverbs) " "(rand-nth verbs) " " (rand-nth prepositions) " a " (rand-nth adjectives) " " (rand-nth nouns) ", which became a "
(rand-nth adjectives) " and " (rand-nth adjectives) " " (rand-nth nouns) "."))
(random-sentence)
;;; jumble the letters in a sentence
;;; https://www.mnn.com/lifestyle/arts-culture/stories/why-your-brain-can-read-jumbled-letters
(def sentence "Goofspiel, also known as The Game of Pure Strategy or GOPS, is a card game for two or more players. It was invented by Merrill Flood while at Princeton University in the 1930s, and Alex Randolph describes a similar game as having been popular with the 5th Indian Army during the Second World War.
The game is simple to learn and play, but has some degree of strategic depth. It is commonly used as an example of multi-stage simultaneous move game in game theory and artificial intelligence.")
(defn sentence->words [sentence]
(clojure.string/split sentence #"\b"))
(defn words->sentence [words]
(clojure.string/join "" words))
(defn seq->str [s]
(clojure.string/join "" s))
(defn shuffle-word [word]
(let [first-char (first word)
last-char (if (> (count word) 1)
(last word)
nil)
middle-word (seq->str (drop-last 1 (rest word)))]
(str first-char
(seq->str (shuffle middle-word))
last-char)))
(words->sentence (map shuffle-word (sentence->words sentence)))
(defn middle [low high]
(int (+ low (/ (- high low) 2))))
(defn guess [num low high max-guesses]
(loop [low low
high high
guesses []
ranges []]
(let [guess (middle low high)
guesses (conj guesses guess)
ranges (conj ranges [low high])]
(if (> (count guesses) max-guesses)
{:result :failed
:num num
:guesses guesses
:guesses-count (count guesses)}
(cond
(= guess num) {:result :succeed
:num num
:guesses guesses
:ranges ranges
:guesses-count (count guesses)}
(< guess num) (recur guess high guesses ranges)
:else (recur low guess guesses ranges))))))
(guess (rand-int 1000) 0 1000 8)
(guess 500 0 1000 20)
;; SET
(ns set-game.logic
(:require [clojure.math.combinatorics :as combo]))
(def shapes #{:oval :diamond :squiggle})
(def colors #{:red :purple :green})
(def numbers #{:1 :2 :3})
(def shadings #{:solid :stripped :outline})
(defn generate-cards []
(for [shape shapes
color colors
number numbers
shading shadings]
[shape color number shading]))
(def cards (generate-cards))
(defn deck []
(take 12 (shuffle cards)))
(defn identical-or-distinct? [p1 p2 p3]
(not= (count (set [p1 p2 p3])) 2))
(defn set-of-cards? [[[c1-shape c1-color c1-number c1-shading]
[c2-shape c2-color c2-number c2-shading]
[c3-shape c3-color c3-number c3-shading]]]
(and
(identical-or-distinct? c1-shape c2-shape c3-shape )
(identical-or-distinct? c1-color c2-color c3-color )
(identical-or-distinct? c1-number c2-number c3-number )
(identical-or-distinct? c1-shading c2-shading c3-shading)))
(defn SETs [deck]
(filter set-of-cards? (combo/combinations deck 3)))
(SETs (deck))
;; build a social graph
;; Given a csv file with a list of friends organized in two columns a,b
;; Stores the friend relationship (symmetric) and write functions:
;; 1. friends?
;; 2. average-num-of-friends
(ns social.graph
(:require [clojure.math.combinatorics :as combo]))
(def people #{"Waligore" "Walke" "Walker" "Walkling" "Wall" "Wallace" "Wallach" "Wallache" "Wallack" "Wallas" "Waller" "Walley" "Wallford" "Walli" "Wallie" "Walling" "Wallinga" "Wallis" "Walliw" "Wallraff" "Walls" "Wally" "Walrath" "Walsh" "Walston" "Walt" "Walter" "Walters" "Walther" "Waltner" "Walton" "Walworth" "Waly" "Wampler" "Wamsley" "Wan" "Wanda" "Wandie" "Wandis" "Wandy" "Wane" "Waneta" "Wanfried" "Wang" "Wanids" "Wanonah" "Wanyen" "Wappes" "Warchaw" "Ward" "Warde" "Warden" "Warder" "Wardieu" "Wardlaw" "Wardle" "Ware" "Wareing" "Warenne" "Warfeld" "Warfield" "Warfold" "Warford" "Warfore"})
(def friend-list (take 100 (shuffle (combo/combinations people 2))))
(defn add-friend [graph person-a person-b]
(update graph person-a (fnil #(conj % person-b) #{person-b})))
(reduce
(fn [graph [person-a person-b]]
(-> graph
(add-friend person-a person-b)
(add-friend person-b person-a)))
{}
friend-list)
(defn friends? [graph person-a person-b])
(defn average-number-of-friends [graph])
(defn connected? [graphperson-a person-b])
;; clojurescript ???
;;
@viebel
Copy link
Author

viebel commented Dec 3, 2020

Level 0: Hello world à la Clojure

Think in terms of data manipulation
In your code, separate side effects from the logic
Discover the usefulness of sequences

Level 1: Write a poem about 99 beers

Write small functions
Leverage anonymous functions

Level 2: Build a social graph

Instead of looping, be reducing
Store associative data in maps

Level 3: Build a mini database of the world population

Functions can return functions
Convert numbers to strings back and forth
Sort a map

Level 4: Jumble letters in words of sentences

Convert strings to sequences back and forth
Manipulate strings
Substrings and subsequences
Shuffle a sequence
Regular expressions

Level 5: Calculate probabilities of D&D dices combinations

List comprehension is sometimes easier than mapping
Use sets to avoid duplicates

Level 6: Teach the computer how to guess any number in less than 20 steps

Defer printing until it’s unavoidable
Iterate at a low level
Gather and store as much data as you can

Level 7: Find the shortest path between cities

https://gist.github.com/viebel/01e4cccbe8807d1577f74c6db5a528c4
Take part of a map
Update maps without mutating them
Convert maps to vectors back and forth
Create infinite sequences without fear

Level 8: Manage bank accounts

Manage state in an immutable world

Level 9: Implement a 2 players card game

Combine all what you have learnt into a full project

*Level 10: Find the solution of a mind challenging card game (SET)

Use combinatorics to do the hard job for you

*Level 11: Search files in a file system

Treat a tree data structure as a sequence

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment