Scala で整数のカスタム セットを作成するにはどうすればよいですか? 具体的には、次のプロパティを持つクラスが必要です。
- 不変です。
- それは
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) コンストラクターをカスタマイズできるようにします。toString
(5) は、既存の実装をオーバーライドする方法の例です。(6) は、新しい機能を追加する方法の例です。
これは、Scala 言語に既に存在する機能を可能な限り利用して、最小限のソース コードとボイラープレートで行う必要があります。
タスクの側面についていくつか 質問しましたが、これで問題全体がカバーされていると思います。私がこれまでに得た最良の回答SetProxy
は、 を使用することです。これは役に立ちますが、上記の (3) で失敗します。私は『Programming in Scala 』の第 2 版の「The Architecture of 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 のセールス ポイントの 1 つは、Java よりも簡潔でクリーンであることです。
Scala バージョンには不透明なコードがたくさんあります。SetLike
、newBuilder
、およびcanBuildFrom
はすべて言語定型文です。これらは、中括弧を使用してセットを記述したり、平均を取ることとは関係ありません。Scala の不変コレクション クラス ライブラリの代償として、私はほとんど受け入れることができます (今のところ、不変性を修飾されていないものとして受け入れていcontains
ます) 。それらは、少なくとも getter および setter 関数と同じくらい醜いです。+
-
iterator
Scala はインターフェイスのボイラープレートを書かない方法を提供する必要があるようですSet
が、私にはわかりません。抽象クラスの代わりに具象クラスを使用SetProxy
して拡張しようとしましたが、どちらも複雑なコンパイラエラーを引き起こしました。HashSet
Set
contains
、+
、-
、および定義なしでこのコードを記述する方法はありiterator
ますか、または上記が私ができる最善の方法ですか?
以下のaxel22のアドバイスに従って、残念ながら pimp my ライブラリ パターンという名前の非常に便利なものを利用する簡単な実装を書きました。
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
これにより、 s の代わりにs をインスタンス化CustomSet
し、必要に応じて暗黙的な変換を行って平均を取得します。
scala> (Set(1,2,3) & Set(2,3,5)).mean
res4: Float = 2.0
これは私の最初のウィッシュ リストのほとんどを満たしますが、項目 (5) はまだ失敗しています。
以下のコメントでaxel22が言ったことは、私がこの質問をしている理由の核心になります。
継承に関しては、イミュータブル (コレクション) クラスは一般に継承が容易ではありません…</p>
これは私の経験と一致しますが、言語設計の観点からは、ここで何かがおかしいようです。Scala はオブジェクト指向言語です。(昨年、Martin Odersky が講演するのを見たとき、それが彼が強調した Haskell のセールス ポイントでした。) 不変性は、明示的に好まれる動作モードです。Scala のコレクション クラスは、Scala のオブジェクト ライブラリの至宝としてもてはやされています。それでも、不変のコレクション クラスを拡張したい場合は、「そうしないでください」または「自分が何をしているのか本当にわからない限り、試してはいけません」という非公式の知識に出くわします。通常、クラスのポイントは、簡単に拡張できるようにすることです。(結局のところ、コレクション クラスは とマークされていませんfinal
。) I'