2017-07-20 18 views
1

我目前正在致力于一个基于pdfbox,一个Java库的pdf生成库。 我本身没有问题,我只是不确定clojure会采取什么聪明的做法。 我尝试坚持使用Hiccup样式生成pdf。Clojure - 检索运行时间值

有了类似的东西(一个非常不切实际的例子):

[: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更新绑定似乎是一个好的解决方案(开放给任何建议)。但是,因为可能有任何数量的帧,他们不能在早些时候宣布。因此,我的问题是:

什么样的功能,概念或做事方式最适合处理这类问题?我怎样才能让用户检索这些数据? (避免所有选项的全局变量和可能的入门)

回答

1

我对此有了一个想法:为什么不使用动态范围值的上下文,这将包含您的结构的所有数据调用堆栈。然后你可以分析你的结构,在这种情况下进行评估。

我会像这样的东西去:

(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]]])))))) 

为您提供所需的结果,我想

UPDATE 的回答为@ amalloy的有关动态的理由问题范围的VAR用法:

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用户可以简单地分解数据生成,保持对项目定义的隐式依赖d定义堆栈上。

+0

我不明白为什么这应该使用动态变量而不是普通的词法变量。另外你的宏看起来像它需要':page'和':frame'硬编码,而我认为还有其他类型的查询需要根据页面的形状来完成。 – amalloy

+0

动态变量将允许从内部函数调用查找上下文..我不明白如何用词法变量(也许全球原子,但你仍然需要清理它以某种方式)。关于其他类型的处理:这只是一个概念证明。人们不得不做更多的管道工作来使其生产就绪 – leetwinski