Карта Scala, содержащая значения типов миксов - PullRequest
5 голосов
/ 05 июля 2011

У меня есть функция, которая возвращает (Groovy код)

[words: "one two", row: 23, col: 45]

В scala я изменяю выше на scala Map, но затем я вынужден объявить это как

Map[String, Any]

но это имеет тот недостаток, что если я получу доступ к ключу, такому как карта ( «слова») Я должен добавить шаблон

map2("words").asInstanceOf[String]

Есть ли лучший способ в scala, который не требует от меня добавления asInstanceOf?

Ответы [ 4 ]

11 голосов
/ 05 июля 2011

Чтобы избежать приведения и использовать статическую типизацию, вы можете либо вернуть кортеж (String, Int, Int):

def getResult = ("one two", 23, 45)

val res = getResult
res._1 // the line

// alternatively use the extractor
val (line, row, _) = getResult // col is discarded
line // the line
row  // the row

, либо использовать класс регистра для результата:

case class MyResult(line: String, row: Int, col: Int)

def getResult = MyResult("one two", 23, 45)

val res = getResult
res.line // the line

// alternatively use the extractor provided by the case class
val MyResult(line, row, _) = getResult // col is discarded
line // the line
row  // the row

Я бы предпочел класс case, потому что поля названы, а на самом деле это всего на одну строку больше.

6 голосов
/ 06 июля 2011

Это тот случай, когда уроки по кейсам - ваш друг.Если ключи вашей карты - это классы дел, то вы можете заставить клиентский код корректно обрабатывать типы (и заставлять его корректно обрабатывать все типы).

        S:\>scala
    Welcome to Scala version 2.9.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_25).
    Type in expressions to have them evaluated.
    Type :help for more information.

    scala> sealed abstract class Thing;
    defined class Thing

    scala> case class Toaster(slices: Int) extends Thing;
    defined class Toaster

    scala> case class Bucket(contents: String) extends Thing;
    defined class Bucket

    scala> val things = Map("toasty" -> Toaster(2), "buck" -> Bucket("stuff"));
things: scala.collection.immutable.Map[java.lang.String,Product with Serializable with Thing] = Map(toasty -> Toaster(2), buck -> Bucket(stu
ff))

scala> for (x <- things) x match {
     case (k,Toaster(s)) => println(k + " " + s)
     case (k,Bucket(c)) => println(k + " " + c)
     }
toasty 2
buck stuff

Ключом здесь является то, что оператор matchвычеркивая различные случаи и предоставляя вам правильно напечатанные переменные, соответствующие полям внутри.Объявляя, что абстрактный класс запечатан, вы даете компилятору понять, что он имеет все доступные подклассы.С помощью этой информации он может сообщить вам, когда у вас пропущены дела, а также может провести дальнейшую оптимизацию.

0 голосов
/ 05 июля 2011

Наличие только двух возможных типов значений, как в вашем примере, позволит использовать тип Either с подтипами Left и Right:

val m = Map("words" -> Left("one two"), "rows"-> Right(23), "cols"-> Right(45))

Если вы вернете значение с карты, вы можете проверить, что у вас есть, например, с сопоставлением с шаблоном или использованием isLeft и isRight, и "разверните" его соответственно.

0 голосов
/ 05 июля 2011

Редактировать: Я понял, что вы получили эту карту как есть и нуждались в способе более четкого взаимодействия с ней. Разумеется, подход к классу случаев превосходит то, что я предлагаю ниже.


Если ваши ключи всегда отображаются на одни и те же типы значений, вы можете сделать что-то вроде этого:

class TypedKey[T] {
  def name = {
    // assumes you declare only `object` instances
    val simpleName = getClass.getSimpleName
    val moduleName = if (simpleName.endsWith("$")) simpleName.substring(0, simpleName.size - 1) else simpleName
    val lastDollar = moduleName.lastIndexOf('$')
    if (lastDollar == -1) moduleName else moduleName.substring(lastDollar + 1)
  }
}

object RubyKeys {
  object words extends TypedKey[String]
  object row extends TypedKey[Int]
  object col extends TypedKey[Int]
}

class MapWrapper(val underlying: Map[String, Any]) {
  def apply[T](key: TypedKey[T]) = underlying(key.name).asInstanceOf[T]
}

def main(args: Array[String]) {

  val map = Map("words" -> "one two", "row" -> 23, "col" -> 45)
  val wrapper = new MapWrapper(map)

  import RubyKeys._

  val w = wrapper(words) // String
  val r = wrapper(row)   // Int
  val c = wrapper(col)   // Int
  println(w, r, c)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...