惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
博客园 - 聂微东
J
Java Code Geeks
Engineering at Meta
Engineering at Meta
Jina AI
Jina AI
D
Docker
B
Blog
S
SegmentFault 最新的问题
宝玉的分享
宝玉的分享
D
DataBreaches.Net
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Y
Y Combinator Blog
N
Netflix TechBlog - Medium
月光博客
月光博客
F
Fortinet All Blogs
爱范儿
爱范儿
H
Help Net Security
腾讯CDC
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
WordPress大学
WordPress大学
The Cloudflare Blog
有赞技术团队
有赞技术团队
T
Tailwind CSS Blog
U
Unit 42

博客园 - heros

理解线性回归 摆脱MarshalByRefObject和DynamicProxy显示接口 url不重复会造成asp.net的私有缓存占用内存过高 回“血泪八条---给中国IT教育培训机构” (因评论一再被银光小子删除,所以决定另开一文) android gif 图片显示问题 注意TDes8 -> Expand方法 简单的字符串相似度计算 [转]Carbide.VS 3.0.1的安装问题 - heros - 博客园 分享SharpDevelop Asp.net DevServer插件 算法题:装备合成 asp.net treeview 复选后展开的bug [转]WebService压缩 ActiveRecord环境搭建和配置 用C#实现win7下vs2008的激活(附学习交流代码) (译)Scheme简明教程7-词法变量 asp.net ModalDialog控件(非ajax tool kit) (译)Scheme简明教程6-条件语句 (译)Scheme简明教程5-Forms代码结构 (译)Scheme简明教程4-数据类型转换及其它类型
(译)Scheme简明教程8-递归
heros · 2009-12-18 · via 博客园 - heros
 

第六章 递归

 一个过程体中可以包含对其它过程的调用,特别的是也可以调用自己。

(define factorial
 (lambda (n)
    (if (= n 0) 1
        (* n (factorial (- n 1))))))

这个递归过程用来计算一个数的阶乘。如果这个数是0,则结果为1。对于任何其它的值n,这个过程会调用其自身来完成n-1阶乘的计算,然后将这个子结果乘上n并返回最终产生的结果。

互递归过程也是可以的。下面判断奇偶数的过程相互进行了调用。

(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)))))

这里提供的两个过程的定义仅作为简单的互递归示例。Scheme已经提供了简单的判断过程even? odd?

6.1          letrec

如果希望将上面的过程定义为局部的,我们会尝试使用let结构:

(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)))

但这并不能成功,因为在初始化值过程中出现的local-even? local-odd?指向的并不是这两个过程本身。把let换成let*同样也不能奏效,因为这时虽然local-odd?中出现的local-even?指向的是前面刚创建好的局部的过程,但local-even? 中的local-odd?还是指向了别处。

为解决这个问题,Scheme提供了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)))

letrec创建的词法变量不仅可以在letrec执行体中可见而且在初始化中也可见。letrec是专门为局部的递归和互递归过程而设置的。(这里也可以使用define来创建两个子结构的方式来实现局部递归)

6.2          已命名let

使用letrec定义递归过程可以实现循环。如果我们想显示101的降数列,可以这样写:

(letrec ((countdown (lambda (i)
                      (if (= i 0) 'liftoff
                          (begin
                            (display i)
                            (newline)
                            (countdown (- i 1)))))))
 (countdown 10))

这会在控制台上输出101,并会返回结果liftoff

Scheme允许使用一种叫已命名letlet变体来更简洁的写出这样的循环:

(let countdown ((i 10))
 (if (= i 0) 'liftoff
      (begin
        (display i)
        (newline)
        (countdown (- i 1)))))

注意在let的后面立即声明了一个变量用来表示这个循环。这个程序和先前用letrec写的程序是等价的。你可以将已命名let看成一个对letrec结构进行扩展的宏。