iT邦幫忙

2022 iThome 鐵人賽

DAY 24
0

午安~昨天建了一個Reagent App,也開啟好了專案起始頁面,是一個里程碑!

但身為工程師的我們,可不會僅止步於此啦~

除了用clojure(結合clojure生態系的套件hiccup)寫幾個html tag(例如::h1:p)來顯示單純字串之外,

還可能會有以下需求

    1. App專案要能夠使用一些Clojure語法來做顯示上的變化
    1. App專案要既然是前端框架,也要能夠增加自己的component
    1. App專案要能夠增加新的頁面和連結,且每個頁面都可以套用2.所說的component

因此,對應以上需求,我思考了以下三點的學習目標,為我們專案增加一些變化~

1. 介紹clojure(script)語法,且將效果顯示在前端
2. 如何建立自己的component,這樣以後就可以重複利用這些元件囉
3. 也會稍微帶到App裡使用的reitit.frontend套件for route及程式碼

今天是週六,先來第一個輕鬆的主題:

1. 介紹clojure(script)語法,做顯示上的變化

範例:打招呼的貓貓Map

看到首頁畫面上比昨天多了一組Day06 - Maps的collection

key分別為:hello:cat

其中:cat的value看起來是遞增的元素,從(一隻貓)變成(兩隻貓)變成(三隻貓)變成(四隻貓),看起來是分別回傳像list的LazySeq,最後再全部裝到一個LazySeq

如果這是一個clojure kata,語法要怎麼寫出來呢?

思考步驟拆解

step 1. data是什麼? 是1到4的數字sequence

顯示1-4,以數字表達會用 (range 1 5)

step 2. 回傳值是什麼? collection會透過 map回傳

當看到回傳結果是裝在小括號內的 (LazySeq)
腦袋馬上聯想到Day08 - clojure functional programming的map

 (map #(str "Good Morning, " %) ["Goma" "Cara"])
=> ("Good Morning, Goma" "Good Morning, Cara")

因此複習map用法 (map f coll)之後

大概會寫出一下的架構

(map (f) (range 1 5))

因此接下來就要進行到重點了: 拼湊匿名函式

我每次執行一次某個function,
function就會幫我把數字產生到對應的貓數量

step 3. clojure語法 constantly: 給我貓貓,其餘免談

來新學一個constantly
語意上是不變的

constantly returns a function which always returns the same value

例如:雙十節快到了,用def產生一個double-ten的值always 1010

(def double-ten (constantly 1010))
(double-ten 1111) 
=> 1010

(double-ten 1212) 
=> 1010

(double-ten :zzz) 
=> 1010

總之不管怎樣就是回傳同樣的值就對了

舉一反三到我們前端顯示的頁面,constantly 用想辦法印出貓

(def two-cats (constantly '??))
(defn home-page []
  (fn []
    [:span.main
     [:h1 "Welcome to Ting's"]
     [:h1 "2022 IT Ironman project "]
     [:div
      [:h4 "I am Ironman."]
      [:p 
       [:h3 (two-cats 1)] 
       [:h3 (two-cats 100)]]]]))

畫面如下:

無論 (two-cats 1) (two-cats 100) 帶了什麼參數,回傳都只會有兩隻貓~

step 4. clojure語法 comp:

來新學一個comp
comp字義是composition合成物,會幫我們回傳一組function

comp usage

(comp)(comp f)(comp f g)(comp f g & fs)

Takes a set of functions 
and returns a fn that is the composition of those fns.  

The returned fn takes a variable number of args,
applies the rightmost of fns to the args, 
the next fn (right-to-left) to the result, etc.

舉個例子練習一下,定義一個 cat-map,裡面是一個槽狀Map

(def cat-map {:hello {:cat 2}})

 (:cat (:hello cat-map))
;;=> 2

;; 
 ((comp :cat :hello) cat-map)
;;=> 2

(:cat (:hello cat-map))
這一句是說把cat-map裡的 :hello 的值拿出來,為 {:cat 2}}
再把:cat 的值拿出來,return為2

(comp :cat :hello)會從最右邊開始當第一個fn、再來是右邊第二個fn,變成回傳一組fn
那cat-map拿去以上的組合fn做操作,結果還是2

map + comp + constantly組合技

最後,回到本題的開頭,遞增的貓貓要怎麼出現在頁面上呢?

(map (comp #(map (constantly '?) %) range) (range 1 5))

整個前端的頁面code長這樣(變得無比花俏:P):

(def cats (constantly '??))
(defn home-page []
  (fn []
    [:span.main
     [:h1 "Welcome to Ting's"]
     [:h1 "2022 IT Ironman project "]
     [:div
      [:h4 "I am Ironman."]
      [:p 
       [:h3 (cats 1)] 
       [:h3 (cats 100)]
       "I have a " [:strong "bold"]
       [:span {:style {:color "red"}} " heart!"]]]
     {:hello "? Good morning my love"  :cat (map (comp #(map (constantly '?) %) range) (range 1 5))} 

     [:ul
      [:li [:a {:href (path-for :items)} "My IronProject"]]
      [:li [:a {:href "/broken/link"} "Articles link "]]]
     ]))

  • result in UI

補充(on Day25): repeat

後來經過前輩提醒,發現之前有用的repeat也可以做到一樣的效果

  • usage
repeat usage

(repeat x)(repeat n x)

Returns a lazy (infinite!, or length n if supplied) sequence of xs.
  • code example
(take 5 (repeat "cat"))
=> ("cat" "cat" "cat" "cat" "cat")

;; 與上列相同
 (repeat 5 "cat")
=> ("cat" "cat" "cat" "cat" "cat")

;; 回傳遞增數量的"cat"
(map #(repeat % "cat") (range 1 5))
=> (("cat") ("cat" "cat") ("cat" "cat" "cat") ("cat" "cat" "cat" "cat"))

不用再寫一次range (用%代替) ,也不用再用composite起來所有的function
把這個概念套用在前端,來簡化一點語法

  • code

  • result in UI

後記:

本篇文章inspired by clerk這個套件

Clerk enables a rich, local-first notebook experience using standard Clojure namespaces and Markdown files with Clojure code fences. You bring your own editor and workflow, your own interactive computing habits, and Clerk enhances all of that with literate programming and rich visualizations.

看了套件簡介之後,發現這個套件可以做到文學程式設計(英語:literate programming)用人類日常使用的語言寫出自由地表達邏輯,就好像一篇文章一樣。

example:

(def notebooks
  (clojure.java.io/file "notebooks"))

(into #{} (map str) (file-seq notebooks))

result:

#{
"notebooks"
"notebooks/controls.clj"
"notebooks/data_science.clj"
"notebooks/elements.clj"
;;...
}

而Reagent app裡竟然預設就有裝了這個套件,
因此就拿clerk文件裡其中一個範例改編一下,把範例設為回傳貓貓emoji來玩玩看囉!

希望大家也會跟我一樣,覺得學新東西是件有趣的事情~:D

明天接著來講解研究以下兩點!

2. App專案要既然是前端框架,也要能夠`增加自己的component`
3. App專案要能夠`增加新的頁面和連結`,且每個頁面都可以套用`2.`所說的component

上一篇
[Day23] 從ClojureScript 到 Reagent (2) 建立Reagent專案
下一篇
[Day25] 從ClojureScript 到 Reagent (3之2) 建立元件 component
系列文
後端Developer實戰ClojureScript: Reagent與前端框架 Reframe30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言