【问题标题】:Adding custom behavior to Clojure's sequences为 Clojure 的序列添加自定义行为
【发布时间】:2011-11-18 08:53:59
【问题描述】:

Clojure 如此强大的部分原因在于所有核心数据类型都实现了相同的序列抽象:clojure.lang.ISeq。

这意味着“first”、“concat”、“cons”、“map”、“rest”等函数一般适用于所有这些数据类型。

我的问题是:如何将我自己的自定义函数添加到组合中,并让它适用于从 ISeq 扩展的所有类型?

第一次尝试是定义我自己的协议,然后是“(extend-type clojure.lang.ISeq ...”,但这不起作用(它会编译但不会将行为添加到实际类型中) . 另一个想法是编写一个宏,在所有 Clojure 类型(PersistentHashMap、PersistentList 等)上显式执行“扩展类型”,但这似乎很麻烦。

是否有任何优雅/惯用的方式来做到这一点?也许是多种方法?

【问题讨论】:

    标签: clojure polymorphism abstraction


    【解决方案1】:

    最好的方法是使用正确处理不同数据类型的现有通用 Clojure 函数编写新函数。

    此类通用函数的示例:

    • into 将项目附加到任何类型的集合中
    • empty 返回与其参数类型相同的空集合

    然后您可以编写自己的通用函数来利用这些,例如:

    (defn take-every-other [coll]
      (into 
        (empty coll)
        (map first (partition 2 coll))))
    
     (take-every-other [1 2 3 4 5 6])
     => [1 3 5]
    
     (take-every-other {:a 1 :b 2 :c 3 :d 4})
     => {:a 1, :c 3}
    

    如果您仍需要更通用的函数功能,您可以随时深入 Clojure 源代码,了解这些函数是如何编写的。

    【讨论】:

      【解决方案2】:

      你到底想做什么?

      如果您尝试向现有类型添加行为:要么编写处理 seqs 的普通函数,要么使用多方法或 extend 来做你想做的事。

      另外,需要注意的是,大多数 Clojure “序列”类型(向量、集合、映射)本身并不是序列(它们没有实现 clojure.lang.ISeq),因此您必须做的不仅仅是添加到 @987654323 @如果你想支持他们。

      【讨论】:

      • 不知道为什么这被否决了。这是正确的答案。如果你想编写一个作用于 seqs 的函数,只需编写一个作用于 seqs 的函数,并在该函数中调用 (seq arg) 将传入的任何可排序集合转换为一个 seq。
      【解决方案3】:

      Stuart Sierra 有一篇题为 Solving the Expression Problem with Clojure 1.2 的 IBM Developerworks 文章可能会为您的问题提供见解和答案。

      它使用协议在几种数据类型上定义一组函数,并使用extend 扩展现有的类(数据类型),以便它们可以使用这些函数。这可能不是您想要的,但它可能是解决您问题的一种方法。

      它还向您展示了如何定义实现现有协议/接口的自定义数据类型(使用 defrecorddeftype)。

      【讨论】:

        猜你喜欢
        • 2015-04-15
        • 2021-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-09
        • 1970-01-01
        • 2011-03-29
        相关资源
        最近更新 更多