从谷歌搜索,我发现不鼓励使用while
循环或使用变量。
现在我实现了一个非常简单的算法,它将从输入流中读取字符并进行相应的解析:如果输入为10:abcdefghej
,它将解析10
,然后在冒号后读取下10个字节。
我有点迷失的是我如何重构它,所以它不依赖于变量。
(defn decode-string [input-stream indicator]
(with-local-vars [length (str (char indicator) )
delimiter (.read input-stream )
string (str "")
counter 0 ]
(while (not(= (var-get delimiter) 58 ))
(var-set length (str (var-get length) (char (var-get delimiter)) ))
(var-set delimiter (.read input-stream )))
(var-set length (new BigInteger (var-get length)) )
(var-set counter (var-get length))
(while (not(zero? (var-get counter) ))
(var-set string (str (var-get string) (char (.read input-stream )) ))
(var-set counter (dec (var-get counter))))
(var-get string)))
另外,我了解声明变量的唯一方法是使用with-local-vars
关键字。在开始时在一个区块中定义所有变量是不是不切实际,或者我错过了一些关键点?
答案 0 :(得分:18)
你所写的是具有类似lisp语法的C代码(没有违法行为)。通过不做来定义一种风格是非常明确的,但是如果你不知道“那么,那么还有什么呢?”它不是很有帮助。
顺便说一句,我不知道indicator
应该做什么。
这就是我如何处理这个问题:
问题分为两部分:找到要读取的字符数,然后读取那么多字符。因此,我会写两个函数:read-count
和read-item
,后者使用前者。
(defn read-count [stream] ;; todo ) (defn read-item [stream] ;; todo )
read-item
首先需要确定要读取的字符数。为此,它使用我们也将定义的方便函数read-count
。
(defn read-item [stream] (let [count (read-count stream)] ;; todo ))
循环使用Clojure通常最好使用loop
和recur
来处理。 loop
也绑定变量,例如let
。 acc
用于累积读取的项目,但请注意,它不会在适当的位置进行修改,而是重新绑定每次迭代。
(defn read-item [stream] (loop [count (read-count stream) acc ""] ;; todo (recur (dec count) ; new value for count (str acc c))))) ; new value for acc
现在我们需要在该循环中执行某些操作:将c
绑定到下一个字符,但在acc
为0时返回count
。(zero? count)
是与(= count 0)
相同。我为那些不熟悉它的人注释了if
形式。
(defn read-item [stream] (loop [count (read-count stream) acc ""] (if (zero? count) ; condition acc ; then (let [c (.read stream)] ; \ (recur (dec count) ; > else (str acc c))))))) ; /
现在我们所需要的只是read-count
函数。它使用类似的循环。
(defn read-count [stream] (loop [count 0] (let [c (.read stream)] (if (= c ":") count (recur (+ (* count 10) (Integer/parseInt c)))))))
在REPL,debug,refactor上测试它。 .read
真的会返回字符吗?有没有更好的方法来解析整数?
我没有对此进行过测试,而且由于没有任何经验或对Clojure的深入了解(我主要使用Common Lisp),我有点受到阻碍,但我认为它显示了如何在“lispy”中解决这类问题“ 办法。请注意我如何考虑声明或修改变量。
答案 1 :(得分:10)
我认为这个派对有点晚了,但是如果你只是将字符串视为一个字符序列并使用Clojure的序列处理原语,问题就会简单得多:
(defn read-prefixed-string [stream]
(let [s (repeatedly #(char (.read stream)))
[before [colon & after]] (split-with (complement #{\:}) s)
num-chars (read-string (apply str before))]
(apply str (take num-chars after))))
user> (let [in (java.io.StringReader. "10:abcdefghij5:klmnopqrstuvwxyz")]
(repeatedly 2 #(read-prefixed-string in)))
("abcdefghij" "klmno")
摘要:
before
和after
的本地,并在我们使用:
时将其绑定到未使用的本地,名为{colon
。 1}}用于描述性。before
以获取其数值after
中取出许多字符,然后将它们全部混合成一个包含(apply str)
Svante的回答是如何用Clojure编写循环代码的一个很好的例子;我希望我的内容是组装内置函数的一个很好的例子,这样他们就可以满足您的需求。当然这两个都使得C解决方案看起来只是“非常简单”!
答案 2 :(得分:6)
Idomatic Clojure非常适合使用序列。在C中,我倾向于考虑变量或多次改变变量的状态。在Clojure中,我认为在序列方面。在这种情况下,我会将问题分解为三层抽象:
stream to bytes:
defn byte-seq [rdr]
"create a lazy seq of bytes in a file and close the file at the end"
(let [result (. rdr read)]
(if (= result -1)
(do (. rdr close) nil)
(lazy-seq (cons result (byte-seq rdr))))))
字节到字符
(defn bytes-to-chars [bytes]
(map char bytes))
chars-to-strings [chars]
(defn chars-to-strings [chars]
(let [length-str (take-wile (#{1234567890} %) chars)
length (Integer/parseInt length-str)
length-of-lengh (inc (count length-str))
str-seq (drop length-of-length chars)]
(lazy-seq
(cons
(take length str-seq)
(recur (drop (+ length-of-length length) chars))))))
这是懒惰地评估的,因此每次需要下一个字符串时,它将从输入流中拉出并构造。你可以在网络流上使用它,例如不必首先缓冲整个流,或者担心从这个流中读取代码会担心它是如何构造的。
ps:我现在不在我的REPL所以请编辑修复任何错误:)
答案 3 :(得分:3)
我自己正在学习Clojure,所以不要把它作为大师的建议而是作为同学的建议。
Clojure是一种函数式编程语言。 函数式编程意味着没有循环,没有变量,也没有副作用。如果你偏离这三条规则,你需要很好的理由这样做,而且有正当理由非常罕见。
你显然是一个非常熟练的程序员,所以看看这些信息 你应该更多地了解功能设计与面向对象设计的不同之处。
http://en.wikibooks.org/wiki/Clojure_Programming/Concepts#Sequence_Functions
另外,我建议查看一些clojure代码,这是一个托管的示例程序 github.com被编写为clojure截屏教程的一部分。
http://github.com/technomancy/mire/tree/master
代码所针对的截屏教程可以在这里找到,但它不是免费的:
http://peepcode.com/products/functional-programming-with-clojure
(无论如何我都不隶属于peepcode.com。)
祝Clojure好运!