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..4c7a258 100644 --- a/otus-10/src/otus_10/homework.clj +++ b/otus-10/src/otus_10/homework.clj @@ -1,6 +1,56 @@ -(ns otus-10.homework) +(ns otus-10-homework.core +(: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 +))) + +(defmulti parse-frame first) +(defmethod parse-frame 0 + [coll] (String. (byte-array (remove zero? (next coll))) "Windows-1252")) +(defmethod parse-frame 1 + [coll] (String. (byte-array (remove zero? (next coll))) "UTF-16")) +(defmethod parse-frame 2 + [coll] (String. (byte-array (remove zero? (next coll))) "UTF-16BE")) +(defmethod parse-frame 3 + [coll] (String. (byte-array (remove zero? (next coll))) "UTF-8")) + + +(defn get-frame + "парсим один фрейм" + [stream] + (let [[nm1 nm2 nm3 nm4 sz1 sz2 sz3 sz4 & rest] stream + name (apply str (map char [nm1 nm2 nm3 nm4])) + size (get-size [sz1 sz2 sz3 sz4]) + text (parse-frame (take (+ size 2) rest))] + {:name name :size size :text text})) + +(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-16/src/otus_16/homework.clj b/otus-16/src/otus_16/homework.clj index a54c2bf..81b1d55 100644 --- a/otus-16/src/otus_16/homework.clj +++ b/otus-16/src/otus_16/homework.clj @@ -1,21 +1,35 @@ -(ns otus-16.homework) +(ns otus-16.homework + (:require [clojure.string :as str] + [clojure.java.io :as io])) +(def reg #".+\] \".*?/(.*?) .*?\" \d+ (\d+) \"(.*?)\".+") +(defn parse-line [line] + (let [[_ url bytes referer] (re-matches reg line)] + {:url url + :bytes (if (str/blank? bytes) 0 (Integer/parseInt bytes)) + :referer referer})) -(defn solution [& {:keys [url referrer] - :or {url :all referrer :all}}] - (println "doing something") - {:total-bytes 12345 - ;; если указан параметр url, то в хэш-мапе будет только одно значение - :bytes-by-url {"some-url" 12345} - ;; если указан параметр referrer, то в хэш-мапе будет только одно значение - :urls-by-referrer {"some-referrer" 12345}}) - +; при вводе без ключей и с ключом url-requested читаем sum-bytes, c ключом referer-requested - count-url +(defn read-log [log-file & {:keys [url-requested referer-requested]}] + (with-open [rdr (io/reader log-file)] + (->> (take-nth 2 (line-seq rdr)) + (map parse-line) + (reduce (fn [acc {:keys [url bytes referer]}] + (cond-> acc + (or (= url url-requested) + (= referer referer-requested) + (every? nil? [url-requested referer-requested])) + ((fn [x] (-> x + (update :count-url inc) + (update :sum-bytes #(+ % bytes))))))) + {:count-url 0 :sum-bytes 0})))) +(defn read-logs [log-files & {:keys [url-requested referer-requested]}] + (->> (pmap #(read-log % :url-requested url-requested + :referer-requested referer-requested) log-files) + (apply merge-with +))) (comment - ;; возможные вызовы функции - (solution) - (solution :url "some-url") - (solution :referrer "some-referrer") - (solution :url "some-url" :referrer "some-referrer")) +(-> (map (partial str "resources/access.log.") (range 2 10)) + read-logs)) diff --git a/url-shortener/test/url-shortener/core_test.clj b/url-shortener/test/url-shortener/core_test.clj new file mode 100644 index 0000000..fe234e7 --- /dev/null +++ b/url-shortener/test/url-shortener/core_test.clj @@ -0,0 +1,45 @@ +(ns url-shortener.core-test + (:require [clojure.test :refer :all] + [url-shortener.core :as sut])) + +(deftest test-get-idx + (testing "legal args" + (are [a b] (= b (#'sut/get-idx a)) + 8448 136.0 4944 79.0 5310 85.0 9626 155.0 5217 84.0)) + (testing "illegal args" + (is (thrown? ClassCastException (#'sut/get-idx "8448"))) + (is (thrown? ClassCastException (#'sut/get-idx [8448]))) + (is (thrown? ClassCastException (#'sut/get-idx {:i 8448}))))) + +(deftest test-get-character-by-idx + (testing "legal args" + (are [a b] (= b (#'sut/get-character-by-idx a)) + 5047 \P 7828 \G 1313 \B 9172 \w 592 \Y)) + (testing "illegal args" + (is (thrown? ClassCastException (#'sut/get-character-by-idx "5047"))) + (is (thrown? ClassCastException (#'sut/get-character-by-idx [5047]))) + (is (thrown? ClassCastException (#'sut/get-character-by-idx {:i 5047}))) + (is (nil? (#'sut/get-character-by-idx -5047))))) + +(deftest test-int->id + (testing "legal args" + (are [a b] (= b (sut/int->id a)) + 5654444 "Niyi" 3233147 "DZ5X" 3592358 "F4XG" 2786217 "Bgoz" 9886206 "fTqw")) + (testing "illegal args" + (is (thrown? ClassCastException (sut/int->id "5654444"))) + (is (thrown? ClassCastException (sut/int->id [5654444]))) + (is (thrown? ClassCastException (sut/int->id {:int 56544447}))) + (is (empty? (sut/int->id -56544447))))) + +(deftest test-id->int + (testing "legal args" + (are [a b] (= b (sut/id->int a)) + "JyX" 76789 "5WQ7" 1316267 "0ztUE" 14751302 "nu95N1" 45720149183)) + (testing "illegal args" + (is (thrown? IllegalArgumentException (sut/id->int 76789))) + (is (thrown? NullPointerException (sut/id->int ["JyX"]))) + (is (thrown? ClassCastException (sut/id->int {:id "JyX"}))))) + + + +(comment (run-tests))