diff --git a/otus-01/src/otus/homework.clj b/otus-01/src/otus/homework.clj index 82c8e9d..f507fc4 100644 --- a/otus-01/src/otus/homework.clj +++ b/otus-01/src/otus/homework.clj @@ -1,5 +1,5 @@ (ns otus.homework) -(defn solution "Add your solution as fuction body here" []) +(defn solution [] (double (/ (+ 5 4 (- 2 (- 3 (+ 6 (/ 4 5))))) (* 3 (- 6 2) (- 2 7))))) diff --git a/otus-02/src/otus_02/homework/palindrome.clj b/otus-02/src/otus_02/homework/palindrome.clj index 591f98e..08e0a8f 100644 --- a/otus-02/src/otus_02/homework/palindrome.clj +++ b/otus-02/src/otus_02/homework/palindrome.clj @@ -1,6 +1,8 @@ (ns otus-02.homework.palindrome (:require [clojure.string :as string])) - -(defn is-palindrome [test-string]) +(defn is-palindrome [test-string] + (->> test-string string/lower-case + (filter #(Character/isLetter %)) + (#(= % (reverse %))))) diff --git a/otus-02/src/otus_02/homework/pangram.clj b/otus-02/src/otus_02/homework/pangram.clj index dc5d34c..e599697 100644 --- a/otus-02/src/otus_02/homework/pangram.clj +++ b/otus-02/src/otus_02/homework/pangram.clj @@ -1,6 +1,10 @@ (ns otus-02.homework.pangram (:require [clojure.string :as string])) - -(defn is-pangram [test-string]) - +(defn is-pangram [test-string] + (->> test-string string/lower-case + (filter #(Character/isLetter %)) + (map int) + (into #{}) + count + (= 26))) diff --git a/otus-02/src/otus_02/homework/square_code.clj b/otus-02/src/otus_02/homework/square_code.clj index 823a185..22f7555 100644 --- a/otus-02/src/otus_02/homework/square_code.clj +++ b/otus-02/src/otus_02/homework/square_code.clj @@ -1,4 +1,5 @@ -(ns otus-02.homework.square-code) +(ns otus-02.otus-02.homework.square-code +(:require [clojure.math :as math] [clojure.string :as string])) ;; Реализовать классический метод составления секретных сообщений, называемый `square code`. ;; Выведите закодированную версию полученного текста. @@ -48,9 +49,31 @@ "aohghn " "sseoau " +(defn encode-string [input] + (let [st (->> input string/lower-case + (filter #(Character/isLetter %))) + sqr (math/sqrt (count st)) + rnd (math/round sqr) + [x y] (if (= (int (math/ceil sqr)) rnd) [(inc rnd) rnd] [rnd rnd])] + (->> (repeat (inc x) (range (inc y))) + flatten + (map vector (concat st (repeat \space))) + (sort-by second) + (map first) + drop-last + (apply str)))) - -(defn encode-string [input]) - - -(defn decode-string [input]) +(defn decode-string [input] + (let [[x y] (->> (range) + (map #(vector % %)) + flatten + (#(map vector % (drop 1 %))) + (drop-while #(< (reduce * %) (count input))) + first)] + (->> (repeat x (range y)) + flatten + (map vector (concat input (repeat \space))) + (sort-by second) + (map first) + (apply str) + (string/trim)))) diff --git a/otus-04/src/otus_04/homework/magic_square.clj b/otus-04/src/otus_04/homework/magic_square.clj index c90f3bf..7b4ec13 100644 --- a/otus-04/src/otus_04/homework/magic_square.clj +++ b/otus-04/src/otus_04/homework/magic_square.clj @@ -6,12 +6,18 @@ ;; Подсказка: используйте "Siamese method" ;; https://en.wikipedia.org/wiki/Siamese_method -(defn magic-square - "Функция возвращает вектор векторов целых чисел, - описывающий магический квадрат размера n*n, - где n - нечётное натуральное число. - Магический квадрат должен быть заполнен так, что суммы всех вертикалей, - горизонталей и диагоналей длиной в n должны быть одинаковы." - [n] - [[0]]) +;; r = 1..n MagSq = n * mod(r' + (r - div(n + 3, 2)), n) + mod(r' + r * 2 - 2, n) + 1 +(defn magic-square [n] + {:pre [(odd? n)]} + (let [rng (map inc (range n)) + repeat-rng (fn [x] (flatten (repeat n x))) + sum-row-col (fn [x y] (map + (sort (repeat-rng x)) (repeat-rng y))) + f-main (fn [x] (map #(mod % n) (sum-row-col rng x))) + b1 (map #(- % (quot (+ n 3), 2)) rng) + b2 (map #(- (* % 2) 2) rng)] + (->> (map + (map #(* % n) (f-main b1)) (f-main b2)) + (map inc) + (partition n) + (map vec) + vec))) diff --git a/otus-04/src/otus_04/homework/scramblies.clj b/otus-04/src/otus_04/homework/scramblies.clj index f76d335..6481c8b 100644 --- a/otus-04/src/otus_04/homework/scramblies.clj +++ b/otus-04/src/otus_04/homework/scramblies.clj @@ -3,8 +3,9 @@ ;; Оригинальная задача: ;; https://www.codewars.com/kata/55c04b4cc56a697bb0000048 -(defn scramble? +(defn scramble? [letters word] "Функция возвращает true, если из букв в строке letters можно составить слово word." - [letters word] - nil) + (let [f-freq (fn [x] (map #(get (frequencies x) % 0) (set word))) + [x y] (map f-freq [letters word])] + (every? true? (map >= x y)))) diff --git a/otus-06/src/otus_06/homework.clj b/otus-06/src/otus_06/homework.clj index d5d5228..a98992b 100644 --- a/otus-06/src/otus_06/homework.clj +++ b/otus-06/src/otus_06/homework.clj @@ -1,4 +1,7 @@ -(ns otus-06.homework) +(ns otus-06.homework + (:require [clojure.string :as string]) + (:require [clojure.java.io :as io]) + (:require [clojure.set :as set])) ;; Загрузить данные из трех файлов на диске. ;; Эти данные сформируют вашу базу данных о продажах. @@ -94,3 +97,124 @@ ;; Файлы находятся в папке otus-06/resources/homework + +;; VARS +;;************************************************** + +(def files (map #(str "homework/" %) ["cust.txt" "prod.txt" "sales.txt"])) + +;; запросы +(def menu "\n*** Sales Menu *** +------------------ +1. Display Customer Table +2. Display Product Table +3. Display Sales Table +4. Total Sales for Customer +5. Total Count for Product +6. Exit + +Enter an option\n") + +(def cust-req "Enter a customer name\n") +(def item-req "Enter an item\n") + +;; ключи для чтения и отображения +(def cust-keys [:custID :name :address :phoneNumber]) +(def prod-keys [:prodID :itemDescription :unitCost]) +(def sales-keys [:salesID :custID :prodID :itemCount]) +(def sales-keys-to-show [:salesID :name :itemDescription :itemCount]) + +(def key-names [cust-keys prod-keys sales-keys]) + +;; ключи для конвертации и расчета total-sum +(def keys-to-int [:itemCount :prodID :custID :salesID]) +(def keys-to-double [:unitCost]) +(def keys-to-count-total-sum [:unitCost :itemCount]) + +;; ключи для агрегации +(def keys-to-cust-aggr [:name :totalCost]) +(def keys-to-item-aggr [:itemDescription :itemCount]) + +;; FUNCTIONS +;;********************************************************************** + +(defn read-to-maps + "читаем файл с соответствующими keys в список мап" + [f k] + (let [r (io/reader (io/resource f))] + (->> (line-seq r) + (map #(string/split % #"\|")) + (map #(zipmap k %))))) + +(defn update-mult-vals + "преобразуем несколько столбов (keys), например, конвертируем" + [map vals fun] + (reduce #(update-in % [%2] fun) map vals)) + +(defn add-new-key + "добавляем новый столбец (key), рассчитанный из нескольких существующих" + [map keys name fun] + (assoc map name (reduce fun (vals (select-keys map keys))))) + +(defn select-data + "выделяем несколько столбцов и преобразуем каждую строку к виду мапа (ID - данные)" + [keys-to-select key-to-first coll] + (->> coll + (map #(select-keys % keys-to-select)) + (map #((fn [x key] (merge {(key x) (dissoc x key)})) % key-to-first)))) + +(defn aggregate + "агрегируем данные aggr-key, группируя по name-key" + [[name-key aggr-key] coll] + (->> coll + (map #(select-keys % [name-key aggr-key])) + (group-by name-key) + (map (fn [[name vals]] + {name-key name + aggr-key (reduce + (map aggr-key vals))})) + (map #((fn [x key] {(key x) (dissoc x key)}) % name-key)) + (reduce merge))) + +(defn aggregate-and-filter + "агрегируем и фильтруем результат по запрошенному значению name-key" + [[name-key aggr-key] message coll] + (println message) + (flush) + (let [filter-input (read-line)] + (->> coll + (aggregate [name-key aggr-key]) + (#(find % filter-input))))) + +(defn print-result + "печатаем либо результат, либо уточняющий запрос" + [x] + (if-not (nil? x) + (run! println x) + (println "Precise your input\n"))) + + +;; собираем все данные в одну таблицу (список мап) +(def full-data (->> (map #(read-to-maps %1 %2) files key-names) + (reduce set/join) + (map (fn [x] (update-mult-vals x keys-to-int #(Integer/parseInt %)))) + (map (fn [x] (update-mult-vals x keys-to-double parse-double))) + (map (fn [x] (add-new-key x keys-to-count-total-sum :totalCost *))))) + +(defn main + "результирующая функция" + [coll] + (println menu) + (flush) + (let [x (read-line)] + (println (str x "\n")) + (->> (cond + (= x "1") (select-data cust-keys :custID coll) + (= x "2") (select-data prod-keys :prodID coll) + (= x "3") (select-data sales-keys-to-show :salesID coll) + (= x "4") (aggregate-and-filter keys-to-cust-aggr cust-req coll) + (= x "5") (aggregate-and-filter keys-to-item-aggr item-req coll) + (= x "6") ["Good Bye\n"]) + print-result) + (if (not= x "6") (main coll) nil))) + +(main full-data) diff --git a/otus-10/src/otus_10/homework.clj b/otus-10/src/otus_10/homework.clj index 3d3169d..c6f5450 100644 --- a/otus-10/src/otus_10/homework.clj +++ b/otus-10/src/otus_10/homework.clj @@ -1,6 +1,59 @@ -(ns otus-10.homework) +(ns otus-10-homework + (:require [clojure.string :as string]) + (:require [clojure.java.io :as io])) +(def n1 "file-12926-ed090b.mp3") -(defn -main - "I don't do a whole lot ... yet." - [& args]) +(defn check-flag + "Проверяем n-ый бит байта в виде integer" + [num-int ind] + (->> num-int + Integer/toBinaryString + reverse + (#(nth % ind \0)) + (= \1))) + +(defn get-size + "Получаем размер (header, subheader or frame)" + [arr] + (->> (range (count arr)) + (map #(Math/pow 128 %)) + reverse + (map * arr) + (apply +))) + + +(defn parse-frame-text [coll] + (let [[encoding-byte & rest] coll + encoding-types ["Windows-1252" "UTF-16" "UTF16BE" "UTF-8"]] + (String. (byte-array rest) (nth encoding-types encoding-byte)))) + +(defn get-frame-inside + "парсим один фрейм" + [stream] + (let [[_ _ _ _ sz1 sz2 sz3 sz4 _ _ & rest] stream + size (get-size [sz1 sz2 sz3 sz4]) + text (parse-frame-text (take size rest))] + {:size size :text text})) + +(defmulti get-frame (fn [coll] (apply str (map char (take 4 coll))))) + +(defmethod get-frame "TALB" [coll] (assoc (get-frame-inside coll) :name "TALB" :description "Album/Movie/Show title")) +(defmethod get-frame "TYER" [coll] (assoc (get-frame-inside coll) :name "TYER" :description "Year")) +(defmethod get-frame "TCON" [coll] (assoc (get-frame-inside coll) :name "TCON" :description "Content type")) +(defmethod get-frame "TDRC" [coll] (assoc (get-frame-inside coll) :name "TDRC" :description "Recording time")) +(defmethod get-frame "TIT2" [coll] (assoc (get-frame-inside coll) :name "TIT2" :description "Title/songname/content description")) +(defmethod get-frame "TPE1" [coll] (assoc (get-frame-inside coll) :name "TPE1" :description "Lead performer(s)/Soloist(s)")) + +(defn parse-mp3-tags [file] + (with-open [in (io/input-stream (io/file (io/resource file)))] + (let [[_ _ _ _ _ flag-byte sz1 sz2 sz3 sz4 & rest] (.readAllBytes in) + tags-size (get-size [sz1 sz2 sz3 sz4]) + [x1 x2 x3 x4] rest + sub-header-size (if (check-flag flag-byte 6) (get-size [x1 x2 x3 x4]) 0) + frames (drop sub-header-size (take tags-size rest))] + (loop [rem-frames frames + acc []] + (if (or (empty? rem-frames) (zero? (first rem-frames))) acc + (recur (drop (+ 10 (:size (get-frame rem-frames))) rem-frames) + (conj acc (get-frame rem-frames)))))))) diff --git a/otus-10/test/otus_10/homework_test.clj b/otus-10/test/otus_10/homework_test.clj new file mode 100644 index 0000000..d634e3c --- /dev/null +++ b/otus-10/test/otus_10/homework_test.clj @@ -0,0 +1,11 @@ +(ns otus-10-homework-test + (:require [clojure.test :refer :all] + [otus-10-homework :as sut])) + +(deftest test-parse-mp3-tags + (testing "проверка бита в байте, представленным числом" + (is (false? (sut/check-flag 1 5))) + (testing "проверка размера фрейма" + (is (= 129.0 (sut/get-size [0 0 1 1])))) + (testing "проверка парсинга массива байтов с выбранной кодировкой" + (is (= "Clojure" (sut/parse-frame-text [0 67 108 111 106 117 114 101]))))))