如何在Scala中编写自定义整数集?具体而言,我想要一个具有以下属性的类:如何在Scala中扩展一组Integer?
- 它是不可变的。
- 它扩展了
Set
的特质。 - 所有集合操作都会根据需要返回此类型的另一个对象。
- 它在其构造函数中使用整数参数的变量列表。
- 其字符串表示形式是逗号分隔的由花括号包围的元素列表。
- 它定义了一个方法
mean
,它返回元素的平均值。
例如:
CustomIntSet(1,2,3) & CustomIntSet(2,3,4) // returns CustomIntSet(2, 3)
CustomIntSet(1,2,3).toString // returns {1, 2, 3}
CustomIntSet(2,3).mean // returns 2.5
(1)和(2)确保该对象确实在合适的Scala方式的东西。 (3)要求构建器代码正确写入。 (4)确保构造函数可以被定制。 (5)是如何覆盖现有toString
实现的示例。 (6)是如何添加新功能的示例。
这应该使用最少的源代码和样板文件来完成,尽可能使用已经存在于Scala语言中的功能。
我已经问了couplequestions了解了任务的各个方面,但我认为这个涵盖了整个问题。目前为止,我得到的最好的response是使用SetProxy
,这很有帮助,但是失败了(3)。我已经在第二版编程Scala中深入研究了“Scala Collections的体系结构”一章并查阅了各种在线示例,但仍然不知所云。
我这样做的目标是写一篇博客文章,比较Scala和Java处理这个问题的设计权衡,但在此之前我必须真正编写Scala代码。我不认为这会很困难,但它一直是,我承认失败。
经过几天后,我想出了以下解决方案。
package example
import scala.collection.{SetLike, mutable}
import scala.collection.immutable.HashSet
import scala.collection.generic.CanBuildFrom
case class CustomSet(self: Set[Int] = new HashSet[Int].empty) extends Set[Int] with SetLike[Int, CustomSet] {
lazy val mean: Float = sum/size
override def toString() = mkString("{", ",", "}")
protected[this] override def newBuilder = CustomSet.newBuilder
override def empty = CustomSet.empty
def contains(elem: Int) = self.contains(elem)
def +(elem: Int) = CustomSet(self + elem)
def -(elem: Int) = CustomSet(self - elem)
def iterator = self.iterator
}
object CustomSet {
def apply(values: Int*): CustomSet = new CustomSet ++ values
def empty = new CustomSet
def newBuilder: mutable.Builder[Int, CustomSet] = new mutable.SetBuilder[Int, CustomSet](empty)
implicit def canBuildFrom: CanBuildFrom[CustomSet, Int, CustomSet] = new CanBuildFrom[CustomSet, Int, CustomSet] {
def apply(from: CustomSet) = newBuilder
def apply() = newBuilder
}
def main(args: Array[String]) {
val s = CustomSet(2, 3, 5, 7) & CustomSet(5, 7, 11, 13)
println(s + " has mean " + s.mean)
}
}
这似乎符合上述所有条件,但它有很多样板。我发现以下Java版本更容易理解。
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
public class CustomSet extends HashSet<Integer> {
public CustomSet(Integer... elements) {
Collections.addAll(this, elements);
}
public float mean() {
int s = 0;
for (int i : this)
s += i;
return (float) s/size();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Iterator<Integer> i = iterator(); i.hasNext();) {
sb.append(i.next());
if (i.hasNext())
sb.append(", ");
}
return "{" + sb + "}";
}
public static void main(String[] args) {
CustomSet s1 = new CustomSet(2, 3, 5, 7, 11);
CustomSet s2 = new CustomSet(5, 7, 11, 13, 17);
s1.retainAll(s2);
System.out.println("The intersection " + s1 + " has mean " + s1.mean());
}
}
这是糟糕的,因为Scala的卖点之一是它比Java更简洁和干净。
Scala版本中有很多不透明的代码。 SetLike
,newBuilder
和canBuildFrom
都是语言样板文件:它们与用大括号编写套件或采取一种方法无关。我几乎可以接受它们作为您为Scala的不可变收藏类库支付的价格(目前接受不变性作为不合格产品),但是仍留下contains
,+
,-
和iterator
,这些只是样板直通代码。 它们至少和getter和setter函数一样丑陋。
看来斯卡拉应该提供一种不写Set
接口样板的方法,但我无法弄清楚。我尝试使用SetProxy
和扩展具体HashSet
类而不是摘要Set
,但这两个都给编译器错误编写错误。
有没有写这个代码不contains
,+
,-
,并且iterator
定义的方式,或者是上面的我能做到的最好?
继axel22建议下面我写了一个简单的实现,它利用了非常有用的,如果不幸名为皮条客我的图书馆模式。
package example
class CustomSet(s: Set[Int]) {
lazy val mean: Float = s.sum/s.size
}
object CustomSet {
implicit def setToCustomSet(s: Set[Int]) = new CustomSet(s)
}
有了这个,你刚刚实例Set
!而非CustomSet
S和需要采取的意思是隐式转换。
scala> (Set(1,2,3) & Set(2,3,5)).mean
res4: Float = 2.0
这满足了我大部分原来的愿望清单,但仍然失败了项目(5)。
东西axel22在评论中说,下面得到的,为什么我问这个问题的心脏。
至于继承,不可变的(集合)类不容易 继承一般...
这广场凭我的经验,但是从语言设计的角度有什么不对劲这里。 Scala是一种面向对象的语言。 (当我看到马丁奥德斯基去年发表演讲时,这是他突出强调的哈斯克尔的卖点。)不变性是明确的首选操作模式。斯卡拉的收藏类被吹捧为其对象库的王冠之宝。然而,当你想扩展一个不可变的集合类时,你会遇到所有这种非正式的传说,以“不要那样做”或“除非你知道你在做什么真的”。通常类的要点是使它们易于扩展。 (毕竟,集合类没有标记为final
。)我正在试图确定这是Scala中的设计缺陷还是我没有看到的设计权衡。
[Extend Scala Set with concrete type](http:// stackoverflow。com/questions/4416885/extend-scala-set-with-concrete-type) – Suma