Clojure While 循环

11

我正在尝试使用Clojure编写代码,需要实现以下算法:

从输入流中读取字符,直到不是分隔符为止。

在Java中可以使用while循环轻松实现,但是我不知道如何在Clojure中实现?

while
   read
   readChar != delimiter

   do some processing....
end while
6个回答

10

我不了解Clojure,但是它看起来像Scheme一样支持"let循环":

(loop [char (readChar)]
   (if (= char delimiter)
       '()
       (do (some-processing)
           (recur (readChar)))))

希望这足以让您开始。我参考了http://clojure.org/special_forms#toc9来回答这个问题。

注意:我知道Clojure不鼓励副作用,因此您可能希望返回有用的东西而不是'()。


关于副作用的问题。也许可以在循环中添加一个累加器来展示如何通过函数式方式构建结果? - Michiel Borkent

6

在Clojure 1.3.0上工作,值得一提的是,现在你可以通过类似以下方式编写while循环:

(while truth-expression
  (call-some-function))

这依赖于一些副作用,使得真值表达式最终变为假。 - tenpn

3
循环方法在Clojure中可行,但loop/recur被认为是低级操作,通常更喜欢使用高阶函数。
通常,这种问题会通过创建一个令牌序列(例如您的示例中的字符),并应用Clojure的一个或多个序列函数(doseq、dorun、take-while等)来解决。
以下示例从类Unix系统的/etc/passwd中读取第一个用户名。
(require '[clojure.java [io :as io]])

(defn char-seq
  "create a lazy sequence of characters from an input stream"
  [i-stream]
  (map char 
   (take-while
    (partial not= -1)
    (repeatedly #(.read i-stream)))))

;; process the sequence one token at a time
;; with-open will automatically close the stream for us 

(with-open [is (io/input-stream "/etc/passwd")]
  (doseq [c (take-while (partial not= \:) (char-seq is))]
    ;; your processing is done here
    (prn c)))

2
我是在 line-seq 的精神下想出了这个方法。它完全是懒加载的,表现出了 Clojure 更多的函数式特性,而不是 loop
(defn delim-seq
  ([#^java.io.Reader rdr #^Character delim]
     (delim-seq rdr delim (StringBuilder.)))
  ([#^java.io.Reader rdr #^Character delim #^StringBuilder buf]
     (lazy-seq
       (let [ch (.read rdr)]
         (when-not (= ch -1)
           (if (= (char ch) delim)
             (cons (str buf) (delim-seq rdr delim))
             (delim-seq rdr delim (doto buf (.append (char ch))))))))))

完整粘贴


一定有更简单的方法来做这件事。 - Kzqai

1

while循环通常涉及可变变量,即等待变量满足某个条件; 在Clojure中,您通常会使用尾递归(编译器将其转换为while循环)

以下不是完全的解决方案,但for循环的这种变体在某些情况下可能有所帮助:

(for [a (range 100) 
      b (range 100) 
      :while (< (* a b) 1000)] 
    [a b]
  )

这将创建一个包含所有 a 和 b 的对的列表,直到 (< (* a b) 1000)。也就是说,它会在满足条件时立即停止。如果你用 :when 替换 :while,那么它会找到所有满足条件的对,即使在找到不满足条件的对后。


0

我推出了这个版本:

(defn read-until
  [^java.io.Reader rdr ^String delim]
  (let [^java.lang.StringBuilder salida (StringBuilder.) ]
    (while
      (not (.endsWith (.toString salida) delim))
      (.append salida (str (char (.read rdr))))
    )
    (.toString salida)
  )
)

它寻找的是字符串,而不是单个字符作为分隔符!

谢谢!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接