Я пытаюсь составить таблицу (график работ), я кодировал ранее Python использования, я думаю, что это было бы хорошее введение в язык Clojure для меня.
У меня есть очень мало опыта в Clojure (или шепелявость в том вопросе), и я сделал мои раунды в Google и хорошем бите метода проб и ошибок, но, может казаться, не получаю голову вокруг этого стиля кодирования.
Вот мои демонстрационные данные (будет прибывать из 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"]}]}))
Я пытался ступить через это первоначально использующее для того, чтобы затем перейти на doseq и наконец domap (который кажется более успешным), и дамп содержания в таблицу HTML (моя исходная программа Python произвела это от sqlite базы данных в электронную таблицу Excel с помощью COM).
Вот моя попытка (создавать-таблица 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 только для получения времен сдвига в их надлежащих столбцах, и код получает 'грязное' ощущение к ней.
Я приношу извинения заранее, если я не включаю достаточно информации, я обычно не обращаюсь за помощью на кодировании, также это - мое 1-е ТАК вопрос :).
Если Вы знаете, что какие-либо лучшие подходы делают это или даже подсказки или приемы, я должен знать как новичок, им определенно рады.
Спасибо.
Нет никакого способа избежать вложенного цикла. Но вам совсем не нужен domap
, Compojure достаточно умен (иногда), чтобы расширить seq за вас. Достаточно list
и map
и for
. Например, ответ Михала Марчика, или:
(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)])))]]))
Шаблон "map over a seq и обернуть все в один тег" встречается достаточно часто, чтобы я иногда использовал для него вспомогательную функцию.
Compojure расширяет один уровень seq
для вас. Таким образом, вы можете бросить некоторые вещи в list
, чтобы теги появлялись последовательно в вашем HTML-выводе, что я и сделал, чтобы h1 и hr появились. В вашем коде вы просто отбрасываете h1 и hr.
Но обратите внимание, что это расширяет только один уровень. Когда у вас есть список списков или список секвенций, внешняя секвенция будет расширяться, а внутренние - нет.
user> (println (html (list [:div "foo"] (for [x [1 2 3]] [:div x]))))
<div>foo</div>clojure.lang.LazySeq@ea73bbfd
Посмотрите, как внутренний seq вызывает у Compojure рвоту. Посмотрите в 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>
Я думаю, что у вас есть несколько небольших проблем с кодом. Я попытался исправить их в приведенном ниже коде. Тестируя это с 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))