【问题标题】:clojure.lang.LazySeq cannot be cast to java.lang.CharSequenceclojure.lang.LazySeq 不能转换为 java.lang.CharSequence
【发布时间】:2018-01-27 02:10:29
【问题描述】:

我正在编写一个函数,对于任何给定的字符串,用相同数量的“。”替换该字符串中的任何数字。字符。

例子:

AT2X -> AT..X

QW3G45 -> QW...G.......

T3Z1 -> T...Z.

我编写了以下 Clojure 函数,但收到一个我不太明白的错误:

java.lang.ClassCastException: clojure.lang.LazySeq (in module: Unnamed Module) can be case to java.lang.Charsequence

我从错误解释为我需要将惰性序列的评估强制返回到字符串(或 CharSequence),但我不知道在哪里这样做或者这是否正确。

(defn dotify
    ;;Replaces digits with the same number of '.'s for use in traditional board formats
    [FEN]
    (let [values (doall (filter isDigit (seq FEN)))]
        (fn [values]
            (let [value (first values)]
                (str/replace FEN value (fn dots [number]
                                           (fn [s times]
                                               (if (> times 0)
                                                   (recur (str s ".") (dec times)))) "" (Character/digit number 10)) value))
        (recur (rest values))) values))

【问题讨论】:

    标签: clojure


    【解决方案1】:

    有一个标准的clojure.string/replace 函数可以处理这种情况。它的最后一个参数可能不仅仅是一个字符串或一个模式,而是一个将找到的片段变成你想要的东西的函数。

    我们先准备这样一个函数:

    (defn replacer [sum-str]
      (let [num (read-string num-str)]
        (apply str (repeat num \.))))
    

    你可以这样试试:

    user> (replacer "2")
    ..
    user> (replacer "9")
    .........
    user> (replacer "22")
    ......................
    user> 
    

    现在将其传递给replace,如下所示:

    user> (clojure.string/replace "a2b3c11" #"\d+" replacer)
    a..b...c...........
    

    【讨论】:

    • 我建议避免使用stringint 之类的名称,使用类似(let [num (read-string arg-str)] ...) 的名称
    【解决方案2】:

    这是一种使用reduce 的方法:

    (defn dotify [s]
      (->> s
           (reduce (fn [acc elem]
                     (if (Character/isDigit elem)
                       (let [dots (Integer/parseInt (str elem))]
                         (apply conj acc (repeat dots \.)))
                       (conj acc elem)))
                   [])
           (apply str)))
    
    (dotify "zx4g1z2h")
    => "zx....g.z..h"
    

    还有一个使用mapcat的版本:

    (defn dotify-mapcat [s]
      (apply str
        (mapcat (fn [c]
                  (if (Character/isDigit c)
                    (repeat (Integer/parseInt (str c)) \.)
                    [c]))
                s)))
    

    您的示例中有一些问题:

    1. 许多内部表单本身就是函数,但看起来您只需要它们的主体或实现,而不是将它们包装在函数中。
    2. 很难通过缩进/空格来判断,但整个函数只是recur-ing,它上面的fn没有被使用或返回。
    3. str/replace 的参数之一是返回函数的函数。

    它有助于将问题分解成更小的部分。一方面,您知道您需要检查字符串中的每个字符并决定是直接返回它还是将其扩展为一系列点。所以你可以从一个函数开始:

    (defn expand-char [^Character c]
      (if (Character/isDigit c)
        (repeat (Integer/parseInt (str c)) \.)
        [c]))
    

    然后在对整个字符串进行操作的高阶函数中使用一次对一个字符进行操作的函数:

    (apply str (mapcat expand-char s))
    => "zx....g.z..h"
    

    请注意,由于 expand-char 函数中的 ^Character 类型提示,这也比上述示例快约 5 倍。

    您也可以使用str/replace 做到这一点:

    (defn expand-char [s]
      (if (Character/isDigit ^Character (first s))
        (apply str (repeat (Integer/parseInt s) \.))
        s))
    (str/replace "zx4g1z2h" #"." expand-char)
    => "zx....g.z..h"
    

    【讨论】:

    • 谢谢!这些是更简洁的例子。仍然掌握何时使用表单与函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 2015-06-13
    • 1970-01-01
    • 1970-01-01
    • 2021-01-04
    相关资源
    最近更新 更多