1

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

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 中并不惯用。

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

4

2 回答 2

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

于 2013-04-14T21:15:21.800 回答
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))))
于 2013-04-14T23:50:09.160 回答