Clojure中的echo程序是否可以使用惰性无限序列构建?

时间:2017-12-09 00:19:08

标签: clojure echo lazy-sequences

以下面的程序为例:

(defn echo-ints []
  (doseq [i (->> (BufferedReader. *in*)
                 (line-seq)
                 (map read-string)
                 (take-while integer?))]
    (println i)))

这个想法是提示用户输入,然后如果它是一个整数则回显它。但是,在这个特定程序中,几乎每一秒输入都不会立即回显。相反,程序会在一次处理两个输入之前等待额外的输入 据推测,这是在幕后发生的一些性能调整的结果。但是在这种情况下,我真的希望有一个即时的反馈循环。有没有一种简单的方法可以实现这一目标,还是必须对程序的逻辑进行重大改变? (这里的主要动机是将无限的用户输入序列传递给另一个将惰性序列转换为其他惰性序列的函数f。如果我写了某种while循环,我将无法使用{ {1}}。)

2 个答案:

答案 0 :(得分:1)

将懒惰与副作用(在这种情况下打印)混合在一起通常并不好,因为大多数序列函数都具有内置优化功能,这些功能会在功能正确的同时导致意外影响。

这里写得很好:https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects

您尝试做的事情似乎非常适合core.async频道。我认为问题是“用户输入流”和“用户输入流”。而不是用户输入的无限序列,并且&#f; f将惰性序列转换为惰性序列'变为将流转换为另一个流'。这将允许您将f写为可以任意组合的换能器。

答案 1 :(得分:-2)

我会像下面这样做。请注意,我们使用spyxspyxx from the Tupelo library来显示一些结果。

首先,编写一个带有固定测试数据的简单版本:

(ns tst.demo.core
  (:use tupelo.test)
  (:require
    [tupelo.core :as t] )
  (:import [java.io BufferedReader StringReader]))
(t/refer-tupelo)

(def user-input
 "hello
  there
  and
  a
  1
  and-a
  2
  and
  a
  3.14159
  and-a
  4
  bye" )

(defn echo-ints
  [str]
  (let [lines (line-seq (BufferedReader. (StringReader. str)))
        data  (map read-string lines)
        nums  (filter integer? data) ]
    (doseq [it data]
      (spyxx it))
    (spyx nums)))
(newline)
(echo-ints user-input)

这给了我们结果:

it => <#clojure.lang.Symbol hello>
it => <#clojure.lang.Symbol there>
it => <#clojure.lang.Symbol and>
it => <#clojure.lang.Symbol a>
it => <#java.lang.Long 1>
it => <#clojure.lang.Symbol and-a>
it => <#java.lang.Long 2>
it => <#clojure.lang.Symbol and>
it => <#clojure.lang.Symbol a>
it => <#java.lang.Double 3.14159>
it => <#clojure.lang.Symbol and-a>
it => <#java.lang.Long 4>
it => <#clojure.lang.Symbol bye>

nums => (1 2 4)

因此,我们看到它有效,并为我们提供了我们想要的数字。

接下来,编写一个循环版本。当我们的测试数据耗尽时,我们会优雅地终止它。

(defn echo-ints-loop
  [str]
  (loop [lines (line-seq (BufferedReader. (StringReader. str)))]
    (let [line      (first lines)
          remaining (rest lines)
          data      (read-string line)]
      (when (integer? data)
        (println "found:" data))
      (when (not-empty? remaining)
        (recur remaining)))))
(newline)
(echo-ints-loop user-input)

found: 1
found: 2
found: 4

接下来,我们编写一个无限循环来读取键盘。你需要在键盘上用CRTL-C终止这个:

(ns demo.core
  (:require [tupelo.core :as t])
  (:import [java.io BufferedReader StringReader]))
(t/refer-tupelo)


(defn echo-ints-inf
  []
  (loop [lines (line-seq (BufferedReader. *in*))]
    (let [line      (first lines)
          remaining (rest lines)
          data      (read-string line)]
      (when (integer? data)
        (println "found:" data))
      (when (not-empty? remaining)
        (recur remaining)))))

(defn -main []
  (println "main - enter")
  (newline)
  (echo-ints-inf))

我们手动运行:

~/clj > lein run
main - enter
hello
there
1

found: 1
and
a
2
found: 2
and-a
3
found: 3
further more
4
found: 4
^C
                                                                                                                                                                      ~/clj > 
~/clj >