Teach Yourself Scheme in Fixnum Days 6 recursion递归
A procedure body can contain calls to other procedures, not least itself:
(define factorial
(lambda (n)
(if (= n 0) 1
(* n (factorial (- n 1))))))
This recursive procedure calculates the factorial of a number. If the number is 0, the answer is 1. For any other number n, the procedure uses itself to calculate the factorial of n ‑ 1, multiplies that subresult by n, and returns the product.
Mutually recursive procedures are also possible. The following predicates for evenness and oddness use each other:
相互递归:
(define is-even?
(lambda (n)
(if (= n 0) #t
(is-odd? (- n 1))))) (define is-odd?
(lambda (n)
(if (= n 0) #f
(is-even? (- n 1)))))
These definitions are offered here only as simple illustrations of mutual recursion. Scheme already provides the primitive predicates even? and odd?.
6.1 letrec
If we wanted the above procedures as local variables, we could try to use alet form:
(let ((local-even? (lambda (n)
(if (= n 0) #t
(local-odd? (- n 1)))))
(local-odd? (lambda (n)
(if (= n 0) #f
(local-even? (- n 1))))))
(list (local-even? 23) (local-odd? 23)))
This won’t quite work, because the occurrences of local‑even? and local‑odd?in the initializations don’t refer to the lexical variables themselves. Changing the let to a let* won’t work either, for while the local‑even?inside local‑odd?’s body refers to the correct procedure value, thelocal‑odd? in local‑even?’s body still points elsewhere.
To solve problems like this, Scheme provides the form letrec:
(letrec ((local-even? (lambda (n)
(if (= n 0) #t
(local-odd? (- n 1)))))
(local-odd? (lambda (n)
(if (= n 0) #f
(local-even? (- n 1))))))
(list (local-even? 23) (local-odd? 23)))
The lexical variables introduced by a letrec are visible not only in theletrec-body but also within all the initializations. letrec is thus tailor-made for defining recursive and mutually recursive local procedures.
letrec专门用来定义为定义递归和相互递归的局部过程使用。
6.2 Named let
A recursive procedure defined using letrec can describe loops. Let’s say we want to display a countdown from 10:
(letrec ((countdown (lambda (i)
(if (= i 0) 'liftoff
(begin
(display i)
(newline)
(countdown (- i 1)))))))
(countdown 10))
This outputs on the console the numbers 10 down to 1, and returns the result liftoff.
Scheme allows a variant of let called named let to write this kind of loop more compactly:
scheme有一个named let可以让let有一个名字。
(let countdown ((i 10))
(if (= i 0) 'liftoff
(begin
(display i)
(newline)
(countdown (- i 1)))))
Note the presence of a variable identifying the loop immediately after thelet. This program is equivalent to the one written with letrec. You may consider the named let to be a macro (chap 8) expanding to the letrec form.
现在变量名countdown立即表示了整个loop,等价于上面用letrec的。
6.3 Iteration
countdown defined above is really a recursive procedure. Scheme can define loops only through recursion. There are no special looping or iteration constructs.
schemem只能通过递归来定义循环,没有loop或其他的循环购置。
Nevertheless, the loop as defined above is a genuine loop, in exactly the same way that other languages bill their loops. Ie, Scheme takes special care to ensure that recursion of the type used above will not generate the procedure call/return overhead.
Scheme does this by a process called tail-call elimination. If you look closely at the countdown procedure, you will note that when the recursive call occurs in countdown’s body, it is the tail call, or the very last thing done — each invocation of countdown either does not call itself, or when it does, it does so as its very last act. To a Scheme implementation, this makes the recursion indistinguishable from iteration. So go ahead, use recursion to write loops. It’s safe.
Here’s another example of a useful tail-recursive procedure:
(define list-position
(lambda (o l)
(let loop ((i ) (l l))
(if (null? l) #f
(if (eqv? (car l) o) i
(loop (+ i ) (cdr l)))))))
list‑position finds the index of the first occurrence of the object o in the list l. If the object is not found in the list, the procedure returns #f. list-position找出list表l中对象o第一次出现的位置。
Here’s yet another tail-recursive procedure, one that reverses its argument list “in place”, ie, by mutating the contents of the existing list, and without allocating a new one:
通过改变(mutate)存在的list,不用分配内存:
(define reverse!
(lambda (s)
(let loop ((s s) (r '()))
(if (null? s) r
(let ((d (cdr s)))
(set-cdr! s r)
(loop d s))))))
(reverse! is a useful enough procedure that it is provided primitively in many Scheme dialects, eg, MzScheme and Guile.)
For some numerical examples of recursion (including iteration), see Appendix C.
|
1
2
3
4
5
6
7
8
9
10
11
12
|
scheme@(guile-user)> (define list-reverse... (lambda (ls)... (let loop ((ls ls) (xs '()))... (if (null? ls)... xs... (loop (cdr ls)... (cons (car ls) xs))))))scheme@(guile-user)> (define ls '(1 2 3 4))scheme@(guile-user)> (list-reverse ls)(4 3 2 1)scheme@(guile-user)> ls(1 2 3 4) |
6.4 Mapping a procedure across a list
A special kind of iteration involves repeating the same action for each element of a list. Scheme offers two procedures for this situation: map andfor‑each.
The map procedure applies a given procedure to every element of a given list, and returns the list of the results. Eg,
(map add2 '(1 2 3))
=> (3 4 5)
The for‑each procedure also applies a procedure to each element in a list, but returns void. The procedure application is done purely for any side-effects it may cause. Eg,
for-each 函数主要是为了得到函数的副作用:
(for-each display
(list 1 2 3))
输出:1 2 3
如果用map会输出:
123(#<void> #<void> #<void>) (Racket下)
has the side-effect of displaying the strings (in the order they appear) on the console.
The procedures applied by map and for‑each need not be one-argument procedures. For example, given an n-argument procedure, map takes n lists and applies the procedure to every set of n of arguments selected from across the lists. Eg,
map和for-each的存储过程参数不必是一个参数的存储过程,可以是n个。
(map cons '(1 2 3) '(10 20 30))
=> ((1 . 10) (2 . 20) (3 . 30)) (map + '(1 2 3) '(10 20 30))
=> (11 22 33)
参考:http://www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-scheme-Z-H-8.html
http://lispor.is-programmer.com/posts/23255.html
Teach Yourself Scheme in Fixnum Days 6 recursion递归的更多相关文章
- Teach Yourself Scheme in Fixnum Days 13 Jump跳转
Jumps One of the signal features of Scheme is its support for jumps or nonlocal control. Specificall ...
- recursion 递归以及递归的缺点
递归定义的算法有两部分: 递归基:直接定义最简单情况下的函数值: 递归步:通过较为简单情况下的函数值定义一般情况下的函数值. 应用条件与准则: (1)问题具有某种可借用的类同自身的子问题描述的性质: ...
- string formating字符串格式化,function函数,group组,recursion递归,练习
# -*- coding: UTF-8 -*- msg = 'i am {} my hobby is {}'.format('lhf',18) print(msg) msg1 = 'i am %s m ...
- Recursion递归
/*java.lang 核心包 如 String Math Integer System Thread等 拿来直接用 * java.awt 窗口工具 GUI * java.net 网络包 * java ...
- Github上的1000多本免费电子书重磅来袭!
Github上的1000多本免费电子书重磅来袭! 以前 StackOverFlow 也给出了一个免费电子书列表,现在在Github上可以看到时刻保持更新的列表了. 瞥一眼下面的书籍分类目录,你就能 ...
- Github 的一个免费编程书籍列表
Index Ada Agda Alef Android APL Arduino ASP.NET MVC Assembly Language Non-X86 AutoHotkey Autotools A ...
- Lisp语言学习的书
Scheme <How to Design Programs : An Introduction to Programming and Computing>(<程序设计方法>) ...
- racket学习-call/cc (let/cc)
Drracket continuation 文中使用let/cc代替call/cc Racket文档中,let/cc说明为: (let/cc k body ...+) Equivalent to (c ...
- 开始学习Scheme
开始学习Scheme 函数式编程(Functional Programming)是在MIT研究人工智能(Artificial Intelligence)时发明的,其编程语言为Lisp.确切地说,L ...
随机推荐
- 上传jar包到Apache Archiva本地仓库
1.登录archiva,点击左侧的upload Artifact 2 jar 包名称 为:java-client-4.1.2.jar 网上的pom配置为: <!-- https://mvnrep ...
- Bootstrap--导航元素
1.标签形导航 2.胶囊型导航: 3.垂直堆叠形导航: 4.导航加下拉菜单: 5.导航列表: 6.可切换的标签导航:
- Ajax请求用户控件(.ascx)404错误
- jTemplates——学习(1)
这里介绍一个基于jQuery开发的模板引擎. jTemplates目前最新的版本是0.7.8,由tPython开发.官方网站:http://jtemplates.tpython.com 两个附件, 一 ...
- Graph Databases—The NOSQL Phenomenon阅读笔记
本章内容着重对了NOSQL和RDBMS(关系型数据库管理系统)的不同,以及其各自背后设计时考虑的因素.然后接下来,着重讲述了NOSQL的4种分类方法.下面我们将对重要知识点进行汇总. 1.We def ...
- DB2完美卸载
会安装,也要会卸载.详细的安装说明不多,我这个我觉得写得还算全. 准备工作. 1.用 ps -ef|grep db2 找出db2安装目录 2. ./db2level 查出DB2的 ...
- Codeforces Round #256 (Div. 2/B)/Codeforces448B_Suffix Structures(字符串处理)
解题报告 四种情况相应以下四组数据. 给两字符串,推断第一个字符串是怎么变到第二个字符串. automaton 去掉随意字符后成功转换 array 改变随意两字符后成功转换 再者是两个都有和两个都没有 ...
- 宙斯是一个完整的Hadoop的作业平台[转]
https://github.com/alibaba/zeus 宙斯(zeus)是什么 宙斯是一个完整的Hadoop的作业平台从Hadoop任务的调试运行到生产任务的周期调度 宙斯支持任务的整个生命周 ...
- Protobuf实现Android Socket通讯开发教程
本节为您介绍Protobuf实现Android Socket通讯开发教程,因此,我们需要先了理一下protobuf 是什么? Protocol buffers是一种编码方法构造的一种有效而可扩展的格式 ...
- Electron开发环境部署
Electron开发环境部署 安装node.js 可以从node.js官方网站上获取安装包,并进行安装,安装完可以通过 ndoe -v 指令进行版本查看. 本文的开发环境为node.js 4.4.5. ...