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

78 篇文章 16 订阅

4.5 定义:define

一个基本定义具为如下表:

(define id expr)

在这种情况下,id被绑定到expr的结果。

Examples:

(define salutation (list-ref '("Hi" "Hello") (random 2)))
> salutation

"Hi"

4.5.1 函数简写

define表还支持函数定义的一个简写:

(define (id arg ...) body ...+)

这是以下内容的简写:

(define id (lambda (arg ...body ...+))

Examples:

(define (greet name)
  (string-append salutation ", " name))
> (greet "John")

"Hi, John"

(define (greet first [surname "Smith"] #:hi [hi salutation])
  (string-append hi ", " first " " surname))

> (greet "John")

"Hi, John Smith"

> (greet "John" #:hi "Hey")

"Hey, John Smith"

> (greet "John" "Doe")

"Hi, John Doe"

函数简写通过define也支持一个剩余参数(rest argument)(即,一个最终参数以在一个列表中收集额外参数):

(define (id arg ... . rest-idbody ...+)

它是以下内容的一个简写:

(define id (lambda (arg ... . rest-idbody ...+))

Examples:

(define (avg . l)
  (/ (apply + l) (length l)))
> (avg 1 2 3)

2

4.5.2 柯里函数简写

注意下面的make-add-suffix函数,它接收一个字符串并返回另一个接受一个字符串的函数:

(define make-add-suffix
  (lambda (s2)
    (lambda (s) (string-append s s2))))

虽然不常见,但make-add-suffix的结果可以直接调用,就像这样:

> ((make-add-suffix "!") "hello")

"hello!"

从某种意义上说,make-add-suffix是一个函数,接受两个参数,但一次只接受一个参数。一个函数,它接受它的参数的一些并返回一个函数以接受更多,这种函数有时被称为一个柯里函数(curried function)

使用define的函数简写表,make-add-suffix可以等效地编写为:

(define (make-add-suffix s2)
  (lambda (s) (string-append s s2)))

这个简写反映了函数调用(make-add-suffix "!")的形态。define表更进一步支持定义反映嵌套函数调用的柯里函数的一个简写:

(define ((make-add-suffix s2) s)
  (string-append s s2))

> ((make-add-suffix "!") "hello")

"hello!"

(define louder (make-add-suffix "!"))
(define less-sure (make-add-suffix "?"))

> (less-sure "really")

"really?"

> (louder "really")

"really!"

用于define的函数简写的完整语法如下所示:

(define (head argsbody ...+)
head=id
|(head args)
args=arg ...
|arg ... . rest-id

这个简写的扩展有一个给定义中的每个head的嵌套lambda表,其最里面的head与最外面的lambda对应。

4.5.3 多值和define-values

一个Racket表达式通常产生一个单独的结果,但有些表达式可以产生多个结果。例如,quotientremainder各自产生一个值,但quotient/remainder同时产生同样的两个值:

> (quotient 13 3)

4

> (remainder 13 3)

1

> (quotient/remainder 13 3)

4

1

如上所示,REPL在自己的行打印每一结果值。

多值函数可以依据values函数来实现,它接受任意数量的值并将它们作为结果返回:

> (values 1 2 3)

1

2

3

(define (split-name name)
  (let ([parts (regexp-split " " name)])
    (if (= (length parts) 2)
        (values (list-ref parts 0) (list-ref parts 1))
        (error "not a <first> <last> name"))))

> (split-name "Adam Smith")

"Adam"

"Smith"

define-values表同时绑定多个标识到产生于一个单表达式的多个结果:

(define-values (id ...) expr)

expr产生的结果数量必须与id的数量相匹配。

Examples:

(define-values (given surname) (split-name "Adam Smith"))
> given

"Adam"

> surname

"Smith"

一个define表(不是一个函数简写)等价于一个带有一个单个iddefine-values表。

在《Racket参考》中的“(define)”部分提供了更多关于定义的内容。

4.5.4 内部定义

当一个句法表的语法指定body,那相应的表可以是一个定义或一个表达式。作为一个body的一个定义是一个内部定义(internal definition)

只要最后一个body是表达式,在一个body序列中的表达式和内部定义可以被混合。

例如, lambda的语法是:

(lambda gen-formals
  body ...+)

所以下面是语法的有效实例:

(lambda (f)                ; 没有定义
  (printf "running\n")
  (f 0))
(lambda (f)                ; 一个定义
  (define (log-it what)
    (printf "~a\n" what))
  (log-it "running")
  (f 0)
  (log-it "done"))
(lambda (f n)              ; 两个定义
  (define (call n)
    (if (zero? n)
        (log-it "done")
        (begin
          (log-it "running")
          (f n)
          (call (- n 1)))))
  (define (log-it what)
    (printf "~a\n" what))
  (call n))

在一个特定的body序列中的内部定义是相互递归的,也就是说,任何定义都可以引用任何其它定义——只要这个引用在定义发生之前没有实际被求值。如果一个定义被过早引用,一个错误就会发生。

Examples:

(define (weird)
  (define x x)
  x)
> (weird)

x: undefined;

 cannot use before initialization

内部定义的一个序列只使用define很容易转换为一个等效的letrec表(如同在下一节中介绍的)。然而,其它的定义表可以表现为一个body,包括define-valuesstruct(见《程序员定义的数据类型》)或define-syntax(见《》)。

在《Racket参考》文档的“(intdef-body)”部分有内部定义更多知识点。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值