2009-06-28 153 views
7

從谷歌搜索,我發現使用while循環或使用變量是不鼓勵的。Clojure變量和循環

現在我實現了一個非常簡單的算法,它將從輸入流中讀取字符並進行相應的解析:如果輸入是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關鍵字。在一開始就定義一個區塊中的所有變量是不切實際的,還是我錯過了一些關鍵點?

回答

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") 

摘要:

  • 轉換醜,側effectful輸入流中字符的懶序列,這樣我們就可以prete nd這只是該操作其餘部分的一個字符串。正如你所看到的,沒有更多的字符實際上是從計算結果所需的流中讀取的。
  • 將字符串拆分爲兩部分:第一個冒號前的前半部分字符,以及剩下的部分。
  • 使用解構那些部分結合當地人命名beforeafter,並去掉了:,而我們是在它,通過它綁定到一個未使用的地方,取名爲colon描述性。
  • 閱讀before以獲得其數值
  • 採取從after許多字符,一起搗爛他們都變成一個字符串(apply str)

斯萬的答案是如何編寫循環十歲上下的代碼一個很好的例子與Clojure;我希望我的例子是組裝內置函數,以便它們能夠滿足您的需求。這些都可以讓C解決方案看起來「非常簡單」!

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祝你好運!

18

你在寫什麼是類Lisp語法的C代碼(沒有任何意圖)。 不要做什麼定義一個風格是非常明確的,但是如果你不知道「那麼,那麼怎麼辦?」並不是很有用。

順便說一句,我不知道indicator應該做什麼。

這是我會怎麼處理這個問題:

  1. 的問題有兩個部分:找到的字符數閱讀,然後閱讀的字符。因此,我會寫兩個函數:read-countread-item,後者使用前者。

     
    (defn read-count [stream] 
        ;; todo 
    ) 
    
    (defn read-item [stream] 
        ;; todo 
    ) 
    
  2. read-item首先需要確定要讀取的字符數。爲此,它使用我們也將定義的方便功能read-count

     
    (defn read-item [stream] 
        (let [count (read-count stream)] 
        ;; todo 
        )) 
    
  3. 循環是在通常的Clojure最好通過使用looprecur處理。 loop也綁定變量,如letacc是爲了積累讀取的項目,但請注意,它沒有被修改,而是重新綁定每個迭代。

     
    (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 
    
  4. 現在我們需要做的在循環的東西:綁定c下一個字符,但返回時acc是相同(= 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)))))))  ;/
    
  5. 現在我們需要的是read-count函數。它使用類似的循環。

     
    (defn read-count [stream] 
        (loop [count 0] 
        (let [c (.read stream)] 
         (if (= c ":") 
          count 
          (recur (+ (* count 10) 
            (Integer/parseInt c))))))) 
    
  6. 在REPL,debug,refactor上測試它。 .read真的會返回字符嗎?有沒有更好的方法來解析一個整數?

我沒有測試過這一點,我有點被沒有的Clojure的任何經驗,也沒有深刻的認識阻礙了(我用的Common Lisp居多),但我認爲它表明如何處理這類問題以一種「偏袒」的方式。請注意我怎麼不考慮聲明或修改變量。

+0

如果我正確理解你的建議,我應該通過嘗試組合它儘可能多地分解功能來解決問題,對嗎? – 2009-06-28 17:38:46

+1

我不會說盡可能多的,而是儘可能明智---你會發現你可以命名的概念,然後把它們封裝在這個名字後面。但是,這只是上述的一個方面。 – Svante 2009-06-28 18:33:24

6

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)))))) 

這被懶惰地所以每次評價下一個字符串是需要的,它將從輸入流中提取並構造ucted。你可以在網絡流上使用它,例如不必先緩衝整個流,或者擔心從這個流中讀取代碼擔心它是如何構造的。

PS:我不是在我此刻的REPL所以請編輯修復任何錯誤回報:)