Clojure for循环不返回原子的更新值

时间:2016-12-02 03:17:37

标签: clojure

我试图编写一个计算给定字符串中元音和辅音数量的函数。返回值是包含两个键vowelsconsonants的地图。每个相应键的值只是计数。

到目前为止我能够开发的功能是

(defn count-vowels-consenants [s]
  (let [m (atom {"vowels" 0 "consenants" 0})
        v #{"a" "e" "i" "o" "u"}]
    (for [xs s]
      (if
          (contains? v (str xs))
            (swap! m update-in ["vowels"] inc)
            (swap! m update-in ["consenants"] inc)
       ))
    @m))

但是(count-vowels-consenants "sldkfjlskjwe")会返回{"vowels":0 "consenants": 0}

我做错了什么?

编辑:将我的输入从str更改为s,因为str是Clojure中的一个函数。

4 个答案:

答案 0 :(得分:3)

I think for is lazy so you're not going to actually do anything until you try to realize it. I added a first onto the for loop which realized the list and resulted in an error which you made by overwriting the str function with the str string. Ideally, you would just do this without the atom rigmarole.

(defn count-vowels-consonants [s]
  (let [v #{\a \e \i \o \u}
        vowels (filter v s)
        consonants (remove v s)]
    {:consonants (count consonants)
     :vowels (count vowels)}))

if the atom is what you want, then use doseq instead of for and it will update the atom for everything in the string. also make sure you don't overwrite the str function by using it in your function binding.

答案 1 :(得分:1)

for is lazy as mentioned by @Brandon H. You can use loop recur if you want. Here I change for with loop-recur.

(defn count-vowels-consenants [input]
  (let [m (atom {"vowels" 0 "consenants" 0})
        v #{"a" "e" "i" "o" "u"}]
    (loop [s input]
      (when (> (count s) 0)
      (if
          (contains? v (first (str s) ))
            (swap! m update-in ["vowels"] inc)
            (swap! m update-in ["consenants"] inc)
       ))
       (recur (apply str (rest s))))
    @m))

答案 2 :(得分:1)

如果这个副作用方案是不可避免的(出于教育原因,我想),只需将for替换为doseq,这是一个副作用,急切等同于for (顺便说一下:你的初始代码中有一个错误:你使用str作为输入参数名称,然后尝试将其用作函数。所以你要从clojure.core中隐藏def,只是尽量避免使用像核心函数一样命名的params:

(defn count-vowels-consenants [input]
  (let [m (atom {"vowels" 0 "consenants" 0})
        v #{"a" "e" "i" "o" "u"}]
    (doseq [s input]
      (if (contains? v (str s))
        (swap! m update-in ["vowels"] inc)
        (swap! m update-in ["consenants"] inc)))
    @m))
#'user/count-vowels-consenants

user> (count-vowels-consenants "asdfg")
;; {"vowels" 1, "consenants" 4}

否则你可以这样做:

user> (reduce #(update %1
                       (if (#{\a \e \i \o \u} %2) 
                         "vowels" "consonants")
                       (fnil inc 0))
              {} "qwertyui")
;;{"consonants" 5, "vowels" 3}

user> (frequencies (map #(if (#{\a \e \i \o \u} %)
                           "vowels" "consonants")
                        "qwertyui"))
;;{"consonants" 5, "vowels" 3}

或者这个(如果你使用true/false而不是“元音/辅音”那么好):

user> (frequencies (map (comp some? #{\a \e \i \o \u}) "qwertyui"))
;;{false 5, true 3}

答案 3 :(得分:0)

这个问题,以及每一个现存的答案,都假设每个角色都是元音或辅音:不是这样。即使在ASCII中,也有小写和大写字母。我会这样做......

(defn count-vowels-consonants [s]
  (let [vowels  #{\a \e \i \o \u
                  \A \E \I \O \U}
        classify (fn [c]
                   (if (Character/isLetter c)
                     (if (vowels c) :vowel :consonant)))]
    (map-v count (dissoc (group-by classify s) nil))))

...其中map-vmap是地图值的函数:

(defn map-v [f m] (reduce (fn [a [k v]] (assoc a k (f v))) {} m))

例如,

(count-vowels-consonants "s2a Boo!")
;{:vowel 3, :consonant 2}

这只遍历字符串一次。

相关问题