Composing Programs 2.3 Sequence - 02

伯克利CS 61A的教学用书笔记和一些课程笔记

一些python笔记

01 关于sum

>>> sum([2, 3, 4])
9
>>> sum([2, 3, 4], 5)    # sum最多能有两个参数,第一个为可迭代对象,第二个为起始值
14
>>> [2, 3] + [4]    # 首先我们知道这个
[2, 3, 4]
>>> sum([[2, 3], [4]], [])    # 然后类比上面,从空列表开始加,那么就会返回一个列表
[2, 3, 4]
>>> [2, 3] + [4] +[]    # 上面的写法就相当于这个
[2, 3, 4]

# 更多例子
>>> sum([ [1], [2, 3], [4] ], [])
[1, 2, 3, 4]
>>> sum([ [1] ], [])
[1]
>>> sum([ [[1]], [2] ], [])
[[1], 2]

2.3.6   Trees

Our ability to use lists as the elements of other lists provides a new means of combination in our programming language. This ability is called a closure property of a data type. 数据类型的闭包(封闭)属性的一个例子:用一个list作为另一个list的一个元素。 In general, a method for combining data values has a closure property if the result of combination can itself be combined using the same method. 如果这样组合的结果本身也能够用同样的方式进行组合,那么这样组合数据的方式就具有闭包(封闭)属性。闭包(封闭)属性给了某种组合方式强大的能力,因为它允许我们通过这种组合方式建立层次结构。

比如下面的代码建立的数据其实具有一定的层次结构:

one_two = [1, 2]
nested = [[1, 2], [],
          [[3, False, None],
           [4, lambda: 5]]]

 

这其实就是嵌套列表。树这种基础的数据结构就是对层次结构的值的建立和操作引入一定规则之后形成的。

基于嵌套列表,我们定义树 tree 的性质和结构,树的每个节点(node)应该具有 label,表示这个节点的值,节点还可以有分支 branches,如果一个节点没有分支,我们称之为叶子 leaf,这和现实中的树非常相似。

下面建立树这种数据结构。和建立有理数一样,我们可以定义出构造器 tree 和选择器 label,另外,对于树我们再定义它具有分支 branches,我们先来定义它们。

>>> def tree(root_label, branches=[]):
        for branch in branches:
            assert is_tree(branch), 'branches must be trees'
        return [root_label] + list(branches)

>>> def label(tree):
        return tree[0]

>>> def branches(tree):
        return tree[1:]

每个树枝 branches 也都是一棵树 tree,而且一棵树也必须有根节点值(root label)。所以我们来建立对树的判断 is_ tree ,以及对叶子的判断 is_leaf。注意递归。

>>> def is_tree(tree):
        if type(tree) != list or len(tree) < 1:
            return False
        for branch in branches(tree):
            if not is_tree(branch):
                return False
        return True

>>> def is_leaf(tree):
        return not branches(tree)

测试一下。

>>> t = tree(3, [tree(1), tree(2, [tree(1), tree(1)])])
>>> t
[3, [1], [2, [1], [1]]]
>>> label(t)
3
>>> branches(t)
[[1], [2, [1], [1]]]
>>> label(branches(t)[1])
2
>>> is_leaf(t)
False
>>> is_leaf(branches(t)[0])
True

下面用以及定义好的函数建立一个Fibonacci树,它的所有label都是Fibonacci序列中的一个数,根节点值是第n个Fibonacci数。注意递归。

>>> def fib_tree(n):
        if n == 0 or n == 1:
            return tree(n)
        else:
            left, right = fib_tree(n-2), fib_tree(n-1)
            fib_n = label(left) + label(right)
            return tree(fib_n, [left, right])
>>> fib_tree(5)
[5, [2, [1], [1, [0], [1]]], [3, [1, [0], [1]], [2, [1], [1, [0], [1]]]]]

除了建立一棵树,我们还可以用建立好的函数来定义处理树的函数。count_leaves函数计算出一棵树有多少叶子。注意递归。

>>> def count_leaves(tree):
      if is_leaf(tree):
          return 1
      else:
          branch_counts = [count_leaves(b) for b in branches(tree)]
          return sum(branch_counts)
>>> count_leaves(fib_tree(5))
8

Partition trees

 A partition tree for n using parts up to size m is a binary (two branch) tree that represents the choices taken during computation.

一个Partition trees就是,用最大为m的正整数来划分n,比如n = 2, m = 2,则一共有两种方法,分别是:

2 = 2

2 = 1 + 1

用二叉树(每个节点值label都只有两个分支breaches)来表示这种算法,就相当于在划分n时做出的一系列选择,如果我们将这棵树打印出来,就知道对n的每种划分方式了。

来定义Partition trees。

  • the left (index 0) branch contains all ways of partitioning n using at least one m, 左分支包含用不超过m的正整数来划分n的所有方式
  • the right (index 1) branch contains partitions using parts up to m-1, and 右分支包含不超过m - 1的正整数来划分n的所有方式
  • the root label is m. 根节点是m

叶子节点是True或者False,表示从该叶子节点到根节点的路径是否是对n的一个正确的划分。如果不理解怎么用递归解决这个问题,请看用最大不超过m的正整数来划分n,有多少种方式

>>> def partition_tree(n, m):
        """Return a partition tree of n using parts of up to m."""
        if n == 0:
            return tree(True)
        elif n < 0 or m == 0:
            return tree(False)
        else:
            left = partition_tree(n-m, m)
            right = partition_tree(n, m-1)
            return tree(m, [left, right])
>>> partition_tree(2, 2)
[2, [True], [1, [1, [True], [False]], [False]]]

下面我们想办法将树打印出来。注意递归。

>>> def print_parts(tree, partition=[]):
        if is_leaf(tree):
            if label(tree):
                print(' + '.join(partition))
        else:
            left, right = branches(tree)
            m = str(label(tree))
            print_parts(left, partition + [m])
            print_parts(right, partition)
>>> print_parts(partition_tree(6, 4))
4 + 2
4 + 1 + 1
3 + 3
3 + 2 + 1
3 + 1 + 1 + 1
2 + 2 + 2
2 + 2 + 1 + 1
2 + 1 + 1 + 1 + 1
1 + 1 + 1 + 1 + 1 + 1

同样我们也能对树的分支进行切片,比如,我们想对一棵树的分支数进行限制。A binarized tree has at most two branches. A common tree transformation called binarization finds a binarized tree with the same labels as an original tree by grouping together branches. 二叉树(binary tree)每个节点最多有两个分支,我们可以将一棵普通的树转化为二叉树,这叫二叉化(binarization)。通过将分组组合,形成一棵和原来的树节点值(label)一样的二叉树。

>>> def right_binarize(t):
        """Construct a right-branching binary tree."""
    return tree(label(t), binarize_branches(branches(t)))

>>> def binarize_branches(bs):
        """Binarize a list of branches."""
        if len(bs) > 2:
            first, rest = bs[0], bs[1:]
            return [right_binarize(first), binarize_branches(rest)]
        else:
            return [right_binarize(b) for b in bs]

>>> right_binarize(tree(0, [tree(x) for x in [1, 2, 3, 4, 5, 6, 7]]))
[0, [1], [[2], [[3], [[4], [[5], [[6], [7]]]]]]]

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值