编写Clojure代码以从控制台重复读取行的惯用方式?

Idiomatic way to write Clojure code for repeatedly reading lines from the console?

最近,我正在编写一个小的CLI脚本,该脚本需要从控制台重复读取日期(已读取的日期数是经过计算的,并且每次可能不同)。样本Ruby代码可以帮助您:

1
dates = x.times.collect { print"Enter date:"; Date.parse(gets.chomp) }

仅出于此目的,我用Clojure编写了脚本,然后使用swap!loop...recur编写了一些相当难看的代码。我想知道在Clojure中达到预期效果的最干净方法是什么。 (Clojure确实具有dotimes,但是它不保留从评估主体返回的值...这可能是强调纯函数式编程的语言所期望的。)


当到达文件末尾时,

read-line返回nil。在控制台上,按CTRL-d(在Windows上为CTRL-z)。

您可以使用以下代码利用此优势:

1
2
(doseq [line (repeatedly read-line) :while line]
    (do-something-with line))

如果必须阅读固定数量的行,则可以使用:

1
(repeatedly n read-line)


如果您的目标是最终输入用户输入的确切x日期序列,则:

1
(for [line (repeatedly x read-line)] (DateFormat/parse line))

或使用map

1
(map DateFormat/parse (repeatedly x read-line))

当心Clojure中的惰性序列:系统将要求用户根据需要输入更多日期。如果您的目标是让用户一次输入所有日期(例如在启动时输入),则使用doall

1
(map DateFormat/parse (doall (repeatedly x read-line)))

这将一次读取所有日期,但仍会延迟解析它们,因此日期格式验证可能会在程序中稍后失败。您也可以将doall上移一个级别以立即进行解析:

1
(doall (map DateFormat/parse (repeatedly x read-line)))

并且您可以使用助手功能来读取带有提示的行:

1
2
3
(defn read-line-with-prompt [prompt]
  (print prompt)
  (read-line))

然后用以下任一替换read-line

1
#(read-line-with-prompt"Enter date:")

1
(partial read-line-with-prompt"Enter date:")


您可以执行以下操作:

1
2
3
4
(defn read-dates [n]
     (doall  (for [_ (range n)] (java.util.Date/parse (read-line)))))

(def my-dates (read-dates 5)) ;Read 5 dates from console