如何在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