[英]Clojure Variables and Looping
通過谷歌搜索,我發現不鼓勵使用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
關鍵字。 在開始時在一個塊中定義所有變量是不切實際的,還是我錯過了一些關鍵點?
您正在編寫的是具有類似Lisp語法的C代碼(無意冒犯)。 用您不做的事情來定義樣式是非常定義的,但是如果您不知道“好吧,那還有什么呢?”並沒有太大幫助。
順便說一句,我不知道應該做什么indicator
。
這就是我解決這個問題的方法:
該問題分為兩部分:找到要讀取的字符數,然后讀取那么多字符。 因此,我將編寫兩個函數: read-count
和read-item
,后者使用前者。
\n (defn讀取計數[流]\n ;; 去做\n )\n\n (defn閱讀項目[流]\n ;; 去做\n )\n
read-item
首先需要確定要讀取的字符數。 為此,它使用了我們還將定義的便捷函數read-count
。
\n (defn閱讀項目[流]\n (讓[count(讀計數流)]\n ;; 去做\n ))\n
通常在Clojure中最好使用loop
和recur
處理loop
。 loop
還綁定變量,如let
。 acc
旨在累積讀取的項目,但請注意,它並未就位修改,而是在每次迭代時重新綁定。
\n (defn閱讀項目[流]\n (循環[計數(讀計數流)\n acc“”]\n ;; 去做\n (重復(十進制計數);新的計數值\n (str acc c))))); acc的新值\n
現在,我們需要在該循環中執行以下操作:將c
綁定到下一個字符,但是當count
為0時返回acc
。 (zero? count)
與(= count 0)
。 我為那些不熟悉它的人添加了if
格式。
\n (defn閱讀項目[流]\n (循環[計數(讀計數流)\n acc“”]\n (如果(零?計數);條件\n acc; 然后\n (讓[c(.read stream)]; \\\n (遞歸(十進制計數);>其他\n (str acc c)))))))); /\n
現在,我們需要的只是read-count
功能。 它使用類似的循環。
\n (defn讀取計數[流]\n (循環[計數0]\n (讓[c(.read流)]\n (如果(= c“:”)\n 計數\n (重復(+(*計數10)\n (整數/ parseInt c))))))))\n
在REPL上進行測試,調試,重構。 .read
是否真的返回字符? 有沒有更好的方法來解析整數?
我沒有對此進行測試,沒有任何經驗或對Clojure的深入了解(我主要使用Common Lisp)使我有點受阻,但是我認為它顯示了如何以“輕率”的方式解決此類問題。 請注意,我不考慮聲明或修改變量。
我想這晚了一點,但是如果您僅將字符串視為字符序列並使用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
來描述,從而去除:
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))))))
字節為字符
(defn bytes-to-chars [bytes]
(map char bytes))
字符到字符串[字符]
(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,所以請編輯以修復所有錯誤:)
我本人正在學習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祝您好運!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.