2009-06-28 152 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所以请编辑修复任何错误回报:)