0

我有一个 Scala 中各种类型的值列表——主要是字符串、整数、双精度值。其中一些值可能为空。我们可以认为这是一个List[Option[AnyRef]]. 对于此列表中的每个值,如果存在,我想生成一个包含括号之间值的字符串;如果它不存在,则只是一个空字符串。此外,在值插入括号之间之前,可能需要对其应用转换。例如,如果值为 an Option[String],我可能想应用该trim函数。

我通过将数据结构视为可选值的映射与对该值进行操作并返回字符串的可选函数配对来解决此问题(我不知道如何在 Scala 中指定它,因为我认为 Map[Option [A], Option[(A) => String]] 是不正确的,因为它暗示每个选项键都是相同的类型)。到目前为止,这是我使用示例数据和函数编写的内容:

lazy val messageContext = Map(
  None -> None,
  Some("hello") -> None,
  Some(4) -> Some(negate _),
  None -> None
) map { t => wrapText(t._1, t._2)  } toList

private def wrapText[A](option: Option[A], transFn: Option[(A) => String]): String = option match {
  case Some(o) => " (%s)".format(transFn.getOrElse(stringIdentity _)(o))
  case _ => ""
}

private def stringIdentity[A](a: A) = a.toString

private def negate(n: Number) = n * -1

但是,我遇到了编译器错误:

error: type mismatch;
 found   : Option[java.lang.Number => java.lang.String]
 required: Option[AnyVal{def getClass(): java.lang.Class[_ >: Double with Int <: AnyVal]} => String]
  ) map { t => wrapText(t._1, t._2)  } toList

我的方法接近吗?使用 Scala 可以轻松实现这一点,还是我必须求助于一些 scalaz 魔法才能使其真正起作用?

谢谢。

编辑:上述输入的正确输出是这样的: List[""," (hello)"," (-4)",""]

4

2 回答 2

2

不需要魔法。我不确定这是否是您想要的,但要重申

  • None在输入中意味着 ""在输出中
  • Some(v)在输入中意味着" (%s)".format(trans(v))在输出中,其中trans是值的一些可选转换

前任:

val input = List(None, Some("hello"), Some(4), None)

def trans(v: Any) = v match {
   case num @ 4 => -num
   case _       => v  // identity
}

val output = input.map {
   case Some(v) => " (%s)".format(trans(v))
   case _       => ""
}
于 2013-01-18T20:19:04.253 回答
0

这里的第一个问题是 aMap[A,B]有两个用于整个映射的类型参数,而不是每个 的几个类型参数tuple。你想做的事情不能使用 a 来完成Map,因为它Map会将键和值的最常见祖先作为类型参数:

scala>  import StackOverflowTest

StackOverflowTest.messageContext
import StackOverflowTest

scala> res0: scala.collection.immutable.Map[Option[java.lang.Comparable[_ >: java.lang.Integer with java.lang.String <: java.lang.Comparable[_ >: java.lang.Integer with java.lang.String <: java.lang.Comparable[_ >: java.lang.Integer with java.lang.String <: java.io.Serializable] with java.io.Serializable] with java.io.Serializable] with java.io.Serializable],Option[java.lang.Integer => Int]] = Map(None -> None, Some(hello) -> None, Some(4) -> Some(<function1>))

scala> 

当您将不同类型的元组放入映射或序列中时,您会丢失有关每个元组的类型信息,并且只保留第一个公共父级。如果您需要一个能够保存元组映射的数据结构,其中每个元组Tuple2[A,B]都不同A,B,您需要一个更动态的结构,例如无形记录或 HList。

https://github.com/milessabin/shapeless

这里的第二个问题是Java中没有定义Number * 1的语义,您需要使用所需的数字子类型,例如java.lang.Integer。

第三个问题是您的 wrapText 需要一段transFn: Option[(A) => String])时间,您的否定输出值类型不是字符串(实际上没有为 java 类 Number 定义 *)。Map如果您尝试仅编译以下内容,请移除该部分并更正否定方法:

  private def wrapText[A](option: Option[A], transFn: Option[(A) => String]): String = option match {
    case Some(o) => " (%s)".format(transFn.getOrElse(stringIdentity _)(o))
    case _ => ""
  }

  private def stringIdentity[A](a: A) = a.toString

  private def negate(n: java.lang.Integer) = n * -1

  wrapText(Some(4),negate _ )

这不会编译错误:

error: type mismatch;
found   : Option[java.lang.Integer => Int]
required: Option[? => String]
wrapText(Some(4),Some(negate _) )

如果你最终纠正否定返回一个字符串,你将撞到最后一堵墙

error: type mismatch;
found   : Option[java.lang.Integer => java.lang.String]
required: Option[Int => String]
wrapText(Some(4),Option(negate _) )

这里的问题是,虽然 Scala 和 Java 执行自动装箱和拆箱,但它们不执行类型参数的自动转换: Option[Int=> String] 与 Option[java.lang.Integer=>String] 不是同一个类,所以你必须强制第一个选项的类型才能正确编译:

  wrapText(Some(4:java.lang.Integer),Some(negate _) )
于 2013-01-18T20:26:59.123 回答