Skip to content

Instantly share code, notes, and snippets.

@dbushenko
Created March 29, 2013 09:16
Show Gist options
  • Save dbushenko/5269742 to your computer and use it in GitHub Desktop.
Save dbushenko/5269742 to your computer and use it in GitHub Desktop.
(ns lection03.core)
;; Eval
(eval '(+ 1 2))
(defn my-pre-macro1 [a b]
(eval (list '+ a b)))
(my-pre-macro1 1 2)
;; Macros
(defmacro my-macro1 [a b]
`(+ ~a ~b))
(my-macro1 1 2)
(macroexpand-1 '(my-macro1 1 2))
;; Время выполнения и время компиляции
(defmacro my-macro1a [a b]
(println "Hello from macro!")
`(+ ~a ~b))
(macroexpand-1 '(my-macro1a 1 2))
(my-macro1a 1 2)
;; Полностью развернуть все вложенные макросы
(require '[clojure.walk :as w])
(macroexpand '(cond a b c d))
(w/macroexpand-all '(cond a b c d))
;; Использование определённых
;; ранее значений
(def abc 17)
(defmacro my-macro2 [a]
`(println ~(+ abc a)))
(my-macro2 4)
(macroexpand-1 '(my-macro2 4))
;; Использование определённых
;; ранее функций
(defn my-compute1 [a b]
(+ a b))
(defmacro my-macro3 [a b]
`(println ~(my-compute1 a b)))
(macroexpand-1 '(my-macro3 1 2))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Синтаксис макросов
;;
;; quote и syntax-quote
;; ssyntax-quote полностью квалифицирует символы
'abc
`abc
'my/abc
`my/abc
;; Такая квалификация символов по умолчанию позволяет гарантировать, что макрос не сгенерирует код, который по оплошности сошлется или переопределит значение, уже имеющее имя в контексте, где оно используется.
;; unquote и unquote-splicing
`(println abc)
`(println ~abc)
(def cde [1 2 3])
`(println ~cde)
`(println ~@cde)
;; Лексическая и динамическая область видимости
(def b 50)
(defn myprint []
(println b))
(myprint)
(let [b 20]
(myprint))
;;---------------
(def ^:dynamic t 50)
(defn myprint2 []
(println t))
(myprint2)
(binding [t 20]
(myprint2))
;; Гигиена макросов
(def x 17)
(defmacro my-macro4 []
(let [x 28]
`(println ~x)))
(my-macro4)
(macroexpand '(my-macro4))
(defmacro my-macro5 []
`(let [x 28]
(println ~x)))
(my-macro5)
(macroexpand '(my-macro5))
(defmacro my-macro6a []
(let [xx (symbol "x")]
`(let [~xx 28]
(println ~xx))))
(macroexpand '(my-macro6a))
(defmacro my-macro6 []
(let [xx (gensym "x")]
`(let [~xx 28]
(println ~xx))))
(my-macro6)
(macroexpand '(my-macro6))
(defmacro my-macro7 []
`(let [x# 28]
(println x#)))
(my-macro7)
(macroexpand '(my-macro7))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Когда использовать макросы
;; Примеры макросов
;;
;; 1. Создание inline-функций
(defmacro my-compute2 [a b]
(let [x (* 2 (+ a b))] ;; inline computation (compile time)
`(println ~x)))
(my-compute2 1 2)
(macroexpand-1 '(my-compute2 1 2))
;; 2. Вычисление условных выражений
(defn unless* [pred body]
(if (not pred) body))
(unless* (> 2 1) (println "True!"))
(unless* (< 2 1) (println "False!"))
(defmacro unless [pred body]
`(if (not ~pred) ~body))
(unless (> 2 1) (println "True!"))
(unless (< 2 1) (println "False!"))
;; 3. Привязка значений к символам, cоздание переменных и функций
(defmacro defn-with-compl [name args pred & body]
`(do (defn ~name ~args (if ~pred ~@body))
(defn ~(symbol (str name "*")) ~args (if-not ~pred ~@body))))
(defn-with-compl greater-than [a b] (> a b)
(println "Really greater!"))
(greater-than 2 1)
(greater-than 1 2)
(greater-than* 2 1)
(greater-than* 1 2)
;; 4. Создание нового окружения
;; Анафорический макрос
(defmacro alet [comput pred & body]
(let [it-symb (symbol "it")]
`(let [~it-symb ~comput]
(if ~pred
~@body))))
(alet (+ 2 3 4)
(> it 7)
(println (+ it 10)))
;; ------------------
(def ^:dynamic z 17)
(defn get-user-input [] "Hello!")
(defmacro my-macro8 [& body]
`(binding [z ~(get-user-input)]
~@body))
(my-macro8 (println z))
;; 5. Анализ и преобразование исходного кода
(defn inl2 [args acc]
(cond
(>= (count args) 2) (recur (drop 2 args)
(concat (list (first args))
(list acc) (list (second args))))
:else acc))
(defn inl3 [[a op b & args]]
(inl2 args (list op a b)))
(defmacro inline [& args]
(let [res (inl3 args)]
`~res))
;; Code walker
(use 'clojure.walk)
(defn with-inline-computations* [code]
(postwalk
(fn [c]
(if (and (list? c)
(or
(contains? (apply hash-set c) '+)
(contains? (apply hash-set c) '-)))
(cons 'inline c)
c))
code))
(defmacro with-inline-computations [& body]
(let [res (with-inline-computations* body)]
`(do ~@res)))
(macroexpand-1
'(with-inline-computations
(let [my-val (1 + 2 - 3 + 4 - 5)]
(println my-val))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment