Ham*_*aya 7 lisp variables loops clojure
从谷歌搜索,我发现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)))
Run Code Online (Sandbox Code Playgroud)
另外,我理解声明变量的唯一方法是使用with-local-vars关键字.在开始时在一个块中定义所有变量是不是不切实际,或者我错过了一些关键点?
Sva*_*nte 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当count为0 时返回与之(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"方式处理这类问题.请注意我如何考虑声明或修改变量.
ama*_*loy 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")
Run Code Online (Sandbox Code Playgroud)
总结:
before和after,并去掉了:,而我们是在它,通过它绑定到一个未使用的地方,取名colon为描述性.before以获取其数值after,然后将它们全部混合成一个字符串(apply str)Svante的答案是如何用Clojure编写循环代码的一个很好的例子; 我希望我的内容是组装内置函数的一个很好的例子,这样他们就可以满足您的需求.当然这两个都使C解决方案看起来不是"非常简单"!
Idomatic Clojure非常适合使用序列.在C中,我倾向于考虑变量或多次改变变量的状态.在Clojure中,我认为在序列方面.在这种情况下,我会将问题分解为三层抽象:
流到字节:
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))))))
Run Code Online (Sandbox Code Playgroud)
字节到字符
(defn bytes-to-chars [bytes]
(map char bytes))
Run Code Online (Sandbox Code Playgroud)
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))))))
Run Code Online (Sandbox Code Playgroud)
这是懒惰地评估的,因此每次需要下一个字符串时,它将从输入流中拉出并构造.你可以在网络流上使用它,例如不必先缓冲整个流,或者担心从这个流中读取代码会担心它是如何构造的.
ps:我现在不在我的REPL所以请编辑修复任何错误:)