美文网首页
7.2简单契约和函数

7.2简单契约和函数

作者: jarod_chan | 来源:发表于2015-12-28 20:29 被阅读75次

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

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)

这个模块导出了两个函数

  • deposit 接受一个数字返回某个值
  • balance 返回一个数字
    但一个模块导出一个函数,它建立了两个交流通道。它自己作为服务器,导入这个函数的模块则作为客户端。如果客户端调用一个函数,它发送一个值到服务器。相反的,如果这个函数调用结束函数返回一个值,服务器发送一个值给客户端。客户端和服务器的区别很重要,因为当出现错误,会把错误归咎到某个部分。
    ->本身并不是契约,它是一个契约联接符。

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解释契约错误信息

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

  • 一个和约束相关的方法或函数,并有短语contract violation或者broken its contract。这个短语取决于客户端还是服务器违反了契约。
    deposit: contract violation
  • 一个描述希望值和实际值
    expected: amount
    given:-10
  • 完整的契约加上那个被违反了
    in: the ist argument of
    (-> amount any)
  • 契约所属的模块
    contract from: improved-bank-server
  • 归咎于谁
    blaming:top-level
    (assuming the contract is correct)
  • 源代码出现的位置
    at: eval:5.0

相关文章

  • 7.2简单契约和函数

    数学函数有一个领域和范围。领域代表能够接受的参数,范围代表产生的值。传统表示方法如下 f:A->B 程序语言的函数...

  • 7、mysql常用函数

    7、mysql常用函数 7.1、IF函数 7.2、IFNULL函数 7.3、NULLIF 函数 7.4、CASE ...

  • 干货时间:聊聊DevOps下的技术系列之契约测试

    摘要:本期和大家简单聊聊在服务交互场景下使用服务契约的重要性,以及契约管理的必要性,最后简单介绍了下契约测试。 1...

  • Elasticsearch源码分析-主分片写入流程

    版本-7.2 注册主分片处理函数 TransportReplicationAction.handlePrimary...

  • 第 7 章 使用函数

    第 7 章 使用函数 7.2 调用函数 调用内建的 abs() 函数 结果: 321 7.3 定义一个函数 声明并...

  • TDD 算不算契约式设计?

    关于题目中的契约式设计,推荐先阅读以下两篇故事汇: 简单聊聊契约式设计(上) 简单聊聊契约式设计(下) TDD不算...

  • 【第十六天】函数作为参数和返回值

    7.2 1.函数作为参数和返回值 在函数式编程中,函数是第一级对象,所谓‘第一级对象’即函数能像普通对象一样来使用...

  • vue.js 核心知识点七

    目录 - 7.1 vue项目中,定义全局函数 全局变量 - 7.2 Vue axios 给开发环境和生产环境配置不...

  • pytorch深度学习之常见优化算法

    7.4动量法 在7.2节(梯度下降和随机梯度下降)中我们提到,目标函数有关自变量的梯度代表了目标函数在自变量当前位...

  • 第六章 函数

    第七章 函数 7.1 定义函数 7.1.1 向函数传递信息 7.2 传递实参 可使用位置实参,这要求实参的...

网友评论

      本文标题:7.2简单契约和函数

      本文链接:https://www.haomeiwen.com/subject/oufvhttx.html