我们写代码时要学会适应辅助函数。作者提出了一个问题,如何对一个表排序。排序函数读取一个表,产生另一个表。排序函数的合约和用途如下:

(sort empty)
;; expected value:
empty
(sort (cons 1297.04 (cons 20000.00 (cons -505.25 empty))))
;; expected value:
(cons 20000.00 (cons 1297.04 (cons -505.25 empty))) 完整程序:(实际上就是插入排序)
#lang racket
;
;;sort:list-of-numbers->list-of-numbers
;;相当于插入排序
(define (sort alon)
(cond
[(empty? alon) empty]
[else (insert (car alon) (sort (cdr alon)) )]
)) ;insert:number list-of-numbers->list-of-numbers
;插入n到alon(降序表)中
(define (insert n alon)
(cond
[(empty? alon) (cons n empty)]
[else (cond
[(>= n (car alon)) (cons n alon)]
[else (cons (car alon) (insert n (cdr alon)))]
)]
)) (sort empty)
(sort (cons (cons (cons (cons (cons empty))))))

程序写起来还是有点困难的。

习题12.2.2.   Here is the function search:

;; search : number list-of-numbers  ->  boolean
(define (search n alon)
(cond
[(empty? alon) false]
[else (or (= (first alon) n) (search n (rest alon)))]))

It determines whether some number occurs in a list of numbers. The function may have to traverse the entire list to find out that the number of interest isn't contained in the list.

Develop the function search-sorted, which determines whether a number occurs in a sorted list of numbers. The function must take advantage of the fact that the list is sorted.

(函数search-sorted一般称为线性查找)

;; search-sorted : number list-of-numbers -> boolean
;; to determine if n is is alon
(define (search-sorted n alon)
(cond
[(empty? alon) #f]
(else (cond
[(= n (car alon)) #t]
[(< n (car alon)) #f]
[(> n (car alon)) (search-sorted n (cdr alon))]
))))
;; EXAMPLES AS TESTS
(search-sorted empty) "should be" false (search-sorted (cons (cons (cons (cons empty)))))
"should be" true (search-sorted (cons (cons (cons empty))))
"should be" false

问题泛化与函数泛华(generalize problem generialize function)

考虑多边形的绘制,

Consider the problem of drawing a polygon, that is, a geometric shape with an arbitrary number of corners.37 A natural representation for a polygon is a list of posn structures:

list-of-posns is either

  1. the empty list, empty, or

  2. (cons p lop) where p is a posn structure and lop is a list of posns.

Each posn represents one corner of the polygon. For example,

(cons (make-posn 10 10)
(cons (make-posn 60 60)
(cons (make-posn 10 60)
empty)))

represents a triangle. The question is what empty means as a polygon. The answer is that empty does not represent a polygon and therefore shouldn't be included in the class of polygon representations. A polygon should always have at least one corner, and the lists that represent polygons should always contain at least one posn. This suggest the following data definition:

(现在的问题是empty代表什么多边形,答案是empty并不代表多边形,所有empty类型并不是多边形,多边形至少应包含一个顶点,即表示多边形的表至少要包含一个posn结构体,隐藏,我们有如下数据定义:)

polygon is either

  1. (cons p empty) where p is a posn, or

  2. (cons p lop) where p is a posn structure and lop is a polygon.

In short, a discussion of how the chosen set of data (lists of posns) represents the intended information (geometric polygons) reveals that our choice was inadequate. Revising the data definition brings us closer to our intentions and makes it easier to design the program.

简而言之,使用有posn组成的表来表示多边形是不恰当的,数据定义的修改是我们更接近模板,编程更坚定。

Because our drawing primitives always produce true (if anything), it is natural to suggest the following contract and purpose statement:

因为绘图操作的返回值总是true,自然我们可以得出如下合约
;; draw-polygon : polygon  ->  true
;; to draw the polygon specified by a-poly
(define (draw-polygon a-poly) ...)

In other words, the function draws the lines between the corners and, if all primitive drawing steps work out, it produces true. For example, the above list of posns should produce a triangle.

这个函数画出顶点之间的连线,在所有的连线画出后,函数返回true。虽然现在的定义与常用表的定义并不相同,模板仍然和表处理

函数的模板类似:

;; draw-polygon : polygon  ->  true
;; to draw the polygon specified by a-poly
(define (draw-polygon a-poly)
(cond
[(empty? (rest a-poly)) ... (first a-poly) ...]
[else ... (first a-poly) ...
... (second a-poly) ...
... (draw-polygon (rest a-poly)) ...]))

完整代码:

(define (draw-polygon a-poly)
(cond
[(empty? (rest a-poly)) true]
[else (and (draw-solid-line (first a-poly) (second a-poly))
(draw-polygon (rest a-poly)))])) (draw-polygon (cons (make-posn )
(cons (make-posn )
(cons (make-posn )
empty)))
)

刚开始执行时总是报错:

evaluate (start <num> <num>) first。

google了一下没找到解决办法,自己摸索了下,在最前面加上

(start 100 100)

就可以了。后面的2个参数开始窗口的width和height)。

注:使用语言必须是《how to design programs》,其中的first,second和rest为how to design programs自己定义的.

(define v (cons 1 (cons  2 (cons 3 empty))))

> (first v)
1
> (second v)
2
> (rest v)
(list 2 3)

不幸的是,若用上面的代码对函数进行三角形绘制测试,我们可以立即发现,这个函数并没有绘制出一个含有3条边的多边形,而是绘制出一条连接所有顶点的开发曲线:

用数学的语言说,我们得到的函数比所需要的更一般,我们刚才定义的函数应该叫connect-the-dots,而不是draw-polygon。

要从这个一般的函数得到所需的函数,必须把最后一个点和第一个点连接起来,有几种方法可以做到这一点,而所有方法都要用到我们刚才定义的函数。换句话说,我们在一般的函数之上,在定义一个辅助函数。

一种方法是,定义一个新函数,把多边形的第一个顶点添加到表的末端,然后用新生成的表来绘制图形;另一种办法是,把最后一个顶点添加到表的前端;还有一种办法是修改draw-polygon函数,使他能够把最后一个顶点和第一个顶点连接起来。这里讨论第二种办法,其他2中作为练习。

要把a-poly的最后一个元素加到它的前端,我们需要:

(cons (last a-poly) a-poly)

其中last是个辅助函数,它的作用是提取非空表的最后一个元素。事实上,定义了 last,就得到了draw-polygon的定义:

;; draw-polygon : polygon  ->  true
;; to draw the polygon specified by a-poly
(define (draw-polygon a-poly)
(connect-dots (cons (last a-poly) a-poly))) ;; connect-dots : polygon -> true
;; to draw connections between the dots of a-poly
(define (connect-dots a-poly)
(cond
[(empty? (rest a-poly)) true]
[else (and (draw-solid-line (first a-poly) (second a-poly) 'red)
(connect-dots (rest a-poly)))])) ;; last : polygon -> posn
;; to extract the last posn on a-poly
(define (last a-poly)
(cond
[(empty? (rest a-poly)) (first a-poly)]
[else (last (rest a-poly))]))

总的来说,draw-polygon的设计是我们想到了一个更一般的问题:连接表中各点。通过定义辅助函数,使用更一般的函数(泛化函数),我们解决了问题。

问题:程序读入一个单词,返回对字母重新排序产生的单词表。

一个表示单词的方式是使用符号表,表中的每一个元素代表一个字母:'a,'b...下面是单词的定义:

word is either

  1. empty, or

  2. (cons a w) where a is a symbol ('a'b...'z) and w is a word.

Problem Statement
#| ------------------------------------------------------------------------
Permutations Data Definitions:
A word is either
* empty
* (cons S W) where S is a (letter) symbol and W is a word. A list-of-words is either
* empty
* (cons W L) where W is a word and L is a list-of-words. Examples:
empty is a word
(list 'b) is a word
(list 'a 'b) is a word
(list 'b 'a) is a word empty is a list of words
(list (list 'a 'b)) is a list of words
(list (list 'a 'b) (list 'b 'a)) is a list of words arrangements : word -> (listof word) Purpose: compute all possible arrangements (permutations)
of all the letters in a a-word; the result is a list of
words Example:
empty has one arrangement empty, so the result should be
- (list empty)
(list 'a) has one arrangment: (list 'a), so the result
should be
- (list (list 'a))
(list 'a 'b) has two different arrangements:
- (list 'b 'a)
- (list 'a 'b)
(list 'a 'b 'c) has six different arrangements:
- (list 'a 'b 'c)
- (list 'a 'c 'b)
- (list 'b 'a 'c)
- (list 'b 'c 'a)
- (list 'c 'a 'b)
- (list 'c 'b 'a)
Note: we should treat all "letters" as distinct from each other.
|#
(define (arrangements a-word)
(cond
((empty? a-word) (list empty))
(else (insert-everywhere/all-words (first a-word)
(arrangements (rest a-word))))))
;; We need a "helper" that combines the permutations for the rest
;; of the word with the first letter: insert-everywhere/all-words does the job. #| ------------------------------------------------------------------------
insert-everywhere/all-words : letter list-of-words -> list-of-words
Purpose: insert a-letter into all words of lo-words
Example:
'a (list (list 'b)) produces (list (list 'a 'b) (list 'b 'a))
'a (list (list 'b) (list 'c)) produces
(list (list (list 'a 'b) (list 'b 'a))
(list (list 'a 'c) (list 'c 'a)))
|#
(define (insert-everywhere/all-words letter lo-words)
(cond
((empty? lo-words) empty)
(else (append (insert-everywhere letter (first lo-words))
(insert-everywhere/all-words letter (rest lo-words))))))
;; We need a "helper" that inserts a letter into a single word. #| ------------------------------------------------------------------------
insert-everywhere : letter word -> list-of-words Purpose: insert a-letter everywhere into word, beginning, end and
between all letters of the word Examples:
'a (list 'b) ==> (list (list 'a 'b) (list 'b 'a))
'a (list 'b 'c) ==> (list (list 'a 'b 'c)
(list 'b 'a 'c)
(list 'b 'c 'a))
|# (define (insert-everywhere a-letter a-word)
(cond
((empty? a-word) (list (list a-letter)))
(else (cons (cons a-letter a-word)
(add-at-beginning (first a-word)
(insert-everywhere a-letter (rest a-word)))))))
;; We need a helper that adds a letter to the beginning of a list of words. #| ------------------------------------------------------------------------
add-at-beginning : letter list-of-words -> list-of-words
Purpose: add a-letter to the beginning of every word in lo-words
Example:
'a (list (list 'b 'c) (list 'c 'b))) should produce
(list (list 'a 'b 'c) (list 'a 'c 'b)))
|#
(define (add-at-beginning a-letter lo-words)
(cond
((empty? lo-words) empty)
(else (cons (cons a-letter (first lo-words))
(add-at-beginning a-letter (rest lo-words)))))) #| ------------------------------------------------------------------------
Tests: (add-at-beginning 'a (list (list 'b 'c) (list 'c 'b)))
=
(list (list 'a 'b 'c) (list 'a 'c 'b)) (insert-everywhere 'a (list 'b))
=
(list (list 'a 'b) (list 'b 'a)) (insert-everywhere/all-words 'a (list (list 'b)))
=
(list (list 'a 'b) (list 'b 'a)) (insert-everywhere/all-words 'a (list (list 'b) (list 'c)))
=
(list (list 'a 'b) (list 'b 'a)
(list 'a 'c) (list 'c 'a))
|# (arrangements empty)
= (list empty) (arrangements (list 'a))
= (list (list 'a)) (arrangements (list 'a 'b))
= (list (list 'a 'b)
(list 'b 'a)) (arrangements (list 'a 'b 'c))
= (list (list 'a 'b 'c)
(list 'a 'c 'b)
(list 'b 'a 'c)
(list 'b 'c 'a)
(list 'c 'a 'b)
(list 'c 'b 'a))

可以看到,用sheme写一个排列还是需要仔细思考的。

《how to design programs》12章函数复合的更多相关文章

  1. 《python解释器源码剖析》第12章--python虚拟机中的函数机制

    12.0 序 函数是任何一门编程语言都具备的基本元素,它可以将多个动作组合起来,一个函数代表了一系列的动作.当然在调用函数时,会干什么来着.对,要在运行时栈中创建栈帧,用于函数的执行. 在python ...

  2. Linux就这个范儿 第12章 一个网络一个世界

    Linux就这个范儿 第12章 一个网络一个世界 与Linux有缘相识还得从一项开发任务说起.十八年前,我在Nucleus  OS上开发无线网桥AP,需要加入STP生成树协议(SpanningTree ...

  3. JS读书心得:《JavaScript框架设计》——第12章 异步处理

    一.何为异步   执行任务的过程可以被分为发起和执行两个部分. 同步执行模式:任务发起后必须等待直到任务执行完成并返回结果后,才会执行下一个任务. 异步执行模式:任务发起后不等待任务执行完成,而是马上 ...

  4. ASM:《X86汇编语言-从实模式到保护模式》第12章:存储器的保护

    12章其实是11章的拓展,代码基本不变,就是在保护模式下展开讨论. ★PART1:存储器的保护机制 1. 修改段寄存器的保护 当执行把段选择子传到段寄存器的选择器部分的时候,处理器固件在完成传送之前, ...

  5. 敏捷软件开发:原则、模式与实践——第12章 ISP:接口隔离原则

    第12章 ISP:接口隔离原则 不应该强迫客户程序依赖并未使用的方法. 这个原则用来处理“胖”接口所存在的缺点.如果类的接口不是内敛的,就表示该类具有“胖”接口.换句话说,类的“胖”接口可以分解成多组 ...

  6. CSS3秘笈第三版涵盖HTML5学习笔记9~12章

    第9章,装饰网站导航 限制访问,处于隐私方面考虑,浏览器已经开始限制可以对伪类:visited应用哪些CSS属性了.其中包括对已访问过的链接定义color.background-color.borde ...

  7. 【STM32H7教程】第12章 STM32H7的HAL库框架设计学习

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第12章       STM32H7的HAL库框架设计学 ...

  8. 【RL-TCPnet网络教程】第12章 TCP传输控制协议基础知识

    第12章      TCP传输控制协议基础知识 本章节为大家讲解TCP(Transmission Control Protocol,传输控制协议),通过本章节的学习,需要大家对TCP有个基本的认识,方 ...

  9. 【二代示波器教程】第12章 示波器设计—DAC信号发生器的实现

    第12章      示波器设计—DAC信号发生器的实现 本章节为大家讲解二代示波器中信号发生器的实现.这个功能还是比较实用的,方便为二代示波器提供测试信号.实现了正弦波,方波和三角波的频率,幅度以及占 ...

随机推荐

  1. Android CursorAdapter

    CursorAdapter 继承于BaseAdapter是个虚类,它为cursor和ListView提供了连接的桥梁.            public abstract class     Cur ...

  2. Mac OS X Mavericks使用手册

    基本信息 作者: 施威铭研究室 出版社:清华大学出版社 ISBN:9787302386018 上架时间:2014-12-30 出版日期:2015 年1月 开本:16 版次:1-1 所属分类: 计算机 ...

  3. 【编译原理】语法分析LL(1)分析法的FIRST和FOLLOW集

    近来复习编译原理,语法分析中的自上而下LL(1)分析法,需要构造求出一个文法的FIRST和FOLLOW集,然后构造分析表,利用分析表+一个栈来做自上而下的语法分析(递归下降/预测分析),可是这个FIR ...

  4. Openstack REST API

    There are some high quality resources that already cover the OpenStack API, so this is a YEA (yet an ...

  5. 【转】基于Qt, TUIO和TSLIB的嵌入式Linux下的多点触摸设计

    这个教程描述了在嵌入式linux下使用Qt如何设置一个支持多点触摸和单点触摸的输入系统.这里假定你已经有了对应的驱动程序,驱动可以从触摸屏的厂商那里获得或者使用一个linux 内核源码中已经存在的驱动 ...

  6. fcitx 输入框纵向

    打开~/.config/fcitx/conf/fcitx-classic-ui.config 找到下面的:# 竖排候选词列表# 可选值:# True False#VerticalList=True-- ...

  7. 平时的笔记02:处理fnmatch模块

    # Copyright 2006 Joe Wreschnig## This program is free software; you can redistribute it and/or modif ...

  8. css-文本及其他

    <!DOCTYPE html>css7-文本和其他 text-align行内元素对齐方式,值为 左/中/右 对齐:left/right/center.test{text-align:cen ...

  9. enumerateObjectsUsingBlock VS for(... in ...)

    enumerateObjectsUsingBlock VS for(... in ...) for(... in ...)用起来非常方便.简洁,同时enumerateObjectsUsingBlock ...

  10. Ubutu命令 笔记积累

    1 man command 查询帮助 查询结果会有 name    synopsis(概要)  description 2 terminal 中快捷键: Ctrl +u 撤销 Ctrl +l  清屏 ...