CS61A fa2021 Composing Programs 2.9 Recursive Objects 递归对象

2.9   递归对象

若一个对象的某个属性值为所属同一个类的某个对象,则称其为递归对象。

问题:这个属性对象可以是自己吗?还是必须得是其他对象呢?

答:可以是自己,但这就是无限递归了。

2.9.1   链表(Linked List Class

正如之间所介绍的,链表由两部分组成,第一部分是一个元素,第二部分是一个子链表。子链表又由两个部分组成……这就很有递归的味道了。直到empty,递归结束。

想讨论链表的递归,总得有链表供我们使用吧。链表满足序列的基本特性——有限长度与元素检索。下面我们通过代码来创建链表类与实现它的基本功能。

>>> class Link:
        """A linked list with a first element and the rest."""
        empty = ()
        def __init__(self, first, rest=empty):
            assert rest is Link.empty or isinstance(rest, Link)
            self.first = first
            self.rest = rest
        def __getitem__(self, i):
            if i == 0:
                return self.first
            else:
                return self.rest[i-1]
        def __len__(self):
            return 1 + len(self.rest)
>>> s = Link(3, Link(4, Link(5)))
>>> len(s)
3
>>> s[1]
4

链表类或许还不够完善,当创建完一个链表后我们只有检索单个元素的操作,却很难再整体观察该链表的每个元素。需要做一些改进,加一个新的方法。

>>> def link_expression(s):
        """Return a string that would evaluate to s."""
        if s.rest is Link.empty:
            rest = ''
        else:
            rest = ', ' + link_expression(s.rest)
        return 'Link({0}{1})'.format(s.first, rest)
>>> link_expression(s)
'Link(3, Link(4, Link(5)))'

这是个好函数,方便实用。如果把它与Link类的__repr__方法绑定起来就更方便啦。

>>> Link.__repr__ = link_expression
>>> s
Link(3, Link(4, Link(5)))

到此我们就基本 完成了Link类的实现。

一个函数里面可以定义另一个函数(闭包),列表里面可以有列表。链表也具有闭包的特性,即链表的第一个元素可以也是链表。

>>> s_first = Link(s, Link(6))
>>> s_first
Link(Link(3, Link(4, Link(5))), Link(6))
>>> len(s_first)
2
>>> len(s_first[0])
3
>>> s_first[0][2]
5

由此看来关于链表递归的深度还是值得讨论的,不过先放一放,暂时把目光放在一些更简单问题上。

递归和递归对象简直是天作之合。我们可以用递归实现很对关于链表的操作。其实,在创建链表类时就已经用到了递归,不过我们的脚步并不止于此。对于链表,我想写一个和list一样的extend函数,用于把两个链表连接起来。

最简单的思路是,把前一个链表的empty改为要加上去的链表。不过目前我们没有直接抵达empty的方法。可以重新从递归的角度思考一下,这个函数该怎么写。

>>> def extend_link(s, t):
        if s is Link.empty:
            return t
        else:
            return Link(s.first, extend_link(s.rest, t))
>>> extend_link(s, s)
Link(3, Link(4, Link(5, Link(3, Link(4, Link(5))))))
>>> Link.__add__ = extend_link
>>> s + s
Link(3, Link(4, Link(5, Link(3, Link(4, Link(5))))))

至此,我们已经发现了链表其实和列表特别像。他们都是有限长度,可以进行元素检索,可以extend。自然,我们也能对列表的map函数和filter函数进行复刻,来做两个属于链表的函数。

那么该怎么写这样的函数呢?递归似乎是万能之法。

>>> def map_link(f, s):
        if s is Link.empty:
            return s
        else:
            return Link(f(s.first), map_link(f, s.rest))
>>> map_link(square, s)
Link(9, Link(16, Link(25)))

map函数完成了,对每个可操作元素调用f,而后产生一个新的链表,完美。那么filter函数呢?

>>> def filter_link(f, s)
        if s is Link.empty:
            return s
        else:
            if f(f.first):
                return Link(s.fisrt, filter(f, s.rest))
            else:
                return filter(f, s.rest)
>>> odd = lambda x: x % 2 == 1
>>> map_link(square, filter_link(odd, s))
Link(9, Link(25))
>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值