Scala似乎定义了3种断言:assert
,require
和assume
。
据我所知,require
的区别(与通用断言相比)是它专门用于检查输入(参数,传入消息等)。那么assume
的含义是什么?
答案 0 :(得分:48)
如果你看一下Predef.scala
中的代码,你会发现这三个人的工作非常相似:
def assert(assertion: Boolean) {
if (!assertion)
throw new java.lang.AssertionError("assertion failed")
}
def assume(assumption: Boolean) {
if (!assumption)
throw new java.lang.AssertionError("assumption failed")
}
def require(requirement: Boolean) {
if (!requirement)
throw new IllegalArgumentException("requirement failed")
}
还有一些版本需要额外的参数用于报告目的(参见http://harrah.github.com/browse/samples/library/scala/Predef.scala.html)。
区别在于它们抛出的异常类型以及它们生成的错误消息。
然而,静态跳棋可以不同地对待这三种。意图是assert
指定静态检查应该尝试证明的条件,assume
将用于检查者可能认为要保留的条件,而require
指定呼叫者必须确保的条件。如果静态检查程序发现违反assert
,则认为它是代码中的错误,而当违反require
时,它会假定调用方有错。
答案 1 :(得分:19)
assert()和assume()之间的区别在于
assert()的目标消费者/上下文正在测试,例如Scala JUnit测试,而assume()的消费者/上下文是“作为函数前后条件的合同样式规范的一种手段” ,意图是静态分析工具可以使用这些规范“(摘自scaladoc)。
在静态分析的上下文中,正如Adam Zalcman指出的那样,assert()是一个全执行路径断言,用于检查全局不变量,而assume()在本地工作以减少检查的数量。分析仪需要做。假设()用于假设保证推理的上下文,这是一种分而治之的机制,可以帮助模型检查员假设某些方法,以便解决当试图检查程序的所有路径时出现的状态爆炸问题可能需要。例如,如果你知道在你的程序设计中,函数f1(n1:Int,n2:Int)永远不会通过n2< n1,然后明确说明这个假设将有助于分析仪不必检查很多n1和n2的组合。
在实践中,由于这样的整个程序模型检查器仍然主要是理论,让我们看一下scala编译器和解释器的作用:
关于这个主题的优秀scaladoc的更多内容:
<强>断言强>
提供了一组assert
函数,用于记录和动态检查代码中的不变量。通过向-Xdisable-assertions
命令提供命令行参数scala
,可以在运行时省略断言语句。
还提供了旨在用于静态分析工具的assert
变体:assume
,require
和ensuring
。 require
和确保旨在用作函数前后条件的合同样式规范的一种手段,意图是静态分析工具可以使用这些规范。例如,
def addNaturals(nats: List[Int]): Int = {
require(nats forall (_ >= 0), "List contains negative numbers")
nats.foldLeft(0)(_ + _)
} ensuring(_ >= 0)
addNaturals的声明声明传递的整数列表应该只包含自然数(即非负数),并且返回的结果也是自然的。 require与assert的区别在于,如果条件失败,则函数的调用者应该责备而不是在addNaturals本身内进行逻辑错误。确保是一种断言形式,声明函数提供的关于它的返回值的保证。 )
答案 2 :(得分:5)
我是亚当斯的第二个回答,这里只是一些小补充:
当违反assume
时,验证工具会默默地修剪路径,即不会更深入地跟踪路径。
因此,assume
通常用于制定前提条件,assert
以制定后置条件。
许多工具都使用这些概念,例如:模仿测试工具KLEE,软件限制模型检查工具,如CBMC和LLBMC,部分也是基于抽象解释的静态代码分析工具。文章Finding common ground: Choose, Assert, Assume介绍了这些概念并试图将它们标准化。