5种Scala益智游戏会让您的大脑受伤

在Scala中寻找异常和错误的非直观案例

在这篇文章中,我们与Nermin SerifovicAndrew Phillips取得了联系 ,您可能会从Scala Puzzlers中了解到。 我们在一起选择了一些问题来探索Scala中的错误和异常。 有些问题乍一看似乎很怪异,但是当您深入挖掘背后的实际情况时,这一切都是有道理的。 我们希望这篇文章有助于您更好地了解Scala的来龙去脉,尽管如此,重要的是,您会发现它很有趣。 让我们深入。

答案在帖子的底部。 但是,嘿,别偷看!

1.异常失败

执行以下代码的结果是什么?

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}

val f = Future { throw new Error("fatal!") } recoverWith {
    case err: Error => Future.successful("Ignoring error: " + err.getMessage)
}

f onComplete {
    case Success(res) => println("Yay: " + res)
    case Failure(e) => println("Oops: " + e.getMessage)
}

答案

  1. 印刷品:
    Yay:忽略错误:致命!
  2. 引发错误
  3. 印刷品:
    糟糕!致命!
  4. 印刷品:
    糟糕:盒装错误

2. $!。*%迭代器!

执行以下代码的结果是什么?

val t = "this is a test"
val rx = " ".r
val m = rx.findAllIn(t)
println(m)
println(m.end)
println(rx.findAllIn(t).end)

答案

  1. 非空迭代器
    5
    5
  2. 空迭代器
    java.lang.IllegalStateException:无匹配项
    java.lang.IllegalStateException:无匹配项
  3. 非空迭代器
    5
    java.lang.IllegalStateException:无匹配项
  4. 非空迭代器
    java.lang.IllegalStateException:无匹配项
    java.lang.IllegalStateException:无匹配项

3.名字叫什么?

执行以下代码的结果是什么?

class C {
    def sum(x: Int = 1, y: Int = 2): Int = x + y
}
class D extends C {
    override def sum(y: Int = 3, x: Int = 4): Int = super.sum(x, y)
}
val d: D = new D
val c: C = d
c.sum(x = 0)
d.sum(x = 0)

答案

  1. 2
    3
  2. 1个
    3
  3. 4
    3
  4. 3
    3

4.(Ex)流惊喜

执行以下代码的结果是什么?

val nats: Stream[Int] = 1 #:: (nats map { _ + 1 })
val odds: Stream[Int] = 1 #:: (odds map { _ + 1 } filter { _ % 2 != 0 })

nats filter { _ % 2 != 0 } take 2 foreach println
odds take 2 foreach println

答案

  1. 印刷品:
    1个
    3 1个 3
  2. 印刷品:
    1个
    2 1个 3
  3. 第一条语句打印:
    1个
    3,第二个引发运行时异常
  4. 第一条语句引发运行时异常,第二条语句显示:
    1个
    3

5.弦的情况

执行以下代码的结果是什么?

def objFromJava: Object = "string"
def stringFromJava: String = null

def printLengthIfString(a: AnyRef) {
    a match {
        case s: String => println("String of length " + s.length)
        case _ => println("Not a string")
    }
}

printLengthIfString(objFromJava)
printLengthIfString(stringFromJava)

答案

  1. 印刷品:
    不是字符串
    长度为0的字符串
  2. 第一版画:
    不是字符串,第二个则抛出NullPointerException
  3. 印刷品:
    长度为6的字符串
    不是字符串
  4. 第一版画:
    长度为6的字符串,第二个字符串引发NullPointerException

解决方案

1.异常失败

正确答案是: 4 =>此代码段将显示“糟糕:装箱错误”。

val f = Future { throw new Error("fatal!") } recoverWith {
    case err: Error => Future.successful("Ignoring error: " + err.getMessage)
}

f onComplete {
    case Success(res) => println("Yay: " + res)
    case Failure(e) => println("Oops: " + e.getMessage)
}

那我们这里有什么?

有一个Future消息“致命!”抛出错误,您可能天真地认为它可以恢复并以成功状态结束。 事实并非如此。 这里抛出的是一个异常而不是一个错误,因此我们不在recoverWith块中输入err大小写。

既然我们知道未来会失败,那么还有另一个警告。 我们打印出的消息不是我们抛出的错误消息。 抛出的异常是一个java.util.concurrent.ExecutionException,当从一个失败的Future创建它时,它带有一个“ Boxed Error”消息。

因此,未来的结果是:
失败(新的ExecutionException(“装箱的错误”,新的错误(“致命!”))))

2. $!。*%迭代器!

正确答案是: 3 =>

非空迭代器
5
java.lang.IllegalStateException:无匹配项

val t = "this is a test"
val rx = " ".r
val m = rx.findAllIn(t)
println(m)
println(m.end)
println(rx.findAllIn(t).end)

那我们这里有什么?

此代码段创建一个正则表达式,并在提供的字符串中查找其所有匹配项(空格字符)。 很有道理,但是findAllIn返回一个Iterator。 当打印时,它的toString给我们“空迭代器”或“非空迭代器”。 由于我们对该字符串有3次匹配,因此它不是空的。

向前,end返回当前命中结束后字符的索引。 因此,它是第一个空格之后的索引,即5。

现在出现了令人惊讶的部分。 最后,我们得到一个IllegalStateException。 确实$!。*%迭代器! 当涉及到业务时,除非您要求或检查第一个元素,否则不会激活正则表达式。 对于第一个println,我们要求使用toString,并且作为“副作用”,我们激活了正则表达式,因此第二个println没有问题。 对于第三个println,我们被悬吊起来并被异常击中。

3.名字叫什么?

正确答案是: 3 =>打印4、3

class C {
    def sum(x: Int = 1, y: Int = 2): Int = x + y
}
class D extends C {
    override def sum(y: Int = 3, x: Int = 4): Int = super.sum(x, y)
}
val d: D = new D
val c: C = d
c.sum(x = 0)
d.sum(x = 0)

那我们这里有什么?

有两个问题:

  • 参数名称的绑定由编译器完成,并且编译器可以使用的唯一信息是变量的静态类型。
  • 对于具有默认值的参数,编译器将创建计算默认参数表达式的方法。 在上面的示例中,类C和D都包含两个默认参数的方法sum $ default $ 1和sum $ default $ 2。 当缺少参数时,编译器将使用这些方法的结果,并在运行时调用这些方法。

从c.sum(x = 0)开始,我们使用缺少y参数的C类和。 这是第二个参数。 幕后发生的事情,因为c是从d创建的,它带有为d类中缺少的参数创建的默认方法。 当选择使用哪种方法时,无论x和y的名称相反,它都会执行此操作。 第二个参数丢失,因此假定它是4,而第一个参数是0。第一个结果是0 + 4 = 4。 哇,我的大脑很痛。

对于d.sum(x = 0),情况有些不同,我们直接求D的和,而第一个y参数丢失。 我们假设它是3,结果是0 + 3。

Josh Suereth用以下规则总结了这一点:“名称是静态的; 值就是运行时间”。

4.(Ex)流惊喜

正确答案是: 3 =>第一条语句输出:1 3,第二条语句抛出运行时异常

val nats: Stream[Int] = 1 #:: (nats map { _ + 1 })
val odds: Stream[Int] = 1 #:: (odds map { _ + 1 } filter { _ % 2 != 0 })

nats filter { _ % 2 != 0 } take 2 foreach println
odds take 2 foreach println

那我们这里有什么?

此代码段创建2个流,小数和赔率。 第一个包含(1 ,?)并保存向其添加更多项的规则,简单+1递增。 同样,第二个规则与过滤器具有相同的规则,该过滤器仅允许保留奇数。 当我们尝试打印时,麻烦就来了。

对于nat,我们展开时没有问题,我们只取前2个奇数值,因为在延迟创建流之后应用了过滤器:(1,(2,(3,?)))

赔率是,我们真的不能拿2。只是第一个。 我们进入无尽的递归状态。 由于2不是奇数,并且在使用+1规则创建Stream时立即激活了过滤器,所以我们永远不会达到3并遇到运行时异常。

5.弦的情况

正确答案是: 3 =>打印:长度为6的字符串,不是字符串

def objFromJava: Object = "string"
def stringFromJava: String = null

def printLengthIfString(a: AnyRef) {
    a match {
        case s: String => println("String of length " + s.length)
        case _ => println("Not a string")
    }
}

printLengthIfString(objFromJava)
printLengthIfString(stringFromJava)

那我们这里有什么?

对于objFromJava,即使它最初是作为对象创建的,我们也要处理它。 由于模式匹配分辨率基于运行时类型,因此将打印出字符串“ string”的长度。

对于stringFromJava(为null),我们得到的是“非字符串”。 由于null是完全不同的问题,因此我们需要明确检查它是否是其中一种情况,并给予一些特殊的照顾。

资料来源

  1. 异常失败
  2. $!。*%迭代器!
  3. 名字叫什么?
  4. (Ex)流惊喜
  5. 一串弦

访问Scala Puzzlers以查看完整列表。

最后的想法

再次感谢Nermin和Andrew与我们分享他们的益智游戏! 我们希望您喜欢阅读它们并尝试解决它们。 在撰写本文时,我们当然已经学到了一些新知识,希望您也是如此。 尽管如果您曾经在自己的代码中遇到过这样的难题,那可能就没那么有趣了。 对于这种情况,我们为Scala构建了Takipi 。 Takipi是一个本地代理,它知道如何跟踪未捕获的异常,捕获的异常以及在生产中的服务器上记录错误。 它使您可以查看导致错误的变量值(遍及整个堆栈),并将其覆盖在代码中。

翻译自: https://www.javacodegeeks.com/2015/11/5-scala-puzzlers-will-make-brain-hurt.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值