Kiểu dữ liệu map trong clojure tương tự dict trong Python.
Do Clojure mọi kiểu dữ liệu đều là immutable, nên mọi thao tác với map đều trả về 1 map mới.
Để update 1 map dựa theo điều kiện sẽ khá phức tạp cho đến khi dùng cond->
macro.
Ví dụ code Python tương ứng
options = {"max-keys": 100}
if 1 == 1:
options["next-token"] = "abcd"
if 1 != 1:
print("won't run")
if 5 > 3:
options["other-op"] = "hehe"
def call_api(url, options):
print(url, options)
call_api("https://url", options)
# https://url {'max-keys': 100, 'next-token': 'abcd', 'other-op': 'hehe'}
Macro cond->
nhận vào các cặp điều kiện - function để chạy khi điều kiện đúng, sử dụng để update dict:
(def init-options {:max-keys 100})
(def options (cond->
options
(= 1 1) (assoc :next-token "abcd")
(not= 1 1) (prn "won't run")
(> 5 3) (assoc :other-op "hehe")))
(defn call-api [url options]
(println "calling " url "with options: " options))
(call-api "https://url" options)
;; calling https://url with options: {:max-keys 100, :next-token abcd, :other-op hehe}
Đầu vào đầu tiên của cond->
là giá trị ban đầu options
, theo sau là các cặp điều kiện:
nếu (= 1 1)
thì gọi (assoc optíons :next-token "abcd")
để thêm cặp key-value vào map options, kết quả này lại được gán tiếp làm đầu vào cho function (prn KETQUA "wont'run"), nhưng điều kiện (not= 1 1) là false nên function không được chạy, và tiếp tục...
Toàn bộ phần (cond-> ...) có thể đưa trực tiếp vào trong (call-api )
:
(defn call-api [url options]
(println "calling " url "with options: " options))
(call-api "https://url"
(cond->
{:max-keys 100}
(= 1 1) (assoc :next-token "abcd")
(not= 1 1) (prn "won't run")
(> 5 3) (assoc :other-op "hehe")))
Code Python liệu có thể viết trong 1 dòng như vậy?
Ứng dụng
Khi option được gọi sẽ thay đổi theo điều kiện, ví dụ gọi API khi cần paginate sang trang, dùng cond->
để update options gọi trang tiếp theo.
(def next-page-token "XYZ")
(call-api "https://url"
(cond->
init-options
(some? next-page-token) (assoc :next-page-token next-page-token)))
Source
Trong REPL
user=>
(source cond->)
(defmacro cond->
"Takes an expression and a set of test/form pairs. Threads expr (via ->)
through each form for which the corresponding test
expression is true. Note that, unlike cond branching, cond-> threading does
not short circuit after the first true test expression."
{:added "1.5"}
[expr & clauses]
(assert (even? (count clauses)))
(let [g (gensym)
steps (map (fn [[test step]] `(if ~test (-> ~g ~step) ~g))
(partition 2 clauses))]
`(let [~g ~expr
~@(interleave (repeat g) (butlast steps))]
~(if (empty? steps)
g
(last steps)))))
nil
Kết luận
cond->
macro giúp viết code update kiểu dữ liệu theo các điều kiện một cách sạch đẹp hơn.
Hết.
HVN at https://pymi.vn and https://www.familug.org.