【问题标题】:Numbering strings inside a vector in clojureclojure 中向量内的字符串编号
【发布时间】:2017-10-27 03:33:31
【问题描述】:

给定以下字符串:

(def text "this is the first sentence . And this is the second sentence")

我想通过在每个单词出现后附加计数来计算文本中出现“this”之类的单词的次数。像这样:

["this: 1", "is" "the" "first" "sentence" "." "and" "this: 2" ...]

作为第一步,我对字符串进行了标记:

 (def words (split text #" "))

然后我创建了一个辅助函数来获取“this”在文本中出现的次数:

 (defn count-this [x] (count(re-seq #"this" text)))

最后我尝试在这个循环中使用count-this函数的结果:

(for [x words]
(if (= x "this")
(str "this: "(apply str (take (count-this)(iterate inc 0))))
x))

这是我得到的:

("this: 01" "is" "the" "first" "sentence" "." "And" "this: 01" "is" ...)

【问题讨论】:

  • 因为没有答案使用clojure.core/frequencies - 这里是doc

标签: string vector clojure count


【解决方案1】:

除了根据需要构建新字符串之外,还可以使用reduce 将计数器线程化通过向量遍历,相当简洁地实现这一点:

(def text "this is the first sentence. And this is the second sentence.")

(defn notate-occurences [word string]
  (->
    (reduce 
        (fn [[count string'] member] 
            (if (= member word) 
              (let [count' (inc count)]
                [count' (conj string' (str member ": " count'))])
              [count (conj string' member)]))
          [0 []]
          (clojure.string/split string #" "))
    second))

(notate-occurences "this" text) 
;; ["this: 1" "is" "the" "first" "sentence." "And" "this: 2" "is" "the" "second""sentence."]

【讨论】:

    【解决方案2】:
    (defn split-by-word [word text]
        (remove empty?
            (flatten
                (map #(if (number? %) (str word ": " (+ 1 %)) (clojure.string/split (clojure.string/trim %) #" "))
                     (butlast (interleave
                          (clojure.string/split (str text " ") (java.util.regex.Pattern/compile (str "\\b" word "\\b")))
                          (range)))))))
    

    【讨论】:

      【解决方案3】:

      您需要在进行过程中保持一些状态。 reduceloop/recuriterate 都这样做。 iterate 只是从一种状态转换到另一种状态。这是过渡函数:

      (defn transition [word]
        (fn [[[head & tail] counted out]]
          (let [[next-counted to-append] (if (= word head)
                                          [(inc counted) (str head ": " (inc counted))]
                                          [counted head])]
            [tail next-counted (conj out to-append)])))
      

      那么你可以使用iterate来行使这个功能,直到没有输入为止:

      (let [in (s/split "this is the first sentence . And this is the second sentence" #" ")
            step (transition "this")]
          (->> (iterate step [in 0 []])
               (drop-while (fn [[[head & _] _ _]]
                             head))
               (map #(nth % 2))
               first))
      
      ;; => ["this: 1" "is" "the" "first" "sentence" "." "And" "this: 2" "is" "the" "second" "sentence"]
      

      【讨论】:

        【解决方案4】:

        这种方法的问题是(apply str (take (count-this)(iterate inc 0))) 每次都会评估相同的东西。

        要对变量进行完全控制,您通常希望使用循环形式。

        例如

        (defn add-indexes [word phrase]
          (let [words (str/split phrase #"\s+")]
            (loop [src words
                   dest []
                   counter 1]
              (if (seq src)
                (if (= word (first src))
                  (recur (rest src) (conj dest (str word " " counter)) (inc counter))
                  (recur (rest src) (conj dest (first src)) counter))
                dest))))
        
        user=> (add-indexes "this" "this is the first sentence . And this is the second sentence")
        ["this 1" "is" "the" "first" "sentence" "." "And" "this 2" "is" "the" "second" "sentence"]
        

        loop 允许您在每次传递时指定每个循环变量的值。因此,您可以根据自己的逻辑决定是否更改它们。

        如果您愿意涉足 Java 并且可能做一些感觉像是在作弊的事情,那么这也是可行的。

        (defn add-indexes2 [word phrase]
          (let [count (java.util.concurrent.atomic.AtomicInteger. 1)]
            (map #(if (= word %) (str % " " (.getAndIncrement count)) %)
                 (str/split phrase #"\s+"))))
        
        user=> (add-indexes2 "this" "this is the first sentence . And this is the second sentence")
        ("this 1" "is" "the" "first" "sentence" "." "And" "this 2" "is" "the" "second" "sentence")
        

        使用可变计数器可能不是纯粹的,但另一方面,它永远不会脱离函数的上下文,因此它的行为不能被外力改变。

        【讨论】:

        • 这里不需要 Java 互操作,这就是 Clojure 使用 atoms 的目的。
        • @Bill 感谢您的回答。我选择了你的答案,因为这就是我现在学习 Clojure(循环,递归)的地方。
        • 一般来说,我认为循环对于这样的事情是一个不错的选择,因为它明确支持在迭代过程中管理值,而且它比尝试将其放入 reduce 更简单。跨度>
        【解决方案5】:

        通常,您可以通过非常简洁的方式从现有的 Clojure 函数中找到一种简单的方法来组合您的解决方案。

        这里有两个非常简短的解决方案来解决您的问题。首先,如果您不需要将结果作为序列,但可以替换字符串:

        (require '(clojure.string))
        
        (def text "this is the first sentence . And this is the second sentence")
        
        (defn replace-token [ca token]
          (swap! ca inc)
          (str token ": " @ca))
        
        (defn count-this [text]
          (let [counter     (atom 0)
                replacer-fn (partial replace-token counter)]
            (clojure.string/replace text #"this" replacer-fn)))
        
        (count-this text)
        ; => "this: 1 is the first sentence . And this: 2 is the second sentence"
        

        上述解决方案利用了可以将函数提供给clojure.string/replace这一事实。

        其次,如果您需要将结果作为序列,则标记化会产生一些开销:

        (defn count-seq [text]
          (let [counter      (atom 0)
                replacer-fn  (partial replace-token counter)
                converter    (fn [tokens] (map #(if (not= % "this")
                                                    % 
                                                    (replacer-fn %))
                                               tokens))]
            (-> text
                (clojure.string/split #" ")
                (converter))))
        
        (count-seq text)
        
        ; => ("this: 1" "is" "the" "first" "sentence" "." "And" "this: 2" "is" "the" "second" "sentence")
        

        loop-recur 模式对于来自非函数式语言的 Clojurian 初学者来说非常常见。在大多数情况下,与mapreduce 和朋友一起使用函数处理有一个更清洁、更惯用的解决方案。

        就像其他答案所述,您最初尝试的主要问题是您的计数器的绑定。事实上,(iterate inc 0) 不受任何约束。看看我上面的例子,想一想绑定原子counter 的范围。作为参考,here is an example of using closures,在这种情况下也可以使用,非常成功!

        作为上述示例的脚注:为了更简洁的代码,您应该通过提取和重用 count-seqcount-this 函数的公共部分来制定更通用的解决方案。此外,可以从count-seq 中提取本地converter 函数。 replace-token 已经适用于所有标记,但请考虑如何将整个解决方案扩展到匹配文本以外的“this”之外。这些留给读者作为练习。

        【讨论】:

          猜你喜欢
          • 2012-07-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多