Lisp Clojure变量和循环
通过谷歌搜索,我发现不鼓励使用Lisp Clojure变量和循环,lisp,clojure,variables,loops,Lisp,Clojure,Variables,Loops,通过谷歌搜索,我发现不鼓励使用while循环或使用变量 现在我实现了一个非常简单的算法,它将从输入流中读取字符并进行相应的解析:如果输入是10:abcdefghej,它将解析出10,然后读取冒号后的下一个10字节 我有点迷茫的是如何重构它,使它不依赖于变量 (defn decode-string [input-stream indicator] (with-local-vars [length (str (char indicator) ) delimiter (
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)))
另外,我知道声明变量的唯一方法是使用
和localvars
关键字。一开始在一个块中定义所有变量不是很不实际吗?或者我遗漏了一些关键点吗?我自己也在学习Clojure,所以不要把这当作导师的建议,而是同学们的建议
Clojure是一种函数式编程语言。
函数式编程意味着没有循环,没有变量,没有副作用。如果你曾经偏离这三条规则,你需要很好的理由来这么做,而有效的理由是非常罕见的
你显然是一个非常熟练的程序员,所以看看这些信息
你应该对功能设计和面向对象设计有更多的了解
另外,我建议看一些clojure代码,这里是一个托管在
github.com是clojure screencast教程的一部分
可在此处找到该代码的screencast教程,但它不是免费的:
(无论如何,我与peepcode.com没有任何关联)
祝你和Clojure好运 您正在编写的是具有类似lisp语法的C代码(无意冒犯)。通过你不做的事情来定义一种风格是非常有定义性的,但是如果你不知道“好吧,那么还有什么其他的呢?” 顺便说一下,我不知道指示器应该做什么 这就是我处理这个问题的方法:
readcount
和readitem
,后者使用前者
(defn read-count [stream]
;; todo
)
(defn read-item [stream]
;; todo
)
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
读取计数
函数。它使用类似的循环
(defn read-count [stream]
(loop [count 0]
(let [c (.read stream)]
(if (= c ":")
count
(recur (+ (* count 10)
(Integer/parseInt c)))))))
(定义读取计数[流]
(循环[计数0]
(让[c(.read stream)]
(如果(=c):
计数
(重复(+*计数10)
(整数/parseInt c(()()()())))
.read
真的返回字符吗?有没有更好的方法来解析整数我没有测试过这一点,我对Clojure既没有任何经验也没有深刻的了解(我主要使用Common Lisp),这让我有点为难,但我认为它展示了如何以“lispy”的方式处理此类问题。请注意,我是如何不考虑声明或修改变量的。Idomatic Clojure真的很适合处理序列。在C语言中,我倾向于用变量或者多次改变变量的状态来思考问题。在Clojure中,我是按照顺序思考的。在本例中,我将把问题分为三个抽象层:
(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)))
- 将流转换为字节序列李>
- 将字节序列转换为字符序列
- 将字符序列转换为字符串序列李>
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:我现在不在回复中,所以请编辑以修复任何错误:)我想这次聚会晚了一点,但如果您只是将字符串作为一个字符序列来处理,并使用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")
摘要:
- 将丑陋的、有副作用的输入流转换成一个惰性的字符序列,这样我们就可以在这个操作的其余部分将它假装成一个字符串。如您所见,实际上从流中读取的字符数不超过计算结果所需的字符数
- 将字符串分为两部分:第一个冒号前的前半个字符,以及剩余的后半个字符
- 使用destructuring将这些部分绑定到名为
和之前的局部变量
,并通过将其绑定到名为之后的局部变量
的未使用局部变量来进行描述,从而在执行时去掉冒号
:
- 在之前读取
,以获取其数值
- 在之后从
中提取那么多字符,并使用
(apply str)
Svante的回答是如何使用Clojure编写循环ish代码的一个很好的例子;我希望我的是一个组装内置函数的好例子,这样它们就可以满足您的需要。当然,这两种方法都使C解决方案看起来“非常简单” 如果我正确理解了你的建议,我应该通过尽可能多地将其组合成单独的函数来解决问题,对吗?我不会说尽可能多,而是尽可能明智——你找出你可以命名的概念,然后将它们封装在名称后面。然而,这只是上面的一个小方面。你总是能给出这么好的功能性答案+1选择流而不是字符串的决定也会使函数更加复杂
(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")