【问题标题】:isDefinedAt with orElse and andThen scala partial functionsisDefinedAt 与 orElse 和 andThen scala 部分函数
【发布时间】:2017-03-30 23:43:17
【问题描述】:

我试图了解 orElse 和 andThen 是如何工作的,但是当与 orElse 和 andThen 一起使用时,“isDefinedAt”的行为是出乎意料的。在我的实现中, isDefinedAt 返回 true 但函数崩溃。这使得实现不可靠。我该如何解决这个问题?

我写了以下 3 个偏函数

PF1

// a function which is defined for even values only. Returns the value
scala> val forEvenOnly:PartialFunction[Int,Int] = {case d if ( (d % 2) == 0) => d}
forEvenOnly: PartialFunction[Int,Int] = <function1>

PF2

// a function which is defined for odd values only. Returns the value
//wanted to try an alternate syntax
scala> class forOddOnly extends PartialFunction[Int,Int] {
     | def apply(x:Int) =  x
     | def isDefinedAt(x:Int) = (x % 2 !=0)
     | }
defined class forOddOnly

PF3

//a function which prints a value if it is less than 100
scala> val lessThan100:PartialFunction[Int,Unit] = {case d if d<100 =>println(d)}
lessThan100: PartialFunction[Int,Unit] = <function1>

就个人而言,它们似乎工作得很好(除了 OddOnly 应该会在偶数值时崩溃但不会崩溃)

scala> val forEvenOnly:PartialFunction[Int,Int] = {case d if ( (d % 2) == 0) => d}
forEvenOnly: PartialFunction[Int,Int] = <function1>

scala> forEvenOnly(1)
scala.MatchError: 1 (of class java.lang.Integer)

scala> forEvenOnly(2)
res60: Int = 2

scala> forEvenOnly.isDefinedAt(1)
res61: Boolean = false

scala> forEvenOnly.isDefinedAt(2)
res62: Boolean = true

问题 1 - 在以下代码中,函数 forOddOnly 的行为有所不同。它不会因为偶数而崩溃,但我想要它。我需要使用“案例”来获得这种行为吗?我预计问题可能出在 forOddOnly 的实现中,因为它适用于 apply 中的所有 Int

scala> class forOddOnly extends PartialFunction[Int,Int] {
     | def apply(x:Int) =  x //  I cannot change it to def apply(x:Int) =  if (x % 2 != 0 ) x. This will not compile
     | def isDefinedAt(x:Int) = (x % 2 !=0)
     | }
defined class forOddOnly

scala> (new forOddOnly).isDefinedAt(1)
res64: Boolean = true

scala> (new forOddOnly).isDefinedAt(2)
res65: Boolean = false

scala> (new forOddOnly)(1)
res66: Int = 1

这不会引发异常 scala> (new forOddOnly)(2) res67: Int = 2

打印小于 100 的值可以正常工作 scala> val lessThan100:PartialFunction[Int,Unit] = {case d if dprintln(d)} lessThan100: PartialFunction[Int,Unit] =

scala> lessThan100(1)
1

scala> lessThan100(100)
scala.MatchError: 100 (of class java.lang.Integer)

scala> lessThan100.isDefinedAt(1)
res86: Boolean = true

scala> lessThan100.isDefinedAt(100)
res87: Boolean = false

问题2 - 在下面的代码中,我使用带有 forEvenOnly 函数和小于 100 的“andThen”。对于大于 100 的偶数值,isDefinedAt 返回 true,但传递 100 或更大的值会使代码崩溃。这使得调用 isDefinedAt 变得不可靠。为什么我会得到这个以及如何解决它? isDefinedAt 按预期对所有奇数值都有效(并且代码也崩溃是奇数值被传递)。如果我更改 forOddOnly 的实现并使用“case”,它可以工作,但我不能使用 apply 获得相同的行为吗?

scala> val printEvenLessThan100 = forEvenOnly andThen lessThan100
printEvenLessThan100: PartialFunction[Int,Unit] = <function1>

scala> printEvenLessThan100.isDefinedAt(1)
res88: Boolean = false

scala> printEvenLessThan100(1)
scala.MatchError: 1 (of class java.lang.Integer)

scala> printEvenLessThan100.isDefinedAt(2)
res89: Boolean = true

scala> printEvenLessThan100(2)
2

问题代码。 isDefinedAt 为大于 100 的偶数返回 true,但代码崩溃 scala> printEvenLessThan100.isDefinedAt(102) res94: Boolean = true

scala> printEvenLessThan100(102)
scala.MatchError: 102 (of class java.lang.Integer)

scala> printEvenLessThan100.isDefinedAt(101)
res91: Boolean = false

scala> lessThan100(101)
scala.MatchError: 101 (of class java.lang.Integer)

问题 3 - 将 andThen 与 forOddOnly 一起使用会导致大于 99 的值崩溃,但不会导致偶数值崩溃。我想我在所有这些例子中都犯了一些常见的错误

scala> val printOddLessThan100 = (new forOddOnly) andThen lessThan100
printOddLessThan100: PartialFunction[Int,Unit] = <function1>

scala> printOddLessThan100(1)
1

//does not crash
scala> printOddLessThan100(2)
2

scala> printOddLessThan100.isDefinedAt(100)
res102: Boolean = false

scala> printOddLessThan100(100)
scala.MatchError: 100 (of class java.lang.Integer)

scala> printOddLessThan100.isDefinedAt(101)
res97: Boolean = true

scala> printOddLessThan100(101)
scala.MatchError: 101 (of class java.lang.Integer)

当我使用 orElse 和 andThen 组合所有函数时,也会发生同样的情况

scala> val printIntLessThan100 = forEvenOnly orElse (new forOddOnly) andThen lessThan100
printIntLessThan100: PartialFunction[Int,Unit] = <function1>

scala> printIntLessThan100(1)
1

scala> printIntLessThan100(2)
2

//why does this return true when the code actually crashes
scala> printIntLessThan100.isDefinedAt(101)
res83: Boolean = true

scala> printIntLessThan100(101)
scala.MatchError: 100 (of class java.lang.Integer)

【问题讨论】:

    标签: scala


    【解决方案1】:

    我认为这解决了问题 1 和 3。

    来自Standard Library scaladoc(已添加重点):

    调用者有责任在之前调用isDefinedAt 调用apply,因为如果isDefinedAt 为假,则不保证 apply 将抛出异常以指示错误情况。如果 没有抛出异常,评估可能会导致任意值。

    PartialFunction 和 scala.Function1 的主要区别是 PartialFunction 的用户可以选择做某事 与声明在其域之外的输入不同。

    因此,当使用未定义的传递参数调用 PF 时,不需要运行时异常。如果没有为该输入定义 PF,则根本没有定义结果。

    问题 2 的答案也在同一页面上,在 andThen() 的描述中:

    返回一个与此部分函数具有相同域的部分函数,​​它将参数 x 映射到 k(this(x))。

    如果您有val pfR = pfA andThen pfB,那么pfR 将与pfA 拥有相同的域。

    【讨论】:

    • “同域”是什么意思?
    • 部分函数的域是 isDefinedAt() 返回 true 的所有内容。换句话说,pfRpfA 具有相同的域,如果 pfA.isDefinedAt(x) == pfR.isDefinedAt(x) 对于 x 的所有值。
    • 好的。所以andThen 只有在我们知道输入是为函数定义的时候才应该被调用。例如pf1 orElse pf2 andThen something
    • @ManuChadha;我不确定您的意思,但让我们考虑一下您的示例:pf1 orElse pf2pf1 的域 (isDefinedAt) 之外的输入值将被馈送到 pf2。不能保证为所有这些值定义了pf2。它的域可能很小。结果是一个新的 PF,其域是 pf1 的域和 pf2 的域的组合。来自pf1pf2 的所有可能输出成为something 可能输入的范围。如果 something 是 PF,则无法保证其域 (isDefinedAt) 将匹配该输入范围。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-02
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    • 2012-12-27
    • 2020-10-22
    相关资源
    最近更新 更多