【问题标题】:Clojure clojure.lang.LazySeq errorsClojure clojure.lang.LazySeq 错误
【发布时间】:2013-08-06 13:48:26
【问题描述】:

我似乎对 clojure I/O(或类型系统)有严重的问题。关键是这个函数,我希望它使用字符串和数字或字符串的集合,并返回与数字相关的字符串字典,比如

(costlist '( '("Milk" 4) '("Bread" 2) '("Milk")))

给予

{"Milk" 4, "Bread" 2 }

定义者

(defn costlist [lst]
    ;returns list of costs and appropriate names
          (let  [snds (filter (fn [x] (not (identical? nil x))) (seconds lst))]
          (zipmap 
                   (firsts (take (count snds) (firsts lst))) 
                   (snds  lst))))

当使用 clojure.lang.PersistentList 类型的列表(我从 clojure.lang.LazySeq 转换而来)时会抛出错误消息

clojure.lang.LazySeq cannot be cast to clojure.lang.IFn

这只会让我感到困惑,因为它的任何论点对我来说似乎都不是 LazySeq。

【问题讨论】:

    标签: clojure lazy-sequences


    【解决方案1】:

    问题是snds是惰性seq,所以(snds lst)会抛出错误。 filter 总是返回一个惰性序列。

    你的函数也太复杂了。尽量简化:

    (defn costlist [lst]
      (zipmap (map first lst)
              (remove nil? (map second lst))))
    

    现在你可以为所欲为:

    (costlist (list '("Milk" 4) '("Bread" 2) '("Milk")))
    

    我使用list 是因为quote 阻止了对表达式的评估 (see ToBeReplaced's answer):

    => '( '("Milk" 4) '("Bread" 2) '("Milk"))
    ((quote ("Milk" 4)) (quote ("Bread" 2)) (quote ("Milk")))
    

    因此,您应该避免使用quote 来构建列表。

    您的解决方案还建议nil 值可能仅出现在列表的末尾。您可以轻松修复它:

    (defn costlist [lst]
      (->>  (filter (comp #{2} count) lst)
            (map vec)
            (into {})))
    

    那么,现在

    (costlist (list '("Milk" 4) '("Milk") '("Bread" 2)))
    

    也可以。

    【讨论】:

      【解决方案2】:

      很难给你一个准确的答案,因为你也在使用你自己的一些功能(秒和第一个)。但是,您应该考虑是否希望 costlist 成为您在上面给出的引用形式。

      以上等价于:

      ((quote ("Milk" 4)) (quote ("Bread" 2)) (quote ("Milk")))
      

      我想你想要的更像是:

      (list '("Milk" 4) '("Bread" 2) '("Milk"))
      

      在您的示例中,最外面的引号导致内部引号被引用!

      【讨论】:

        【解决方案3】:

        不要在'() 中使用'。你不想quote ' 得到quote。 假设您将列表写为:

        '(("Milk" 4)("Bread" 2)("Milk"))
        

        最优雅的可能是:

        (def costlist (comp (partial into {}) 
                            (partial map vec) 
                            (partial filter second))
        

        类似

        (def costlist #(into {} (map vec (filter second %))))
        

        不幸的是,这将创建两个惰性序列,一个会更好地提高性能。 你可以(use '(clojure.core [reducers :as r]) 为自己编写一个很酷的还原函数:

        (def r-fn (comp (r/map vec) (r/filter second)))
        

        这让你去

        (def costlist #(into {} (r-fn %)))
        

        感谢into 使用reduce conj,您的列表元素只会被分配一次。

        由于哈希映射的顺序无关紧要,您也可以使用r/fold。然后您的列表将被过滤并将元素并行转换为向量(如果您的列表包含超过 512 个元素)。

        (def constlist #(r/fold (r/monoid conj hash-map) (r-fn %)))
        

        根据集合的大小,reducer 可能有点太重了。对于像您的示例中的小尺寸,我建议您编写自己的归约函数:

        (def costlist #(reduce (fn [acc [k v]]
                                 (if v
                                   (assoc acc k v)
                                   acc)) {} %)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-06-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-10
          • 2011-04-23
          相关资源
          最近更新 更多