我正在尝试创建一个我之前使用python编写的表(工作计划表),我认为这对我来说是一个很好的Clojure语言介绍。
我在Clojure(或者说是lisp)方面的经验很少,而且我已经在google中进行了多次试验,并且尝试了很多试验和错误,但似乎无法理解这种编码方式。< / p>
以下是我的示例数据(将来会来自sqlite数据库):
(def smpl2 (ref {"Salaried"
[{"John Doe" ["12:00-20:00" nil nil nil "11:00-19:00"]}
{"Mary Jane" [nil "12:00-20:00" nil nil nil "11:00-19:00"]}]
"Shift Manager"
[{"Peter Simpson" ["12:00-20:00" nil nil nil "11:00-19:00"]}
{"Joe Jones" [nil "12:00-20:00" nil nil nil "11:00-19:00"]}]
"Other"
[{"Super Man" ["07:00-16:00" "07:00-16:00" "07:00-16:00"
"07:00-16:00" "07:00-16:00"]}]}))
我最初尝试使用 for 然后转到 doseq ,最后 domap (这似乎更成功)并转储将内容放入html表(我的原始python程序将其从sqlite数据库输出到使用COM的excel电子表格中)。
这是我的尝试(create-table fn):
(defn html-doc [title & body]
(html (doctype "xhtml/transitional")
[:html [:head [:title title]] [:body body]]))
(defn create-table []
[:h1 "Schedule"]
[:hr]
[:table (:style "border: 0; width: 90%")
[:th "Name"][:th "Mon"][:th "Tue"][:th "Wed"]
[:th "Thur"][:th "Fri"][:th "Sat"][:th "Sun"]
[:tr
(domap [ct @smpl2]
[:tr [:td (key ct)]
(domap [cl (val ct)]
(domap [c cl]
[:tr [:td (key c)]]))])
]])
(defroutes tstr
(GET "/" ((html-doc "Sample" create-table)))
(ANY "*" 404))
输出带有部分(工资,经理等)的部分以及部分中的名称,我只是觉得我通过嵌套太多次来滥用domap,因为我可能需要添加更多的domaps只是为了在适当的列中获得转换时间,代码对它有一种“肮脏”的感觉。
如果我没有提供足够的信息,我会事先道歉,我通常不会在编码方面寻求帮助,这也是我的第一个问题:)。
如果您知道有任何更好的方法来做到这一点,或者甚至是我应该知道的新手提示或技巧,那么他们肯定是受欢迎的。
感谢。
答案 0 :(得分:4)
没有办法避免某种嵌套循环。但是你根本不需要domap
,Compojure足够聪明(有时)为你扩展seq。 list
和map
以及for
就足够了。例如MichałMarczyk的回答,或者:
(defn map-tag [tag xs]
(map (fn [x] [tag x]) xs))
(defn create-table []
(list
[:h1 "Schedule"]
[:hr]
[:table {:style "border: 0; width: 90%"}
[:tr (map-tag :th ["Name" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"])]
[:tr (for [[category people] smpl2]
(list* [:tr [:td category]]
(for [person people
[name hours] person]
[:tr [:td name] (map-tag :td hours)])))]]))
“在seq上映射并将所有内容包装在同一个标签中”模式很常见,我有时会喜欢使用辅助函数。
Compojure为您扩展了一级seq
。因此,您可以将一些内容放入list
以使标记按顺序显示在HTML输出中,我这样做是为了显示h1和hr。在你的代码中,你只是抛弃了h1和hr。
但请注意,它只扩展了一个级别。当你有一个列表列表或一个seq列表时,外部seq将会扩展,但内部seq将不会扩展。
user> (println (html (list [:div "foo"] (for [x [1 2 3]] [:div x]))))
<div>foo</div>clojure.lang.LazySeq@ea73bbfd
了解内部seq如何制作Compojure barf。查看compojure.html.gen/expand-seqs
以查看其工作原理,或者如果您愿意,可以更改/修复它。
当您在for
中嵌套for
或list
时,这可能是一个问题,因为for
会返回一个懒惰的seq。但你必须避免使用seq-in-a-seq。我使用上面的list*
。 list
和html
的组合也可以使用。还有很多其他方法。
user> (println (html (list* [:div "foo"] (for [x [1 2 3]] [:div x]))))
<div>foo</div><div>1</div><div>2</div><div>3</div>
user> (println (html (list [:div "foo"] (html (for [x [1 2 3]] [:div x])))))
<div>foo</div><div>1</div><div>2</div><div>3</div>
答案 1 :(得分:3)
我认为您的代码存在一些小问题。我已经尝试在下面的代码中纠正它们。用Compojure 0.3.2进行测试,我敢说它有效。 (当然,请随意指出任何需要改进或似乎不适合你的事情。)
(use 'compojure) ; you'd use a ns form normally
;;; I'm not using a ref here; this doesn't change much,
;;; though with a ref / atom / whatever you'd have to take care
;;; to dereference it once per request so as to generate a consistent
;;; (though possibly outdated, of course) view of data;
;;; this doesn't come into play here anyway
(def smpl2 {"Salaried" [{"John Doe" ["12:00-20:00" nil nil nil "11:00-19:00"]}
{"Mary Jane" [nil "12:00-20:00" nil nil nil "11:00-19:00"]}]
"Shift Manager" [{"Peter Simpson" ["12:00-20:00" nil nil nil "11:00-19:00"]}
{"Joe Jones" [nil "12:00-20:00" nil nil nil "11:00-19:00"]}]
"Other" [{"Super Man" ["07:00-16:00" "07:00-16:00" "07:00-16:00"
"07:00-16:00" "07:00-16:00"]}]})
(defn html-doc [title & body]
(html (doctype :xhtml-transitional) ; the idiomatic way to insert
; the xtml/transitional doctype
[:html
[:head [:title title]]
[:body body]]))
(defn create-table []
(html
[:h1 "Schedule"]
[:hr]
[:table {:style "border: 0; width: 90%;"}
[:tr
[:th "Name"][:th "Mon"][:th "Tue"][:th "Wed"]
[:th "Thur"][:th "Fri"][:th "Sat"][:th "Sun"]]
(for [category smpl2]
[:div [:tr [:td (key category)]] ; for returns just one thing per
; 'iteration', so I'm using a div
; to package two things together;
; it could be avoided, so tell me
; if it's a problem
(for [people (val category)]
(for [person people]
[:tr
[:td (key person)]
(for [hours (val person)]
[:td hours])]))])]))
(defn index-html [request]
(html-doc "Sample" (create-table)))
(defroutes test-routes
(GET "/" index-html)
(ANY "*" 404))
(defserver test-server
{:port 8080}
"/*"
(servlet test-routes))