Open40
trying 4ever-clojure
Problem 29, Get the Caps
((fn [word] (apply str (filter #(Character/isUpperCase %) word))) "HeLlO, WoRlD!")
これだと、Could not resolve symbol: Character/isUpperCase
となってしまう
なので愚直な感じにした
(fn [word] (apply str (filter #(contains? (set "ABCDEFGHIJKLMNOPQRSTUVWXYZ") %) word)))
containsはsetに変換してから使う必要がある
Problem 30, Compress a Sequence
reduce #(if (= (last %1) %2) %1 (conj %1 %2)) []
他の解答例も見てみたけど、reduce使うのが一番シンプルっぽい
Problem 31, Pack a Sequence
partition-by identity
全然検討つかなくてChatGPTに聞いた。
そしたら関数をpartition-by
の関数を教えてくれた。
こんな関数あるんだなあ。
Problem 32, Duplicate a Sequence
reduce #(concat %1 (take 2 (repeat %2))) []
Problem 33, Replicate a Sequence
(fn [list n]
(reduce #(concat %1 (take n (repeat %2))) [] list))
Problem 39, Interleave Two Seqs
(fn [a-list b-list]
(flatten (map #(list %1 %2) a-list b-list)))
- mapに渡せるシーケンスは2つ以上も可能
- これを直接やってくれる、interleaveという関数もある
Problem 40, Interpose a Seq
(fn [divider a-seq]
(reduce
#(conj %1 divider %2)
[(first a-seq)]
(rest a-seq)))
- interposeはこれをやってくる関数
Problem 41, Drop Every Nth Item
(fn [dropped-seq target]
(keep-indexed #(when (not= (mod (inc %1) target) 0) %2) dropped-seq))
filter-indexed的なメソッドを探したら代わりに見つかったのがkee-indexed
elseでnullを返すケースは、ifよりもwhenを使った方が良さそう
Problem 42, Factorial Fun
(fn [n] (reduce * 1 (range 1 (inc n))))
Problem 43, Reverse Interleave
(fn [a-seq size]
(map
(fn [x] (filter #(= (mod % size) x) a-seq))
(range size)))
これだと、最初の例の出力が((2 4 6) (1 3 5))
と逆になってしまう
なので、Problem 42 を参考に、keep-indexed
を使う形に
(fn [a-seq size]
(map
(fn [x] (keep-indexed #(when (= (mod %1 size) x) %2) a-seq))
(range size)))
Problem 44, Rotate Sequence
(fn [n a-seq]
(as-> (mod n (count a-seq)) index
(concat
(subvec (vec a-seq) index)
(subvec (vec a-seq) 0 index))))
Problem 46, Flipping out
(fn [f] (fn [& args] (apply f (reverse args))))
applyする必要がある
Problem 49, Split a sequence
(fn [pos a-seq]
(list (subvec a-seq 0 pos) (subvec a-seq pos)))
Problem 50, Split by Type
単純にtypeでgroup-byすると、マップになる
(group-by type [1 :a 2 :b 3 :c])
> {java.lang.Long [1 2 3], clojure.lang.Keyword [:a :b :c]}
これをvaluesだけのシーケンスにすればいいから
(fn [a-seq] (vals (group-by type a-seq)))
Problem 53, Longest Increasing Sub-Seq
(fn [a-seq]
(as-> a-seq result
(reduce
#(let [last-sub-seq (last %1)]
(if (= (last last-sub-seq) (dec %2))
(assoc %1 (dec (count %1)) (conj last-sub-seq %2))
(conj %1 [%2])))
[[(first result)]]
(rest result))
(reduce #(if (< (count %1) (count %2)) %2 %1) [] result)
(if (< (count result) 2) [] result)))
Problem 54, Partition a Sequence
(fn [size a-seq]
(filter
#(>= (count %) size)
(vals (group-by #(quot % size) a-seq))))
Problem 55, Count Occurences
(fn [a-seq]
(as-> a-seq result
(group-by identity result)
(into {} (map #(hash-map (first %) (count (second %))) result))))
Problem 56, Find Distinct Items
reduce
(fn [acc current]
(if (some #(= % current) acc) acc (conj acc current)))
[]
Problem 58, Function Composition
(fn [& functions]
(fn [& args]
(reduce
(fn [result f] (f result))
(apply (last functions) args)
(rest (reverse functions)))))
Problem 59, Juxtaposition
(fn [& functions]
(fn [& args]
(map #(apply % args) functions)))
Problem 60, Sequence Reductions
(fn my-reductions
([a-fn a-seq] (my-reductions a-fn (first a-seq) (rest a-seq)))
([a-fn acc a-seq]
(lazy-seq
(if (empty? a-seq)
[acc]
(let [result (a-fn acc (first a-seq))]
(cons acc (my-reductions a-fn result (rest a-seq))))))))
最初はreduce使おうとしていたけど、reduceはlazyなseqを返さないからうまく行かず。。
試行錯誤してもうまくいかず、最後は解答例を参考に。
Problem 61, Map Construction
(fn [keys values]
(into {} (map #(hash-map %1 %2) keys values)))
Problem 62, Re-implement Iteration
(fn my-iterate [f x]
(lazy-seq
(cons x (my-iterate f (f x)))))
Problem 63, Group a Sequence
(fn [grouping-fn a-seq]
(reduce
(fn [acc current]
(let [group (grouping-fn current)]
(assoc acc group (vec (conj (acc group) current)))))
{}
a-seq))
Problem 65, Black Box Testing
(fn [a-seq]
(let [empty-seq (empty a-seq)
foo-bar-seq (conj empty-seq [:foo :bar])]
(cond
(= :bar (:foo foo-bar-seq)) :map
(= (count foo-bar-seq) (count (conj foo-bar-seq [:foo :bar]))) :set
(= :bar (first (conj foo-bar-seq :bar))) :list
(= :bar (last (conj foo-bar-seq :bar))) :vector
:else :unknown)))
いい方法が思い浮かばなかったので、解答例を参考にした
Problem 66, Greatest Common Divisor
(fn [x y]
(apply max (filter
#(and (= 0 (mod x %)) (= 0 (mod y %)))
(range 1 (inc (min x y))))))
Prime Numbers
(fn [size]
(take size (filter
(fn [num]
(and
(< 1 num)
(empty? (filter
(fn [divisor] (= 0 (mod num divisor)))
(range 2 (inc (quot num 2)))))))
(range))))
Problem 69, Merge with a Function
clojure
(fn [f & dictionaries]
(let [inner-merge (fn [base key value]
(as-> value $
(if (contains? base key) (f (get base key) $) $)
(assoc base key $)))]
(loop [result (first dictionaries)
remains (rest dictionaries)]
(if (empty? remains)
result
(recur (reduce (fn [acc [k v]] (inner-merge acc k v)) result (first remains))
(rest remains))))))
Problem 70, Word Sorting
clojure
(fn [text]
(let [compare-word (fn [a b]
(loop [rest-a a
rest-b b]
(let [result (compare (clojure.string/lower-case (first rest-a))
(clojure.string/lower-case (first rest-b)))]
(if (= result 0)
(recur (rest rest-a) (rest rest-b))
result))))]
(as-> (subs text 0 (dec (count text))) $
(clojure.string/split $ #" ")
(sort compare-word $))))
- 文字列の部分文字列を取得するには、
subs
-
(first "")
; nil -
(compare "a" nil)
; 1
Problem 73, Analyze a Tic-Tac-Toe Board
clojure
(fn [board]
(let [get-board-value (fn [{:keys [y x]}] (nth (nth board y) x))
all-check? (fn [next-point-fn first-point value]
(loop [point first-point]
(if (< (apply max (vals point)) (count board))
(if (= value (get-board-value point))
(recur (next-point-fn point))
false)
true)))
next-point-horizontal (fn [{:keys [y x]}] {:y y, :x (inc x)})
next-point-vertival (fn [{:keys [y x]}] {:y (inc y), :x x})
all-check-diagonal-left-top? (partial all-check?
(fn [{:keys [y x]}] {:y (inc y), :x (inc x)})
{:y 0, :x 0})
all-check-diagonal-left-bottom? (partial all-check?
(fn [{:keys [y x]}] {:y (dec y), :x (inc x)})
{:y 2, :x 0})
analyze-vh (fn []
(loop [i 0]
(if (< i (count board))
(cond
(all-check? next-point-horizontal {:y i, :x 0} :o) :o
(all-check? next-point-horizontal {:y i, :x 0} :x) :x
(all-check? next-point-vertival {:y 0, :x i} :o) :o
(all-check? next-point-vertival {:y 0, :x i} :x) :x
:else (recur (inc i)))
nil)))
analyze-diagonal (fn []
(cond
(all-check-diagonal-left-top? :o) :o
(all-check-diagonal-left-top? :x) :x
(all-check-diagonal-left-bottom? :o) :o
(all-check-diagonal-left-bottom? :x) :x
:else nil))]
(or (analyze-vh) (analyze-diagonal))))
- 3マス以外の正方形でもいけるようにしている
- or は nil にも使える
Problem 74, Filter Perfect Squares
clojure
(fn [nums-str]
(let [integer-value? (fn [num] (== num (clojure.math/floor num)))
perfect-square? (fn [num-str]
(if-let [num (parse-double num-str)]
(integer-value? (clojure.math/sqrt num))
false))]
(as-> nums-str result
(clojure.string/split result #",")
(filter perfect-square? result)
(clojure.string/join "," result))))
- Webサイト上だと、
Could not resolve symbol: Math/floor
となってしまう。。 - if-let : 値が存在する場合にのみ処理を行うための関数
- Maybe的な関数を使うにはライブラリが別途必要
Problem 75, Euler's Totient Function
clojure
(fn [num]
(let [greatest-common-denominator (fn [base target]
(loop [a base
b target]
(let [rest (mod a b)]
(if (= 0 rest) b (recur b rest)))))
coprime? (fn [a b] (= 1 (greatest-common-denominator a b)))]
(loop [i 2
result 1]
(if (< i num)
(recur (inc i) (if (coprime? num i) (inc result) result))
result))))
- ユークリッドの互除法を使って互いに素かどうか判定
Problem 77, Anagram Finder
clojure
(fn [words]
(letfn [(anagram? [a b] (= (sort a) (sort b)))
(divide-anagram [word words]
(reduce
(fn [acc current]
(if (anagram? word current)
(assoc acc :y-list (conj (get acc :y-list) current))
(assoc acc :n-list (conj (get acc :n-list) current))))
{:y-list [] :n-list []}
words))
(find [result words]
(if (empty? words)
result
(let [word (first words)
{:keys [y-list n-list]} (divide-anagram word (rest words))
next-result (if (empty? y-list)
result
(conj result (set (conj y-list word))))]
#(find next-result n-list))))]
(trampoline find #{} words)))
Problem 78, Reimplement Trampoline
clojure
(fn [f & args]
(loop [result (apply f args)]
(if (fn? result)
(recur (result))
result)))
Problem 79, Triangle Minimal Path
(fn [triangle]
(letfn [(get-min [index value above]
(cond
(= 0 index) (+ value (first above))
(<= (count above) index) (+ value (last above))
:else (+ value (min (nth above (dec index)) (nth above index)))))
(calc-all-path []
(reduce
(fn [acc current] (map-indexed #(get-min %1 %2 acc) current))
[0]
triangle))]
(apply min (calc-all-path))))
Problem 80, Perfect Numbers
(fn [num]
(letfn [(sum-of-divisors-wihtout-itself []
(reduce
(fn [acc cur]
(if (= 0 (mod num cur)) (+ acc cur) acc))
0
(range 1 (inc (quot num 2)))))]
(= num (sum-of-divisors-wihtout-itself))))
Problem 81, Set Intersection
(fn [a-set b-set]
(set (filter #(contains? b-set %) a-set)))
Problem 83, A Half-Truth
(fn [& args]
(and (not-every? true? args) (not-every? false? args)))
Problem 85, Power Set
reduce
(fn [acc cur]
(set (concat acc (map #(conj % cur) acc))))
#{#{}}
Problem 86, Happy numbers
(fn [num]
(letfn [(calc-new-number [n]
(loop [sum 0
current n]
(let [last-digit (mod current 10)
next (quot current 10)
next-sum (+ sum (* last-digit last-digit))]
(if (zero? next) next-sum (recur next-sum next)))))]
(loop [current (calc-new-number num)
history #{num}]
(cond
(contains? history current) false ; 履歴にある数字になったら無限ループ
(= 1 current) true
:else (recur
(calc-new-number current)
(conj history current))))))