【问题标题】:Iterating through a map with doseq使用doseq遍历地图
【发布时间】:2011-07-31 15:22:20
【问题描述】:

我是 Clojure 的新手,我正在从 labrepl 做一些基本的事情,现在我想编写一个函数,将某些字母替换为其他字母,例如:elosska → elößkä。

这是我写的:

(ns student.dialect (:require [clojure.string :as str]))
(defn germanize
  [sentence]
  (def german-letters {"a" "ä" "u" "ü" "o" "ö" "ss" "ß"})
  (doseq [[original-letter new-letter] german-letters]
    (str/replace sentence original-letter new-letter)))

但它并没有像我预期的那样工作。你能帮帮我吗?

【问题讨论】:

    标签: clojure


    【解决方案1】:

    这是我的看法,

    (def german-letters {"a" "ä" "u" "ü" "o" "ö" "ss" "ß"}) (defn germanize [s] (reduce (fn[sentence [match replacement]] (str/replace sentence match replacement)) s german-letters))
    (germanize "elosska")
    

    【讨论】:

    • 是的,这比我的还甜。非常好:-)
    • 是的,reduce 更实用更简短,我只是想说明问题出在哪里...
    • 哇,我花了一点时间去理解这个,这是对 reduce 和参数解构的一种巧妙使用!我可以立即在我自己的一些代码上使用该技术。真希望有一本像这样具有功能“模式”的书。
    【解决方案2】:

    这里有两个问题:

    1. doseq 不保留由其评估创建的列表头,因此您不会得到任何结果
    2. str/replace 处理单独的文本副本,产生 4 种不同的结果 - 您可以通过将 doseq 替换为 for 来检查这一点,您将获得包含 4 个条目的列表。

    您的代码可以通过以下方式重写:

    (def german-letters {"a" "ä" "u" "ü" "o" "ö" "ss" "ß"})
    (defn germanize [sentence]
      (loop [text sentence
             letters german-letters]
        (if (empty? letters)
          text
          (let [[original-letter new-letter] (first letters)]
            (recur (str/replace text original-letter new-letter)
                   (rest letters))))))
    

    在这种情况下,会收集中间结果,因此所有替换都应用于相同的字符串,从而生成正确的字符串:

    user> (germanize "elosska")
    "elößkä"
    

    附:也不建议在函数中使用def - 最好将其用于顶级表单

    【讨论】:

    • Hamza Yerlikaya 提供函数式解决方案 - 当您需要收集中间结果时,reduce 非常方便...
    【解决方案3】:

    当然,Alex 已经使用doseq 正确回答了关于原始问题的问题...但我发现这个问题很有趣,并想看看一个更“实用”的解决方案是什么样的。我的意思是不使用循环。

    我想出了这个:

    (ns student.dialect (:require [clojure.string :as str]))
    
    (defn germanize [sentence]
      (let [letters {"a" "ä" "u" "ü" "o" "ö" "ss" "ß"}
            regex (re-pattern (apply str (interpose \| (keys letters))))]
        (str/replace sentence regex letters)))
    

    产生相同的结果:

    student.dialect=> (germanize "elosska")
    "elößkä"
    

    regex (re-pattern... 行简单地计算为#"ss|a|o|u",如果输入为显式字符串,它会更简洁,更易于阅读,但我认为最好只定义一个德语字母。

    【讨论】:

    • 我会说这是要走的路,尤其是在完全关注性能的情况下。它只执行一个替换并构建一个字符串(使用StringBuffer),而loop-ing 或reduce-ing 在替换映射上将构造一个新字符串,并且总是遍历整个字符串从上一步开始。此外,由于(正确地)提出的所有解决方案都使用clojure.string/replace,这是一个可以在一次调用中处理整个操作的内置函数,因此应该允许它处理它。另外,这基本上读起来像问题陈述。 +1。
    • 顺便说一句,我在上面假设替换永远不会引入需要进一步处理的模式。这里就是这种情况,但如果不是,那将是一个完全不同的问题,需要进一步澄清(例如,整个操作是否意味着幂等?如果不是,应该如何订购模式/替换对?它仍然是可以使用地图来保存它们吗?)。顺便说一句... :-)
    • @Michał 感谢您的评论。我没有意识到 reduce 对性能的影响,所以我今天学到了一些有价值的东西(而且还有很多 Clo​​jure 需要学习!)。
    猜你喜欢
    • 2019-07-31
    • 2011-03-23
    • 2020-10-23
    • 2011-12-22
    • 1970-01-01
    • 2019-10-13
    • 2020-01-25
    相关资源
    最近更新 更多