Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Parsing 在Clojure中思考:避免简单字符串解析器的OOP_Parsing_Clojure_Functional Programming_Atomic - Fatal编程技术网

Parsing 在Clojure中思考:避免简单字符串解析器的OOP

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

我目前正在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
          \( (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!正是我一直在寻找的。