我目前正在开发一个围绕pdfbox构建的pdf生成库,这是一个java库。 我本身没有问题,我只是不确定什么是clojure做某事的聪明方法。 我试图坚持使用Hiccup风格语法来生成pdf。
有类似的东西(一个非常不切实际的例子):
[:page {:title "hey"}
[:frame {:name "frame1" :top 130}]]
我想在文档的后面检索传递给页面和框架的值(解析后的函数)。例如,下一帧:
[:frame {:bottom (+ 10 (:top "frame1"))} (str "Titre:" (:title page))]
每个函数都将其选项映射传递给另一个函数,因此第一帧的选项实际上如下所示:
{:title "hey", :name "frame1", :top 130}
但显然用户在执行这种代码时无法访问该地图。
对于page
我认为使用通过绑定更新的全局Var似乎是一个好的解决方案(对任何建议开放)。但由于可能存在任何数量的帧,因此无法提前声明。因此,我的问题是:
什么样的功能,概念或做事方式最适合处理这类问题?我怎样才能让用户能够检索这些数据? (如果可能,避免所有选项的全局变量和get-in)
答案 0 :(得分:1)
我对此有一个想法:为什么不为上下文使用动态范围的值,它将包含struct的调用堆栈的所有数据。然后你可以分析你的结构,在这种情况下进行评估。
我会选择这样的东西:
(def ^:dynamic *context* ())
(defn lookup-context [& kv-pairs]
(some #(when (every? (fn [[k v]] (= (k %) v)) kv-pairs) %)
*context*))
(defmacro with-context [data]
(let [items (tree-seq #(and (vector? %) (#{:frame :page} (first %)))
#(nthnext % 2)
data)
ctx-items (reverse (map second items))
let-bindings (zipmap ctx-items (repeatedly gensym))
data (clojure.walk/postwalk-replace let-bindings data)]
(reduce (fn [acc [itm sym]]
`(let [~sym ~itm]
(binding [*context* (cons ~sym *context*)] ~acc)))
data ;; here goes your data parsing
let-bindings)))
所以这个宏建立了级联动态绑定,以及它内部lookup-context
的所有调用(即使在嵌套函数中调用“;;这里是你的数据解析”部分)
例如使用此结构:
(with-context [:page
{:name "page0" :val 1000}
[:frame
{:name "frame0" :val 10}
[:frame {:name "frame1" :val (+ (:val (lookup-context [:name "page0"]))
(:val (lookup-context [:name "frame0"])))}]]])
它将扩展到这个:
(let [G__8644 {:name "page0", :val 1000}]
(binding [*context* (cons G__8644 *context*)]
(let [G__8643 {:name "frame0", :val 10}]
(binding [*context* (cons G__8643 *context*)]
(let [G__8642 {:name "frame1",
:val
(+
(:val (lookup-context [:name "page0"]))
(:val (lookup-context [:name "frame0"])))}]
(binding [*context* (cons G__8642 *context*)]
[:page G__8644 [:frame G__8643 [:frame G__8642]]]))))))
给你你需要的结果,我想
<强>更新强> 作为@ amalloy关于动态范围变量使用的原因的问题的答案:
user> (defn item-factory []
[:frame {:name "frame2" :val (+ (:val (lookup-context [:name "frame1"]))
(:val (lookup-context [:name "page0"])))}])
#'user/item-factory
user>
(with-context [:page
{:name "page0" :val 1000}
[:frame
{:name "frame0" :val 10}
[:frame {:name "frame1" :val (+ (:val (lookup-context [:name "page0"]))
(:val (lookup-context [:name "frame0"])))}]
(item-factory)]])
;;=> [:page {:name "page0", :val 1000}
;; [:frame {:name "frame0", :val 10}
;; [:frame {:name "frame1", :val 1010}]
;; [:frame {:name "frame2", :val 2010}]]]
正如您所看到的,在数据处理中调用的item-factory
函数也是上下文感知的,这意味着lib用户可以简单地分解数据生成,保持对上面定义的项的隐式依赖性定义堆栈。