Parsing 如何在不使用递归的情况下将输入拆分为可变长度的片段?

Parsing 如何在不使用递归的情况下将输入拆分为可变长度的片段?,parsing,clojure,Parsing,Clojure,我有一个包含以下内容的输入文件: 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

我有一个包含以下内容的输入文件:

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中不是惯用的

有没有更好的办法来解决这个问题

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

我不喜欢Michiel Borkent的答案有几个原因,其中一个原因是
((comp number?read string)“3 blah blahb stuff等”)
返回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))))

伯肯特先生的方法是正确的。我还建议您查看
recur
loop
以实现Clojure版本的尾部递归。感谢您指向
recur
loop
!您还可以使用edn/read字符串,因此,(解析内容“3abc\nstuff21”)也会被解析,就像在我的解决方案中一样。呃,不,不,它不会。如果输入格式正确,至少不会。我认为可以很安全地说,这家伙正在解决codejam难题或类似的问题,所以我们看的是整个“包含一个INTER的行,N,后面是包含一组数字的N行”的东西。看看他的代码,这就是它所做的。关于可扩展性,当我们需要从带有数字的行中提取更多信息时,这个解决方案可以很容易地扩展。当截面间的厚度是任意的时,Michiel-Borkent的解可以很容易地扩展。这两种解决方案在各自的意义上都是可扩展的。关于这个解决方案:根据我阅读文档的理解,当需要一次性递归函数时,
loop
recur
更为惯用,然后用
let
定义局部函数。对不起,d.j.sheldrick,我不知怎的错过了你的回答。你说得对,我是在解决“密码果酱”难题。但我最初的问题定义没有反映这一点(它没有指定其他字符串也可以包含数字)。只有当我读到迈克尔·博肯特的答案时,我才意识到这一点。当然,您的解决方案更适合于codejam样式的输入。因此,此解决方案忽略了一个事实,即数字指定了我们应该读取的行数,而只使用带有数字的行作为分度表。非常整洁。