【问题标题】:A case class as a "wrapper" class for a collection. What about map/foldLeft/案例类作为集合的“包装”类。 map/foldLeft/ 怎么样
【发布时间】:2013-05-12 21:38:13
【问题描述】:

我尝试做的是想出一个 case 类,我可以在模式匹配中使用它,它只有一个字段,例如一个不可变的集合。此外,我想使用 map、foldLeft 等应该传递给集合的函数。我尝试如下:

case class foo(s:Set[String]) extends Iterable[String] {
    override def iterator = s.iterator
}

现在,如果我尝试使用例如map 函数,我得到一个类型错误:

var bar = foo(Set() + "test1" + "test2")
bar = bar.map(x => x)

 found   : Iterable[String]
 required: foo
   bar = bar.map(x => x)
                ^

类型错误非常好(据我了解)。但是,我想知道如何为一个集合实现一个包装案例类,以便可以调用 map、foldLeft 等并且仍然接收案例类的对象。是否需要覆盖所有这些功能,还是有其他方法?

编辑

我倾向于接受对我有用的 Régis Jean-Gilles 的解决方案。然而,在谷歌上搜索了几个小时后,我发现了另一个有趣的 Scala trait,名为 SetProxy。我找不到任何琐碎的例子,所以我不确定这个特征是否符合我的要求:

  1. 想出一个自定义类型,即与Set不同的类型
  2. 类型必须是案例类(我们要做模式匹配)
  3. 我们需要“委托”方法 map、foldLeft 等,它们应该将调用传递给我们的实际集合,并以我们的新类型返回结果集合包装 arround

我的第一个想法是扩展Set,但我的自定义类型Foo 已经扩展了另一个类。因此,第二个想法是混合特征 Iterable 和 IterableLike。现在我对 SetProxy 的特性感到不满,这让我想到了哪个是“最好的”方式。你有什么想法和经历?

自从我三天前开始学习 Scala 以来,任何指点都非常感谢!

【问题讨论】:

    标签: scala


    【解决方案1】:

    嗯,这对我来说听起来很有希望,但 Scala 说变量 b 是 Iterable[String] 类型而不是 Foo 类型,即我看不出 IterableLike 在这种情况下有何帮助

    你是对的。仅从IterableLike 继承,如 mpartel 所示,会使 some 方法的返回类型更精确(如filter,它将返回Foo),但对于其他如map flatMap 的您需要提供适当的 CanBuildFrom 隐式。 这是一个可以做到这一点的代码 sn-p:

    import collection.IterableLike
    import collection.generic.CanBuildFrom
    import collection.mutable.Builder
    
    case class Foo( s:Set[String] ) extends Iterable[String] with IterableLike[String, Foo] {
      override def iterator = s.iterator
      override protected[this] def newBuilder: scala.collection.mutable.Builder[String, Foo] = new Foo.FooBuilder
      def +(elem: String ): Foo = new Foo( s + elem )
    }
    
    object Foo  {
      val empty: Foo = Foo( Set.empty[String] )
      def apply( elems: String* ) = new Foo( elems.toSet )
    
      class FooBuilder extends Builder[String, Foo] {
        protected var elems: Foo = empty
        def +=(x: String): this.type = { elems = elems + x; this }
        def clear() { elems = empty }
        def result: Foo = elems
      }
    
      implicit def canBuildFrom[T]: CanBuildFrom[Foo, String, Foo] = new CanBuildFrom[Foo, String, Foo] {
        def apply(from: Foo) = apply()
        def apply() = new FooBuilder
      }
    }
    

    还有一些repl中的测试:

    scala> var bar = Foo(Set() + "test1" + "test2")
    bar: Foo = (test1, test2)
    
    scala> bar = bar.map(x => x) // compiles just fine because map now returns Foo
    bar: Foo = (test1, test2)
    

    【讨论】:

      【解决方案2】:

      继承 IterableLike[String, Foo] 为您提供所有这些方法,以便它们返回 Foo。 IterableLike 要求你在 iterator 之外实现 newBuilder

      import scala.collection.IterableLike
      import scala.collection.mutable.{Builder, SetBuilder}
      
      case class Foo(stuff: Set[String]) extends Iterable[String] with IterableLike[String, Foo] {
        def iterator: Iterator[String] = stuff.iterator
      
        protected[this] override def newBuilder: Builder[String, Foo] = {
          new SetBuilder[String, Set[String]](Set.empty).mapResult(Foo(_))
        }
      }
      
      // Test:
      val a = Foo(Set("a", "b", "c"))
      val b = a.map(_.toUpperCase)
      println(b.toList.sorted.mkString(", "))  // Prints A, B, C
      

      【讨论】:

      • 嗯,这对我来说听起来很有希望,但 Scala 说变量 b 的类型为 Iterable[String] 而不是 Foo,即我看不出 IterableLike 在这种情况下有何帮助:
      • 你的 Scala 版本是什么? 2.10.1 对我来说。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-21
      • 1970-01-01
      • 2019-04-26
      相关资源
      最近更新 更多