7.2简单契约和函数

2015-12-28  本文已影响75人  jarod_chan

数学函数有一个领域和范围。领域代表能够接受的参数,范围代表产生的值。传统表示方法如下

f:A->B

程序语言的函数也有领域和范围,契约能保证函数值接受领域的值和只产生范围的值。->创建一个函数契约。

  #lang racket
  (provide (contract-out
                [deposit (-> number? any)]
                [balance (-> number?)]))
  (define amout 0)
  (define (deposit a) (set! amount (+ amount a)))
  (define (balance) amout)

这个模块导出了两个函数

7.2.1->样式

如果你使用数学函数,你可能更愿意让契约箭头出现在领域和范围之间,而不是在开头。事实上,你确实可以这么做

  (provide (contract-out
                 [deposit (number? . -> . any)]))

在racket里面一个s表达式如果含有两个点的符号在中间,读取器则会重排s表达式让符号在前面。所以它等价于

  (-> number? any)

7.2.2使用 define/contract 和 ->

define/contract形式也能使用契约来定义。

  (define/contract (deposit amount)
    (->number? any)
    ; implementation goes here
    ...)

这种使用方式有两个影响
1 因为调用的时候总是检查契约,即使在模块内部。所以,它会造成性能下降。特别是在循环和递归的时候。
2 在某些情况下,同模块调用时,可能有一些更加宽松的输入限制。在这种情况下,define/contract建立的限制太严格了。
7.2.3any和any/c
any契约匹配任何一种结果,它只能使用在范围的位置。除此之外,我们能使用void?契约,表示函数会返回(void)值。void?契约会被契约检测系统检查,当函数返回值的时候。相反的,any契约告诉契约检测系统不用检查返回值,它告诉客户端模块服务器不会对函数返回值作出任何承诺,可以单个值也可以多值。
any/c和any类似,但是,any/c代表一个单值。

  (define (f x) (values (+ x 1) (- x 1)))

上面这个函数匹配(-> integer? any),但是不匹配(-> integer? any/c)。当承诺返回单个结果很重要的时候使用any/c,当竟可能少的承诺返回结果时使用any。

7.2.4定制自己的契约

  #lang racket
   (define (amount? a)
    (and (number? a) (integer? a) (exact? a) (>= a 0)))

    (provide (contract-out
      ; an amount is a natural number of cents
      ; is the given number an amount?
      [deposit (-> amount? any)]
      [amount? (-> any/c boolean?)]
      [balance (-> amount?)]))

      (define amount 0)
      (define (deposit a) (set! amount (+ amount a)))
      (define (balance) amount)

上面这个模块自定义了amount?函数作为契约。如果客户端无法理解amount?,那就没有任何意义。所以模块也导出了amount?谓词本身。我们可使用natural-number/c替代amount?,因为它们的约束恰好相同。

  (provide (contract-out
                [depsoit (-> natural-number/c any)]
                [balance (-> natural-number/c)]))

每个接受一个参数的函数都能被当做谓词在契约里使用。所以契约组合器变的很有用。比如and/c和or/c.
(define amount/c
(and/c number? integer? exact? (or/c positive? zero?)))
(provide (contract-out
[deposit (-> amount/c any)]
[balance (-> amount/c)]))

7.2.5 高阶函数契约

函数契约不是只能使用简单的谓词在他们的领域和范围。任何这里讨论的组合器,包括函数契约本身,都能被当做契约来使用在函数的参数和结果上。

  (->integer? (-> integer? integer?))

它表示接受一个参数并返回一个函数,这个函数接受一个参数并最终返回一个结果。
类似的还有

  (-> (->ineger? integer?) integer?)

7.2.6 契约消息tempN

如果在契约里传入一个lambda表达式(匿名函数),则调用的时候错误信息会显示tempN,如果想改进这种错误提示,可以先申明一个有函数名的谓词判断函数,那么提示就会变得清晰。

7.2.7解释契约错误信息

一般来说,契约的错误信息包含下面留个方面

上一篇 下一篇

猜你喜欢

热点阅读