在scala中要求和断言之间做什么选择

时间:2014-10-01 11:52:11

标签: scala

requireassert都用于在运行时执行某些检查以验证某些条件。

那么它们之间的基本区别是什么?

我看到的唯一一个是require抛出IllegalArgumentExceptionassert抛出AssertionError

如何选择使用哪一个?

6 个答案:

答案 0 :(得分:68)

Kigyo所述,存在语义差异

  • 断言意味着您的程序已达到不一致状态,这可能是当前方法/函数的问题(我喜欢将其视为HTTP 500 InternalServerError)
  • require意味着该方法的调用者出错并且应该修复它的调用(我喜欢将它想象为HTTP 400 BadRequest)

还有一个主要的技术差异:

assert注明@elidable(ASSERTION) 这意味着您可以使用-Xelide-below ASSERTION-Xdisable-assertions编译程序,编译器不会生成断言的字节码。如果您有大量断言,这可以显着减少字节码大小并提高性能。

了解这一点,您可以使用assert验证程序中所有不变量无处不在(每个方法/函数的所有前置条件/​​后置条件)电话)并且不支付生产价格。

您通常会进行" 测试"在启用所有断言的情况下进行构建,它会慢一些,因为它会一直验证所有断言,然后你就可以拥有" 生产"在没有断言的情况下构建您的产品,您将消除通过断言完成的所有内部状态检查

require不是elidable,在库(包括内部库)中使用更有意义来通知调用者调用给定方法/函数的前提条件。

答案 1 :(得分:20)

这只是我的主观观点。

每当我想要参数约束时,我都会使用require

作为一个例子,我们可以采用自然数的阶乘。由于我们不想解决负数,我们想要抛出IllegalArgumentException

我会使用assert,只要你想确保某些条件(如不变量)在执行期间始终为真。我认为这是一种测试方式。

以下是使用requireassert

的阶乘的示例实现
def fac(i: Int) = {
  require(i >= 0, "i must be non negative") //this is for correct input

  @tailrec def loop(k: Int, result: Long = 1): Long = {
    assert(result == 1 || result >= k)   //this is only for verification

    if(k > 0) loop(k - 1, result * k) else result
  }

  loop(i)
}

result > 1为真时,循环至少执行一次。因此结果必须大于或等于k。这将是一个循环不变量。

如果您确定自己的代码是正确的,可以删除assert,但require会保留。

答案 2 :(得分:2)

用非常简单的语言:

Require用于对函数的调用者或某个类的对象的创建者强制执行前提条件。然而,assert用于检查函数本身的代码。 因此,如果先决条件失败,那么您将获得illegal argument exception。然而,如果断言失败并且不是调用者的错,那么你得到assertion error

答案 3 :(得分:1)

Scaladocs/javadocs 也很不错:

  • 断言()

package utils import ( "text/template" ) type Context map[string]interface{} type FString struct { Data string template *template.Template } func (fs *FString) MustCompile(expr string, funcMap template.FuncMap) { fs.template = template.Must(template.New("f-string"). Funcs(funcMap). Parse(expr)) } func (fs *FString) Write(b []byte) (n int, err error) { fs.Data += string(b) return len(b), nil } func (fs *FString) Render(context map[string]interface{}) error { if err := fs.template.Execute(fs, context); err != nil { return err } return nil } func (fs *FString) MustRender(context Context) { if err := fs.Render(context); err != nil { panic(err) } } func (fs *FString) Clear() string { // return the data and clear it out := fs.Data fs.Data = "" return out }

  • 需要()

Tests an expression, throwing an AssertionError if false. Calls to this method will not be generated if -Xelide-below is greater than ASSERTION.

答案 4 :(得分:0)

需求,确保和不变性是按合同设计(CBD)开发过程中的概念。

require检查调用者使用该例程应满足的前提条件。

确保检查返回值的正确性(并仅验证所需的更改已发生,仅此而已)

不变性在所有关键时刻检查类的有效性。

CBD是一种用于构建正确/健壮软件的开发方法。有关CBD Google的更多详细信息,您应该点击Eiffel Software的链接。希望这会有所帮助。

答案 5 :(得分:0)

您可以在here中查看有关Scala语言的详细讨论。

我可以补充一点,区分requireassert的关键是理解这两个。这两个都是软件质量工具,但是来自不同范式的不同工具箱。概括而言,assert是采用纠正方法的软件测试工具,而require是采用预防方法的按合同设计工具。

requireassert都是控制状态有效性的手段。从历史上看,有两种截然不同的范例来处理无效状态。第一个是主流,统称为软件测试学科方法和工具。另一个称为按合同设计。这是两个无法比较的范式。

软件测试可确保代码具有足够的通用性,能够执行容易出错的操作,并且不会被滥用。 按合同设计控制代码具有这种功能。换句话说,软件测试是纠正性的,而按合同设计是预防性的。

  • assert用于编写单元测试,即,如果某个方法通过所有由assert表达式编写的测试,则该代码将被视为无错误。因此,assert除了运营代码外,还是一个独立的机构。
  • require嵌入在代码中,并且部分代码可确保不会发生任何有害的事情。