【问题标题】:Scala PartialFunction defined with case syntax always returns true on isDefinedAt使用 case 语法定义的 Scala PartialFunction 总是在 isDefinedAt 上返回 true
【发布时间】:2013-01-26 13:07:29
【问题描述】:

我只是在研究 scala,因为大多数人似乎都喜欢它。所以我想我会写 FizzBu​​zz(因为它比 HelloWorld 有趣得多),但我有点卡住了,因为无论我在其中添加什么,如果我使用匹配/大小写语法定义我的 PartialFunctions 他们的 isDefinedAt方法似乎总是返回 true。谁能向我解释我缺少什么(或者这是一个错误?)。我正在使用 Scala 2.10.0 版本。

代码:

object Main extends App {
  def applyAndJoin[A, B](f: PartialFunction[A, B],
    g: PartialFunction[A, B])(h: (B, B) => B): PartialFunction[A, B] = {
    PartialFunction[A, B] {
      case a if (f isDefinedAt a) && (g isDefinedAt a) => h(f(a), g(a))
    } orElse f orElse g
  }

  val fizz = PartialFunction[Int, String] { case a if a % 3 == 0 => "Fizz" }
  val buzz = PartialFunction[Int, String] { case a if a % 5 == 0 => "Buzz" }
  val makeString = PartialFunction[Int, String] { case a => a.toString }
  val fizzBuzz = applyAndJoin(fizz, buzz)((a, b) => a + b) orElse makeString
  1 to 100 collect fizzBuzz foreach println //this fails because scala thinks
                                            //that fizz is defined at 1
}

堆栈跟踪:

Exception in thread "main" scala.MatchError: 1 (of class java.lang.Integer)
    at Main$$anonfun$1.apply(Main.scala:9)
    at Main$$anonfun$1.apply(Main.scala:9)
    at scala.PartialFunction$$anonfun$apply$1.applyOrElse(PartialFunction.scala:242)
    at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:33)
    at Main$$anonfun$applyAndJoin$1.apply(Main.scala:5)
    at scala.PartialFunction$$anonfun$apply$1.applyOrElse(PartialFunction.scala:242)
    at scala.PartialFunction$OrElse.apply(PartialFunction.scala:162)
    at scala.collection.TraversableLike$$anonfun$collect$1.apply(TraversableLike.scala:278)
    at scala.collection.immutable.Range.foreach(Range.scala:142)
    at scala.collection.TraversableLike$class.collect(TraversableLike.scala:278)
    at scala.collection.AbstractTraversable.collect(Traversable.scala:105)
    at Main$delayedInit$body.apply(Main.scala:13)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:71)
    at scala.App$$anonfun$main$1.apply(App.scala:71)
    at scala.collection.immutable.List.foreach(List.scala:309)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32)
    at scala.App$class.main(App.scala:71)
    at Main$.main(Main.scala:1)
    at Main.main(Main.scala)

【问题讨论】:

  • ... 以及 java.lang.Integer 的来源非常有趣。代码中的 java Integer 没有明显的原因。
  • @cybye java.lang.Integer 来自于未能将范围 (1) 的第一个元素与 case a if a % 3 == 0 ... 匹配。

标签: scala


【解决方案1】:

你的 PartialFunction 定义没有按照你的想法做,你应该写

val fizz: PartialFunction[Int, String] = { case a if a % 3 == 0 => "Fizz" }
val buzz: PartialFunction[Int, String] = { case a if a % 5 == 0 => "Buzz" }
val makeString: PartialFunction[Int, String] = { case a => a.toString }

这样你定义了一个PartialFunction[Int, String]类型的变量,它的值是匿名定义{ case a if ...}


相反,您正在定义一个变量

val fizz = ...

使用对 PartialFunction 对象 apply() 方法的调用,在 scala 2.10 中定义为

def apply[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) }

在这种情况下,结果函数 fizz 是为每个 x 定义的,但是当调用“包装的”f(x) 时,会出现错误。

【讨论】:

  • 事实上,您所需要的要简单得多,只要您已经拥有:val fizzBuzz = fizz orElse buzz orElse makeString。在这方面,scala 确实可以非常有表现力和简洁......现在为“FizzBu​​zz”
  • 但这会让我得到“Fizz”或“Buzz”或“”,但我真的想要一个组合器来连接此时定义的所有函数的结果。
  • 我建议让applyAndJoinapplyAndJoin(...) = new PartialFunction[A, B] { def isDefinedAt(x: A): Boolean = ... def apply(x: A): B = ... 的心情返回PartialFunction[A, B] 的新子类
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-04
  • 1970-01-01
  • 1970-01-01
  • 2015-12-16
  • 2016-05-10
  • 1970-01-01
相关资源
最近更新 更多