Racket编程指南——4 表达式和定义(八)

51 篇文章 16 订阅

4.12 简单分派:case

通过将一个表达式的结果与子句的值相匹配,case表分派到一个从句:

(case expr
  [(datum ...+) body ...+]
  ...)

每个datum将使用equal?对比expr的结果,然后相应的body被求值。case表可以为N个datum在O(log N)时间内分派正确的从句。

可以给每个从句提供多个datum,而且如果任何一个datum匹配,那么相应的body被求值。

Example:

> (let ([v (random 6)])
    (printf "~a\n" v)
    (case v
      [(0) 'zero]
      [(1) 'one]
      [(2) 'two]
      [(3 4 5) 'many]))

3

'many

一个case表的最后从句可以使用else,就像cond那样:

Example:

> (case (random 6)
    [(0) 'zero]
    [(1) 'one]
    [(2) 'two]
    [else 'many])

'zero

对于更一般的模式匹配(但没有分派时间保证),使用match,这个会在《模式匹配》中介绍。

4.13 动态绑定:parameterize

在《Racket参考》中的“(parameterize)”部分也有关于parameterize的文档。

parameterize表把一个新值和body表达式的求值过程中的一个参数parameter相结合:

(parameterize ([parameter-expr value-expr] ...)
  body ...+)

术语“参数”有时用于指一个函数的参数,但Racket中的“参数”在这里有更具体的意义描述。

例如,error-print-width参数控制在错误消息中打印一个值的字符数:

> (parameterize ([error-print-width 5])
    (car (expt 10 1024)))

car: contract violation

  expected: pair?

  given: 10...

> (parameterize ([error-print-width 10])
    (car (expt 10 1024)))

car: contract violation

  expected: pair?

  given: 1000000...

一般来说,参数实现了一种动态绑定。make-parameter函数接受任何值并返回一个初始化为给定值的新参数。应用参数作为一个函数返回它的当前值:

> (define location (make-parameter "here"))
> (location)

"here"

在一个parameterize表里,每个parameter-expr必须产生一个参数。在body的求值过程中,每一个指定的参数给出对应于value-expr的结果。当控制离开parameterize表——无论是通过一个正常的返回、一个例外或其它逃逸——这个参数恢复到其先前的值:

> (parameterize ([location "there"])
    (location))

"there"

> (location)

"here"

> (parameterize ([location "in a house"])
    (list (location)
          (parameterize ([location "with a mouse"])
            (location))
          (location)))

'("in a house" "with a mouse" "in a house")

> (parameterize ([location "in a box"])
    (car (location)))

car: contract violation

  expected: pair?

  given: "in a box"

> (location)

"here"

parameterize表不是一个像let的绑定表;每次上边location的使用都直接指向原始的定义。在parameterize主体被求值的整个时间内,一个parameterize表调整一个参数的值,即使对于参数的使用是在parameterize主体以外符合文本:

> (define (would-you-could-you?)
    (and (not (equal? (location) "here"))
         (not (equal? (location) "there"))))
> (would-you-could-you?)

#f

> (parameterize ([location "on a bus"])
    (would-you-could-you?))

#t

如果一个参数的一个使用是在一个parameterize的主体内部符合文本,但是在parameterize表产生一个值之前没有被求值,那么这个使用没有明白这个被parameterize表所设置的值:

> (let ([get (parameterize ([location "with a fox"])
               (lambda () (location)))])
    (get))

"here"

一个参数的当前绑定可以通过用一个值作为一个函数调用这个参数来做必要的调整。如果一个parameterize已经调整了参数的值,那么直接应用参数过程仅仅影响与活动parameterize相关的值:

> (define (try-again! where)
    (location where))
> (location)

"here"

> (parameterize ([location "on a train"])
    (list (location)
          (begin (try-again! "in a boat")
                 (location))))

'("on a train" "in a boat")

> (location)

"here"

使用parameterize通常更适合于强制更新一个参数值——对于用let绑定一个新变量的大多数相同原因是更好地使用set! (参见《赋值:set!》)。

似乎变量和set!可以解决很多参数解决的相同问题。例如,lokation可以被定义为一个字符串,以及set!可以用来调整它的值:

> (define lokation "here")
> (define (would-ya-could-ya?)
    (and (not (equal? lokation "here"))
         (not (equal? lokation "there"))))
> (set! lokation "on a bus")
> (would-ya-could-ya?)

#t

然而,参数比set!提供了几个关键的优点:

  • parameterize表有助于在控制因一个异常导致的逃逸时自动重置一个参数的值。 添加异常处理器和其它表以重绕一个set!是比较繁琐的。

  • 参数可以和尾调用很好地工作(请参阅《尾递归》)。在一个parameterize表最后的body相对于parameterize表来说是处于尾部位置。

  • 参数与线程恰当地工作(请参阅《Racket参考》"(threads)"部分)。parameterize表仅因为在当前线程中的求值而调整一个参数的值,以避免与其它线程竞争。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值