Open40

trying 4ever-clojure

むらむーむらむー

Problem 29, Get the Caps

https://4clojure.oxal.org/#/problem/29

((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 43, Reverse Interleave

https://4clojure.oxal.org/#/problem/43

(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 50, Split by Type

単純にtypeでgroup-byすると、マップになる

https://4clojure.oxal.org/#/problem/50

(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

https://4clojure.oxal.org/#/problem/53

(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 60, Sequence Reductions

https://4clojure.oxal.org/#/problem/60

(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 65, Black Box Testing

https://4clojure.oxal.org/#/problem/65

(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 69, Merge with a Function

https://4clojure.oxal.org/#/problem/69

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

https://4clojure.oxal.org/#/problem/70

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

https://4clojure.oxal.org/#/problem/73

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

https://4clojure.oxal.org/#/problem/74

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

https://4clojure.oxal.org/#/problem/75

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

https://4clojure.oxal.org/#/problem/77

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 79, Triangle Minimal Path

https://4clojure.oxal.org/#/problem/79

(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 86, Happy numbers

https://4clojure.oxal.org/#/problem/86

(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))))))