我有一个包含以下内容的输入文件:
2
stuff-11
stuff-12
3
stuff-21
stuff-22
stuff-23
1
stuff-31
我想得到以下结果:
([stuff-11 stuff-12] [stuff-21 stuff-22 stuff-23] [stuff-31])
我最初的解决方案是使用累加器递归,如下所示:
(defn parse-input [lines accum]
(if (= 0 (count lines))
accum
(let [[line-num (Integer. (first lines))]
[head tail] (split-at (+ 1 line-num) lines)]
[stuff (vec (drop 1 head))]]
(parse-input tail (concat accum [stuff]))))
(def result (parse-input input []))
但是,据我所知,由于JVM上缺少TCO,递归函数在Clojure中并不惯用。
有没有更好的方法来解决这个问题?
答案 0 :(得分:1)
user=> (require '[clojure.string :as s])
nil
user=> (require '[clojure.edn :as edn])
nil
user=> (keep-indexed #(if (odd? %) %2)
(partition-by (comp number? edn/read-string)
(s/split-lines (slurp "/tmp/input.txt"))))
(("stuff-11" "stuff-12") ("stuff-21" "stuff-22" "stuff-23") ("stuff-31"))
其中/tmp/input.txt
包含您提供的文字。
如果您希望将一系列向量作为结果,请将#(if (odd? %) %2)
替换为#(if (odd? %) (vec %2))
。
答案 1 :(得分:1)
我不喜欢Michiel Borkent的答案有几个原因,其中一个原因是((comp number? read-string) "3 blah blahb stuff and etc")
返回true。此外,虽然它可能简洁,但它不是非常直观或可扩展。
我认为你有正确的直觉来使用递归,但是懒惰的seq更习惯。
(defn parse-stuff [text]
(let [step (fn step [[head & tail]]
(when-let [n (clojure.edn/read-string head)]
(cons (vec (take n tail))
(lazy-seq (step (drop n tail))))))]
(step (clojure.string/split-lines text))))