kurtzace / diary-2024

0 stars 0 forks source link

Clojure notes #4

Open kurtzace opened 4 months ago

kurtzace commented 4 months ago

Example dependencies image

Line by line io image

Json file io

Thread image

image

kurtzace commented 4 months ago

Screenshot_20231125-150645 Screenshot_20231125-150852 17070457398208874751131720069904 17070457907674972834234643729536

kurtzace commented 4 months ago

vector []

(first (rest (nth.. (conj my-arr ​".."​) (cons ​".."​ my-arr)

list '(....)

same as vector

map

{"ke" "val" "key2" "val2"}
{:"ke" "val" :"key2" "val2"}
(hash-map k v 
    k2 v2)

(get my-map "k")
(my-map :"k")
(assoc my-map :k2 36)
(dissoc my-map :k1)
(keys
(vals

Sets

#{:sci-fi :romance :mystery}
(contains? 
(disj my-set "king"
kurtzace commented 3 months ago

let inside func

(​defn​ compute-discount-amount [amount discount-percent min-charge]
​     (​let​ [discount (* amount discount-percent)
​           discounted-amount (- amount discount)]
;;if & let

(​defn​ uppercase-author [book]
​     (if-let [author (:author book)]
​       (.toUpperCase author)))

;;or

(when-let [author (:author book)]
​       (.toUpperCase author)))

merge request params to body


(​defn​ assoc-query-params
​     ​"Parse and assoc parameters from the query string​
​   ​  with the request."​
​     [request encoding]
​     (merge-with merge request
​       (if-let [query-string (:query-string request)]
​         (​let​ [params (parse-params query-string encoding)]
​           {:query-params params, :params params})
​         {:query-params {}, :params {}})))

symbols

(.get the-var)         ​; Get the value of the var: "Austen"​
​   (.-sym the-var)        ​; Get the symbol of the var: author
kurtzace commented 3 months ago

ns

from cli

(require '[myapp.pricing :as pricing]) ;;or (require '[myapp.pricing :refer [my-func]])

from prog

(ns myapp.core
​     (:require [myapp.pricing :as pricing])
​     (:gen-class))

get pre-defined vars

(ns-map (find-ns ​'user​)) 

(require :reload 

(ns-unmap ;; to remove old mapping

Collections

(​def​ title-seq (seq [​

(cons ​"myelem"​ (seq '( ;;add elem

(first

(next

(rest

(sort 

(reverse

(partition 2 my-seq)

(interleave titles authors) ;; combine

(​def​ my-lets [​"L"​ ​"T"​ ​"B"​])

(interpose ​"and"​ my-lets) ;;(L and T and B)

(filter neg? '(1 -22 3 -99 4 5 6 -77))

(some my-func? books) ;;

(​def​ doubled (map #(* 2 %) some-numbers))

(map :title books) ;; or    (map (​fn​ [book] (:title book)) books)

;;shotcut
​   (map (​fn​ [book] (count (:title book))) books) === ​   (map (comp count :title) books) === ​   (​for​ [b books] \n (count (:title b)))

(reduce + 0 numbers)

;; or any single value result
(​defn​ hi-price [hi book]
​     (​if​ (> (:price book) hi)
​       (:price book)
​       hi))
​   
​   (reduce hi-price 0 books)

;; top three rated books by title

(interpose
​     ​" // "​
​     (map :title (take 3 (reverse (sort-by :rating books)))))

count

​   (​defn​ my-count [col]
​     (​let​ [the-seq (seq col)]
​       (loop [n 0 s the-seq]
​         (​if​ (seq s)
​           (recur (inc n) (rest s))
​           n))))
kurtzace commented 3 months ago

IO

(require '[clojure.java.io :as io])
   ... (with-open [r (io/reader ​"authors.txt"​)]
     (line-seq r)

regex

​   (​def​ re #​"\w+"​)
    (​if​ (re-matches  re title)
    (re-seq #​"\w+"​ title)

->> lets you write a nested expression

(​defn​ format-top-titles [books]
​     (->>
​       books
​       (sort-by :rating)
​       reverse
​       (take 3)
​       (map :title)
​       (interpose ​" // "​)
​       (apply str)))

;; is equivalent to 

(​defn​ format-top-titles [books]
​     (apply
​       str
​       (interpose
​         ​" // "​
​         (map :title (take 3 (reverse (sort-by :rating books)))))))

Lazy


(​def​ repeated-text (repeat jack))

(take 7 (cycle [1 2 3])) ;;will give you (1 2 3 1 2 3 1).

    (​def​ numbers [1 2 3])
​   (​def​ trilogy (map #(str ​"w, Book "​ % ) numbers)) ;;gives
​   (​"w, Book 1"​
​    ​"w, Book 2"​
​    ​"w, Book 3"​)

lazy-seq uses some macro magi

​   ​;; Get the contents of the file as a string.​
​   (slurp ​"chap1.txt"​)

;;skip lazy
​   (doall chapters)

doseq, which realizes each item in a lazy sequence one at a time
kurtzace commented 3 months ago

Destructuring (destructuring with function parameters and with let)

(​let​ [[elem1 elem2 elem3] artists]

(​let​ [[dummy dummy elem3] artists]
;or
(​let​ [[_ _ elem3] artists]

    (​def​ authors  [{:name ​"Jane Austen"​ :born 1775}
​                  {:name ​"Charles Dickens"​ :born 1812}]
(​let​ [[{dob-1 :born} {dob-2 :born}] authors]

​   {:name ​"Romeo"​ :age 16 :gender :male}
    (​defn​ character-desc [{name :name age :age gender :gender}] ;; or     (​defn​ character-desc [{:keys [name age gender]}]
kurtzace commented 3 months ago

record (more performant than maps)

​   (defrecord MyRec[name place animal])
(​def​ watson (->MyRec ​"ka"​ ​"IN"​ ​"hen"​))

or

(​def​ elizabeth (map->MyRec
​                     {:name ​"Elizabeth"​
​                      :place ​"PrideApts"​
​                      :animal ​"PrideLions"​}))

(:name elizabeth)

;;use assoc to modify the values
(​def​ specific-eliz (assoc elizabeth :place ​"Bungalow"​))

return the type of the record
​   (class specific-eliz)

(= (instance? MyRec specific-eliz)

protocol

(defprotocol Person
​     (full-name [this])
​     (greeting [this msg])
​     (description [this]))

(defrecord FictionalCharacter[name appears-in author]
​     Person
​     (full-name [this] (:name this))
​     (greeting [this msg] (str msg ​" "​ (:name this)))
​     (description [this]
​       (str (:name this) ​" is a character in "​ (:appears-in this))))

(​def​ sam (->FictionalCharacter ​"Sam Weller"​ ​"The Pickwick Papers"​ ​"Dickens"​))
(greeting sam ​"Hello!"​)

​   (extend-protocol Marketable
​     Employee
​       ...
​     FictionalCharacter
​       (make-slogan [fc] (str (:name fc) ​" is the GREATEST character!"​))

(extend-protocol Marketable
​     String
​       (make-slogan [s] (str ​\"​ s ​\"​ ​" is a string! WOW!"​))

Clojure provides us with reify, which takes a protocol name and some method implementations and creates a one-off implementation of that protocol:

(​def​ test-component (reify Lifecycle
​                        (start [this]
​                          (println ​"Start!"​)
​                          this)
​                        (stop [this]
​                          (println ​"Stop!"​)
​                          this)))
kurtzace commented 3 months ago

tests

    (ns inventory.core-test
​     (:require [clojure.test :refer :all])
​     (:require [inventory.core :as i]))

(deftest test-finding-books
​     (is (not (nil? (i/find-by-title ​"Emma"​ books)))))

;;in run_tests.clj
(require '[inventory.core-test :as ct])
​   
​   (ct/test-finding-books)

with lein test

test.check library provides a variety of generators, values you can use to generate test data

​   (require '[clojure.test.check.generators :as gen])
​   
​   (gen/sample gen/string-alphanumeric)

​   (​def​ copies-gen (gen/such-that (complement zero?) gen/pos-int))

;;generate an endless supply of inventories:

(​def​ inventory-gen (gen/not-empty (gen/vector book-gen)))
...

(​def​ book-gen
​     (gen/hash-map :title title-gen :author author-gen :copies copies-gen))

prop check ( test.check )

;;each positive integer is smaller than the next

(prop/for-all [i gen/pos-int]
​     (< i (inc i)))

(defspec longrange-equals-range 100
​     (prop/for-all [start gen/int
​                    end gen/int
​                    step gen/s-pos-int]
​                   (= (clojure.lang.Range/create start end step)
​                      (clojure.lang.LongRange/create start end step))))

Spec


(ns my-app.core
​  (:require [clojure.spec.alpha :as s]))

(s/valid? number? 44)     ​; Returns true.
(s/and number? #(> % 10) #(< % 100)))

(​def​ n-or-s (s/or :a-number number? :a-string string?))
    (s/valid? n-or-s ​"Hello!"​)
(s/coll-of string?)

    (​def​ book-s
​     (s/keys :req-un [:inventory.core/title
​                      :inventory.core/author
​                      :inventory.core/copies]))

(s/valid? book-s {:title ​"Arabian Nights"​ :copies 17})

clojure.core/def! The idea of clojure.spec/def is to allow you to register your spec in a JVM-wide registry of specs any code can then use. For example, this:
kurtzace commented 3 months ago

Functional Spec

explain to figure out why your book map failed to match the spec
(s/explain ::book {:author :austen :title :emma})

;; for function
​   (s/fdef find-by-title
​      :args (s/cat :title ::title
​                   :inventory ::inventory))

;;to use functional spec with unit test generator

(​defn​ book-blurb [book]
| (str ​"The best selling book "​ (:title book) ​" by "​ (:author book)))

(s/fdef book-blurb
​     :args (s/cat :book ::book)
​     :ret (s/and string? (partial re-find #​"The best selling"​)))

​ (stest/check ​'inventory.core/book-blurb​)

 s/?. Essentially, s/? makes the next part of the spec optional

;use explain to figure out why your book map failed to match the spec:
 (s/explain ::book {:author :austen :title :emma})

;even conform

;within tests
(require '[clojure.spec.test.alpha :as stest])
​   
​   (stest/check ​'inventory.core/book-blurb​)

    (​defn​ check-return [{:keys [args ret]}]
​     (​let​ [author (-> args :book :author)]
​       (not (neg? (.indexOf ret author)))))
​ 

;:fn key.   
​   (s/fdef book-blurb
​     :args (s/cat :book ::book)
​     :ret (s/and string? (partial re-find #​"The best selling"​))
​     :fn check-return)
kurtzace commented 3 months ago

loop image

kurtzace commented 3 months ago

Java interop

(​def​ authors (java.io.File. ​"authors.txt"​))
(.exists authors)

(​def​ rect (java.awt.Rectangle. 0 0 10 20))
we could then reach into its public fields:

​   (println ​"Width:"​ (.-width rect))
​   (println ​"Height:"​ (.-height rect))

​   (ns read-authors
​     (:import (java.io File InputStream)))

;; In the REPL.​
​   
​   (import '(java.io File InputStream))

(​def​ temp-authors-file (File/createTempFile ​"authors_list"​ ​".txt"​))
kurtzace commented 3 months ago

NEW PROJECT

lein new my-proj

(defproject my-proj ​"0.1.0-SNAPSHOT"​
​     :description ​"FIXME: write description"​
​     :url ​"http://example.com/FIXME"​
​     :license {:name ​"Eclipse Public License"​
​               :url ​"http://www.eclipse.org/legal/epl-v10.html"​}
​     :dependencies [[org.clojure/clojure ​"1.8.0"​]
​                    [com.google.code.gson/gson ​"2.8.0"​]])

;you can use the built-in memfn function to turn a method name into a function. For example, while .exists is not a function, (memfn exists) is.
kurtzace commented 3 months ago

threads

main

​   (​defn​ -main []
​     (println ​"Coming to you live from the main thread!"​))

new thread

​   ​;; Make a thread.​
​   
​   (​defn​ do-in-a-thread []
          (Thread/sleep 3000)
​     ....)
​   
​   (​def​ the-thread (Thread. do-in-a-thread))
​   
​   ​;; And run it.​
​   
​   (.start my-thread)

    (.join my-thread)

;to deliver result
(​def​ the-result (promise)); declare promise
    (deliver the-result ​"A-Result"​); from bg process 
(deref the-result) ; or @the-result to get answer by waiting

;or better to autocreate thread

​   (​def​ revenue-future
​     (future (apply + (map :revenue inventory))))
@revenue-future

;java way
​   (import java.util.concurrent.Executors)
(​def​ fixed-pool (Executors/newFixedThreadPool 3))

(.execute work-1-func)
(.execute work-2-func)

;for timeout
(deref revenue-promise 500 :oh-snap)

;for non blocking main thread - allowing main to exit
(.setDaemon t true)

;parallel map
(pmap some-computationally-intensive-f a-collection)
kurtzace commented 3 months ago

Compojure library

(ns storefront.handler
​     (:require [compojure.core :refer :all]
​               [compojure.handler :as handler]))
​   
​   (defroutes main-routes
​     (GET ​"/"​ [] ​"Welcome"​)
​     (GET ​"/book"​ [title author]
​          (str ​"...."​)))
​   
​   (​def​ app (handler/site main-routes))
kurtzace commented 3 months ago

state

(​def​ counter (atom 0))

;then inside a method
(swap! counter inc); or ​   (swap! counter + 1)
(​if​ (= @counter 5)...

;maps too
​   (​def​ by-title (atom {}))

(​defn​ add-book [{title :title :as book}]
   (swap! by-title #(assoc % title book)))
  (swap! by-title #(assoc % dissoc book)))

;; for keeping 2 values synchronized
(​def​ by-title (ref {}))
(​def​ total-copies (ref 0))

;inside method
(dosync
​       (alter by-title #(assoc % title book))
​       (alter total-copies + (:copies book)))

;If there are side effects that need to happen as you update your mutable state, then use an agent. 

;The purpose of memoize is to speed up your program by caching the results of function calls.

(shutdown-agents))

    (​def​ title-agent (agent ​"..."​))
        (send title-agent + "...")
​   (​if​ (agent-error title-agent)
​     (restart-agent
​       title-agent
​       ​"..."​
​       :clear-actions true))

    (eval '(count title))

to read untrusted data'
​   (require '[clojure.edn :as edn])
        (​def​ untrusted (edn/read))
kurtzace commented 3 months ago
;macro
​   (​defmacro​ print-it [something]
​     (list ​'println​ ​"Something is"​ something))

​   (print-it (+ 10 20));; (println ​"Something is"​ (+ 10 20))
kurtzace commented 3 months ago

references