Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

宏 课程示例代码 #3

Open
jiacai2050 opened this issue Dec 23, 2017 · 0 comments
Open

宏 课程示例代码 #3

jiacai2050 opened this issue Dec 23, 2017 · 0 comments

Comments

@jiacai2050
Copy link
Owner

jiacai2050 commented Dec 23, 2017

(ns ^{:author "Jiacai Liu"
      :doc "https://segmentfault.com/l/1500000012556115"}
    demo.macro)

;; quote syntax-quote unquote splicing-unquote
;; 控制 symbol/list 求值时的行为
(let [x '(* 2 3) y x]
  (println `y)
  (println ``y)
  (println ``~y)
  (println ``~~y)
  (println (eval ``~~y))
  (println `(~@y))
  (println `(~y)))

(defmacro nothing [params]
  (println params (type params)))

(nothing x)

;; 一个简单的宏使用示例
(macroexpand-1 '(when (even? (rand-int 100))
                  (println "good luck!")
                  (println "lisp rocks!")))

;; 辅助函数
(defn prime? [n]
  (let [guard (int (Math/ceil (Math/sqrt n)))]
    (loop [i 2]
      (if (zero? (mod n i))
        false
        (if (= i guard)
          true
          (recur (inc i)))))))

(defn next-prime [n]
  (if (prime? n)
    n
    (recur (inc n))))

;; do-primes 基本形式
(defmacro do-primes [[variable start end] & body]
  `(loop [~variable ~start]
     (when (< ~variable ~end)
       (when (prime? ~variable)
         ~@body)
       (recur (next-prime (inc ~variable))))))

;; (doseq [a (range 10)]
;;   (println a))
(do-primes [n 2 (rand-int 10)] ;; [2, 13)
           (println n))

(def start 1)
;; 使用 gensym 保证宏卫生
(defmacro do-primes2 [[variable start end] & body]
  `(let [start# ~start
         end# ~end]
     (loop [~variable start#]
       (when (< ~variable end#)
         (when (prime? ~variable)
           ~@body)
         (recur (next-prime (inc ~variable)))))))

(do-primes2 [n 2 (rand-int 10)] ;; [2, 13)
            (println n))

(defmacro do-primes2-danger [[variable start end] & body]
  `(let [inner-start ~start
         inner-end ~end]
     (loop [~variable inner-start]
       (when (< ~variable inner-end)
         (when (prime? ~variable)
           ~@body)
         (recur (next-prime (inc ~variable)))))))

(macroexpand-1 '(do-primes2 [n 2 (+ 10 (rand-int 30))]
                                  (println n)))
(do-primes2-danger [n 2 (+ 10 (rand-int 30))]
                   (println n))
;; ~(quote inner-start)
(defmacro do-primes2-safe [[variable start end] & body]
  `(let [~'inner-start ~start
         ~'inner-end ~end]
     (loop [~variable ~'inner-start]
       (when (< ~variable ~'inner-end)
         (when (prime? ~variable)
           ~@body)
         (recur (next-prime (inc ~variable)))))))

(do-primes2-safe [n 2 (+ 10 (rand-int 30))]
                 (println n))


;; 宏嵌套宏
(defmacro only-once [names & body]
  (let [gensyms (repeatedly (count names) gensym)]
    (println "mmm")
    `(let [~@(interleave gensyms (repeat '(gensym)))]
       (println "nnn")
       `(let [~~@(mapcat #(list %1 %2) gensyms names)]
          (println "xxx")
          ~(let [~@(mapcat #(list %1 %2) names gensyms)]
             (println "yyy")
             ~@body)))))

(defmacro do-primes3 [[variable start end] & body]
  (only-once [start end]
             `(loop [~variable ~start]
                (when (< ~variable ~end)
                  (when (prime? ~variable)
                    ~@body)
                  (recur (next-prime (inc ~variable)))))))

(do-primes3 [n 2 (+ 10 (rand-int 30))]
           (println n))

;; 使用辅助函数来定义宏
(defmacro def-watched [name & value]
  `(do
     (def ~name ~@value)
     (add-watch (var ~name)
                :re-bind
                (fn [~'key ~'r old# new#]
                  (println '~name old# " -> " new#)))))
(def-watched foo 1)                  
(def foo 2)


(defmacro gen-watch-fn [name]
  (fn [k r o n]
    (println name ":" o " -> " n)))

(defmacro def-watched2 [name & value]
  `(do
     (def ~name ~@value)
     (add-watch (var ~name)
                :re-bind (gen-watch-fn '~name))))

(def-watched2 bar 1)                  
(def-watched2 bar 2)                  

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant