set, let, macros, nuts

吃可爱长大的小学妹 提交于 2019-12-23 12:34:40

问题


I am trying to build a quick toc from an html content. (to make it short)

The code is dead simple:

(defn toc [content]
 (doseq [i (take 5 (iterate inc 1))] 
   (let [h  (str "h" i)]
    (println ($ content h)))))

where content is the html content, and $ is a macro required from clojure-soup

While

($ content "h1")

works, and returns a list of all the tags.

Simple:

($ content (str "h" 1))

just won't make it whatever I do.

How do I force

(str "h" 1) 

to be properly eval-ed before the macro is called ?

Bonus points for explaining why :)


回答1:


This is not possible if, as you imply, $ is a macro: it's simply not how macros work. The macro needs to expand into something, at compile time, and it can only do so once. You have run-time data, like the various values of h, but there is no way to use that at compile-time. To me it sounds like $ should have been a function.




回答2:


Amalloy answer the questions why part. For making it work part you will need to use eval .

In place of ($ content h) use

(eval `($ content ~h))

Another explanation for why this is so is based on the fact that what operations does the macro do at compile time and what it does at runtime (i.e what code it emits). Below is such an example to clear things out.

(def user "ankur")
(defmacro do-at-compile [v] (if (string? v) `true `false))
(defmacro do-at-runtime [v] `(if (string? ~v) true false))

(do-at-compile "hello") ;; => true
(do-at-compile user) ;; => false, because macro does perform the check at compile time
(do-at-runtime "hello") ;; => true
(do-at-runtime user) ;; => true

The $ macro is doing the calculations on the passed second parameter at compile time and hence it is not working in your particular case.



来源:https://stackoverflow.com/questions/12984726/set-let-macros-nuts

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!