Parsing 在Clojure中思考:避免简单字符串解析器的OOP
我目前正在Clojure中实现一个小型解析器,它接受如下输入字符串: aaa(bbb(ccc)ddd(eee))fff(ggg)hhh 并返回不包含括号内字符的字符串,即 (bbb(ccc)ddd(eee))(ggg) 我编写了以下函数:Parsing 在Clojure中思考:避免简单字符串解析器的OOP,parsing,clojure,functional-programming,atomic,Parsing,Clojure,Functional Programming,Atomic,我目前正在Clojure中实现一个小型解析器,它接受如下输入字符串: aaa(bbb(ccc)ddd(eee))fff(ggg)hhh 并返回不包含括号内字符的字符串,即 (bbb(ccc)ddd(eee))(ggg) 我编写了以下函数: (defn- parse-str [input] (let [bracket (atom 0) output (atom [])] (doseq [ch (seq input)] (case ch
(defn- parse-str [input]
(let [bracket (atom 0)
output (atom [])]
(doseq [ch (seq input)]
(case ch
\( (swap! bracket inc)
\) (swap! bracket dec)
nil)
(if (or (> @bracket 0) (= ch \)))
(swap! output conj ch)))
(apply str @output)))
这对我来说很有用:
(解析str“aaa(bbb(ccc)ddd(eee))fff(ggg)hhh”)
“(bbb(ccc)ddd(eee))(ggg)”
然而,我担心我的方法过于面向对象,因为它使用原子作为某种局部变量来保持解析器的当前状态
是否可以从功能性更强的编程角度编写相同的函数?(避开原子?)
任何改进我的代码的意见都将受到欢迎。两种方法:您可以使用显式递归或reduce
(defn parse-str [input]
(letfn [(parse [input bracket result]
(if (seq input)
(let [[ch & rest] input]
(case ch
\( (recur rest (inc bracket) (conj result ch))
\) (recur rest (dec bracket) (conj result ch))
(recur rest bracket (if (> bracket 0)
(conj result ch)
result))))
result))]
(clojure.string/join (parse input 0 []))))
(defn parse-str [input]
(clojure.string/join
(second (reduce (fn [acc ch]
(let [[bracket result] acc]
(case ch
\( [(inc bracket) (conj result ch)]
\) [(dec bracket) (conj result ch)]
[bracket (if (> bracket 0)
(conj result ch)
result)])))
[0 []]
input))))
两种方法:可以使用显式递归或reduce
(defn parse-str [input]
(letfn [(parse [input bracket result]
(if (seq input)
(let [[ch & rest] input]
(case ch
\( (recur rest (inc bracket) (conj result ch))
\) (recur rest (dec bracket) (conj result ch))
(recur rest bracket (if (> bracket 0)
(conj result ch)
result))))
result))]
(clojure.string/join (parse input 0 []))))
(defn parse-str [input]
(clojure.string/join
(second (reduce (fn [acc ch]
(let [[bracket result] acc]
(case ch
\( [(inc bracket) (conj result ch)]
\) [(dec bracket) (conj result ch)]
[bracket (if (> bracket 0)
(conj result ch)
result)])))
[0 []]
input))))
在许多使用局部变量的情况下,只需将任何变化的变量作为参数放入循环,从而使用递归而不是变异
(defn- parse-str [input]
;; Instead of using atoms to hold the state, use parameters in loop
(loop [output []
bracket 0
;; The [ch & tail] syntax is called destructuring,
;; it means let ch be the first element of (seq input),
;; and tail the rest of the elements
[ch & tail] (seq input)]
;; If there's no elements left, ch will be nil, which is logical false
(if ch
(let [bracket* (case ch
\( (inc bracket)
\) (dec bracket)
bracket)
output* (if (or (> bracket* 0) (= ch \)))
(conj output ch)
output)]
;; Recurse with the updated values
(recur output* bracket* tail))
;; If there's no characters left, apply str to the output
(apply str output))))
在许多使用局部变量的情况下,只需将任何变化的变量作为参数放入循环,从而使用递归而不是变异
(defn- parse-str [input]
;; Instead of using atoms to hold the state, use parameters in loop
(loop [output []
bracket 0
;; The [ch & tail] syntax is called destructuring,
;; it means let ch be the first element of (seq input),
;; and tail the rest of the elements
[ch & tail] (seq input)]
;; If there's no elements left, ch will be nil, which is logical false
(if ch
(let [bracket* (case ch
\( (inc bracket)
\) (dec bracket)
bracket)
output* (if (or (> bracket* 0) (= ch \)))
(conj output ch)
output)]
;; Recurse with the updated values
(recur output* bracket* tail))
;; If there's no characters left, apply str to the output
(apply str output))))
这是函数的迭代版本;但它仍然是纯功能的。我发现这样的代码布局很容易阅读。记住,当使用递归时,总是先检查终止条件
(defn parse-str [s]
(loop [[x & xs] (seq s), acc [], depth 0]
(cond
(not x) (clojure.string/join acc)
(= x \() (recur xs (conj acc x) (inc depth))
(= x \)) (recur xs (conj acc x) (dec depth))
(<= depth 0) (recur xs acc depth)
:else (recur xs (conj acc x) depth))))
(defn parse str[s]
(循环[[x&xs](序列s),附件[],深度0]
(续)
(不是x)(clojure.string/join acc)
(=x\()(重复x(合并附件x)(包括深度))
(=x\)(重复出现x(合并附件x)(dec深度))
(这是一个迭代版本的函数;但它在功能上仍然是纯的。我发现这样安排代码很容易阅读。记住,在使用递归时,一定要先检查终止条件
(defn parse-str [s]
(loop [[x & xs] (seq s), acc [], depth 0]
(cond
(not x) (clojure.string/join acc)
(= x \() (recur xs (conj acc x) (inc depth))
(= x \)) (recur xs (conj acc x) (dec depth))
(<= depth 0) (recur xs acc depth)
:else (recur xs (conj acc x) depth))))
(defn parse str[s]
(循环[[x&xs](序列s),附件[],深度0]
(续)
(不是x)(clojure.string/join acc)
(=x\()(重复x(合并附件x)(包括深度))
(=x\)(重复出现x(合并附件x)(dec深度))
(看看parser combinators,Clojure有很多类似Parsec的实现。这种方法功能齐全。看看parser combinators,Clojure有很多类似Parsec的实现。这种方法功能齐全。非常感谢!正是我一直在寻找的。谢谢t!正是我一直在寻找的。