1

私は Scala を使ったプログラミングがまったく初めてで、次の問題があります。

多くのデータ型 (Int、String など) を含むことができる HashMap が必要です。C++ では、BOOST の MultiMap を使用します。Scala には MultiMap トレイトが用意されていると聞いたことがあります。基本的に私がしたいことは次のとおりです。

val map = HashMap[String, ListBuffer[_]]

ListBuffer の要素の具体的なデータ型は、実行時に決定されます。この実装をコンソールでテストすると、次のように動作します。

scala> val a = new HashMap[String, ListBuffer[_]]()
a: scala.collection.mutable.HashMap[String,scala.collection.mutable.ListBuffer[_]] = Map()

scala> val b = new ListBuffer[String]()
b: scala.collection.mutable.ListBuffer[String] = ListBuffer()

scala> val c = new ListBuffer[Int]()
c: scala.collection.mutable.ListBuffer[Int] = ListBuffer()

scala> b += "String"
res0: b.type = ListBuffer(String)

scala> c += 1
res1: c.type = ListBuffer(1)

scala> a += "String Buffer" -> b
res2: a.type = Map((String Buffer,ListBuffer(String)))

scala> a += "This is an Int Buffer" -> c
res3: a.type = Map((String Buffer,ListBuffer(String)), (This is an Int Buffer,ListBuffer(1)))

したがって、基本的には機能します。私の最初の質問は、ListBuffer を間接レイヤーとして使用せずに、Scala で同じ動作を実装する可能性があるかどうかです。

たとえば、次の内容のマップを取得します: Map((String, 1),(String, "String value"), ...)

上記の ListBuffer-Implementation を使用しようとすると、次の型の不一致エラーが発生します。

found   : _$1 where type _$1
required: _$3 where type _$3

私は基本的に次のことをしようとしています:

イテレータを使用して、マップのキーを反復処理します。

var valueIds = new ListBuffer[Int]()
val iterator = map.keys
iterator.foreach(key => { valueIds += setValue((map.apply(key)).last) }

setValue は Int を返し、ListBuffers の最後の要素を処理する必要があるメソッドです。

上記のタイプの不一致を修正する方法を知っている人はいますか?

ご協力いただきありがとうございます!

よろしく

4

4 に答える 4

6

キーごとに複数の値を保存する必要がありますか?もしそうなら、ListBufferとにかくまたは他のコレクションを使用する必要があります。(ScalaのMultiMapストアセットなので、複製を保持する必要がある場合、それらは機能しません。)

キーごとに複数の値を保存する必要がない場合は、次のAnyタイプが必要です。

scala> val map = collection.mutable.HashMap[String,Any]()
map: scala.collection.mutable.HashMap[String,Any] = Map()

scala> map += "One" -> 1
res1: map.type = Map((One,1))

scala> map += "Two" -> "ii"
res2: map.type = Map((Two,ii), (One,1))

scala> map += "Three" -> None
res3: map.type = Map((Three,None), (Two,ii), (One,1))

ここで、おそらくパターンマッチングを実行するか、collectを使用して値に役立つことを行う必要があります。

scala> map.values.foreach(_ match { case i: Int => println("We stored the number "+i) })
We stored the number 1

scala> map.values.collect{ case i: Int => i }
res4: Iterable[Int] = List(1)
于 2011-03-30T15:20:37.693 に答える
4

ScalaにはMultiMapクラスがあります

scala> import scala.collection.mutable.{HashMap, MultiMap, Set}     
import scala.collection.mutable.{HashMap, MultiMap, Set}

scala> val a = new HashMap[String, Set[Any]] with MultiMap[String, Any]
a: scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[Any]] with scala.collection.mutable.MultiMap[String,Any] = Map()

scala> a.addBinding("Pants", 1)
res0: a.type = Map((Pants,Set(1)))

scala> a.addBinding("Pants", 2)
res1: a.type = Map((Pants,Set(1, 2)))

scala> a.addBinding("Trousers", 3)
res2: a.type = Map((Trousers,Set(3)), (Pants,Set(1, 2)))

scala> a.mapValues { v => v.last }
res3: scala.collection.Map[String,Any] = Map((Trousers,3), (Pants,2))

scala> val valueIds = a.values.flatten
valueIds: Iterable[Any] = List(3, 1, 2)

あなたが望むものに関してあなたのコードを見るとき、それは理にかなっていると思います。

于 2011-03-30T15:20:14.327 に答える
1

プレースホルダー_を型パラメーターとして使用すると、これは実存型に変換されます。つまり、定義ListBuffer[_]はになりListBuffer[A] forSome { type A }ます。これは、コンパイラがその型について何も知らず、それについて何Aも仮定できないことを意味します。

最も簡単な修正は、を使用しListBuffer[Any]てマップを次のようにラップすることです。

val m = new HashMap[String,ListBuffer[Any]]

def get(key: String) =
  m.getOrElseUpdate(key, new ListBuffer())

get("Strings") += "a"
get("Strings") += "b" += "c"
get("Ints") += 1 += 2
// m is now:
// Map(Ints -> ListBuffer(1, 2), Strings -> ListBuffer(a, b, c))
于 2011-03-30T15:24:34.977 に答える
0

もう1つの方法を提案させてください:

import scala.collection.mutable

val map = mutable.Map.empty[String, Vector[Any]].withDefaultValue(Vector.empty)
map("strings") :+= "one"
map("ints") :+= 1
map("ints") ++= Seq(1, 2)

assert {
  map("default") == Vector.empty && // no side effects here
  !map.contains("default")
}
于 2012-03-26T13:52:56.050 に答える