成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

SICP Python 描述 2.3 序列

AlexTuan / 3035人閱讀

摘要:序列不是特定的抽象數(shù)據(jù)類(lèi)型,而是不同類(lèi)型共有的一組行為。不像抽象數(shù)據(jù)類(lèi)型,我們并沒(méi)有闡述如何構(gòu)造序列。這兩個(gè)選擇器和一個(gè)構(gòu)造器,以及一個(gè)常量共同實(shí)現(xiàn)了抽象數(shù)據(jù)類(lèi)型的遞歸列表。

2.3 序列

來(lái)源:2.3 Sequences

譯者:飛龍

協(xié)議:CC BY-NC-SA 4.0

序列是數(shù)據(jù)值的順序容器。不像偶對(duì)只有兩個(gè)元素,序列可以擁有任意(但是有限)個(gè)有序元素。

序列在計(jì)算機(jī)科學(xué)中是強(qiáng)大而基本的抽象。例如,如果我們使用序列,我們就可以列出伯克利的每個(gè)學(xué)生,或者世界上的每所大學(xué),或者每所大學(xué)中的每個(gè)學(xué)生。我們可以列出上過(guò)的每一門(mén)課,提交的每個(gè)作業(yè),或者得到的每個(gè)成績(jī)。序列抽象讓數(shù)千個(gè)數(shù)據(jù)驅(qū)動(dòng)的程序影響著我們每天的生活。

序列不是特定的抽象數(shù)據(jù)類(lèi)型,而是不同類(lèi)型共有的一組行為。也就是說(shuō),它們是許多序列種類(lèi),但是都有一定的屬性。特別地,

長(zhǎng)度。序列擁有有限的長(zhǎng)度。

元素選擇。序列的每個(gè)元素都擁有相應(yīng)的非負(fù)整數(shù)作為下標(biāo),它小于序列長(zhǎng)度,以第一個(gè)元素的 0 開(kāi)始。

不像抽象數(shù)據(jù)類(lèi)型,我們并沒(méi)有闡述如何構(gòu)造序列。序列抽象是一組行為,它們并沒(méi)有完全指定類(lèi)型(例如,使用構(gòu)造器和選擇器),但是可以在多種類(lèi)型中共享。序列提供了一個(gè)抽象層級(jí),將特定程序如何操作序列類(lèi)型的細(xì)節(jié)隱藏。

這一節(jié)中,我們開(kāi)發(fā)了一個(gè)特定的抽象數(shù)據(jù)類(lèi)型,它可以實(shí)現(xiàn)序列抽象。我們之后介紹實(shí)現(xiàn)相同抽象的 Python 內(nèi)建類(lèi)型。

2.3.1 嵌套偶對(duì)

對(duì)于有理數(shù),我們使用二元組將兩個(gè)整數(shù)對(duì)象配對(duì),之后展示了我們可以同樣通過(guò)函數(shù)來(lái)實(shí)現(xiàn)偶對(duì)。這種情況下,每個(gè)我們構(gòu)造的偶對(duì)的元素都是整數(shù)。然而,就像表達(dá)式,元組可以嵌套。每個(gè)偶對(duì)的元素本身也可以是偶對(duì),這個(gè)特性在實(shí)現(xiàn)偶對(duì)的任意一個(gè)方法,元組或調(diào)度函數(shù)中都有效。

可視化偶對(duì)的一個(gè)標(biāo)準(zhǔn)方法 -- 這里也就是偶對(duì)(1,2) -- 叫做盒子和指針記號(hào)。每個(gè)值,復(fù)合或原始,都描述為指向盒子的指針。原始值的盒子只包含那個(gè)值的表示。例如,數(shù)值的盒子只包含數(shù)字。偶對(duì)的盒子實(shí)際上是兩個(gè)盒子:左邊的部分(箭頭指向的)包含偶對(duì)的第一個(gè)元素,右邊的部分包含第二個(gè)。

嵌套元素的 Python 表達(dá)式:

>>> ((1, 2), (3, 4))
((1, 2), (3, 4))

具有下面的結(jié)構(gòu):

使用元組作為其它元組元素的能力,提供了我們編程語(yǔ)言中的一個(gè)新的組合手段。我們將這種將元組以這種方式嵌套的能力叫做元組數(shù)據(jù)類(lèi)型的封閉性。通常,如果組合結(jié)果自己可以使用相同的方式組合,組合數(shù)據(jù)值的方式就滿(mǎn)足封閉性。封閉性在任何組合手段中都是核心能力,因?yàn)樗试S我們創(chuàng)建層次數(shù)據(jù)結(jié)構(gòu) -- 結(jié)構(gòu)由多個(gè)部分組成,它們自己也由多個(gè)部分組成,以此類(lèi)推。我們?cè)诘谌聲?huì)探索一些層次結(jié)構(gòu)?,F(xiàn)在,我們考慮一個(gè)特定的重要結(jié)構(gòu)。

2.3.2 遞歸列表

我們可以使用嵌套偶對(duì)來(lái)構(gòu)建任意長(zhǎng)度的元素列表,它讓我們能夠?qū)崿F(xiàn)抽象序列。下面的圖展示了四元素列表1, 2, 3, 4的遞歸表示:

這個(gè)列表由一系列偶對(duì)表示。每個(gè)偶對(duì)的第一個(gè)元素是列表中的元素,而第二個(gè)元素是用于表示列表其余部分的偶對(duì)。最后一個(gè)偶對(duì)的第二個(gè)元素是None,它表明列表到末尾了。我們可以使用嵌套的元組字面值來(lái)構(gòu)造這個(gè)結(jié)構(gòu):

>>> (1, (2, (3, (4, None))))
(1, (2, (3, (4, None))))

這個(gè)嵌套的結(jié)構(gòu)通常對(duì)應(yīng)了一種非常實(shí)用的序列思考方式,我們?cè)?Python 解釋器的執(zhí)行規(guī)則中已經(jīng)見(jiàn)過(guò)它了。一個(gè)非空序列可以劃分為:

它的第一個(gè)元素,以及

序列的其余部分。

序列的其余部分本身就是一個(gè)(可能為空的)序列。我們將序列的這種看法叫做遞歸,因?yàn)樾蛄邪渌蛄凶鳛榈诙€(gè)組成部分。

由于我們的列表表示是遞歸的,我們?cè)趯?shí)現(xiàn)中叫它rlist,以便不會(huì)和 Python 內(nèi)建的list類(lèi)型混淆,我們會(huì)稍后在這一章介紹它。一個(gè)遞歸列表可以由第一個(gè)元素和列表的剩余部分構(gòu)造。None值表示空的遞歸列表。

>>> empty_rlist = None
>>> def make_rlist(first, rest):
        """Make a recursive list from its first element and the rest."""
        return (first, rest)
>>> def first(s):
        """Return the first element of a recursive list s."""
        return s[0]
>>> def rest(s):
        """Return the rest of the elements of a recursive list s."""
        return s[1]

這兩個(gè)選擇器和一個(gè)構(gòu)造器,以及一個(gè)常量共同實(shí)現(xiàn)了抽象數(shù)據(jù)類(lèi)型的遞歸列表。遞歸列表唯一的行為條件是,就像偶對(duì)那樣,它的構(gòu)造器和選擇器是相反的函數(shù)。

如果一個(gè)遞歸列表s由元素f和列表r構(gòu)造,那么first(s)返回f,并且rest(s)返回r。

我們可以使用構(gòu)造器和選擇器來(lái)操作遞歸列表。

>>> counts = make_rlist(1, make_rlist(2, make_rlist(3, make_rlist(4, empty_rlist))))
>>> first(counts)
1
>>> rest(counts)
(2, (3, (4, None)))

遞歸列表可以按序儲(chǔ)存元素序列,但是它還沒(méi)有實(shí)現(xiàn)序列的抽象。使用我們已經(jīng)定義的數(shù)據(jù)類(lèi)型抽象,我們就可以實(shí)現(xiàn)描述兩個(gè)序列的行為:長(zhǎng)度和元素選擇。

>>> def len_rlist(s):
        """Return the length of recursive list s."""
        length = 0
        while s != empty_rlist:
            s, length = rest(s), length + 1
        return length
>>> def getitem_rlist(s, i):
        """Return the element at index i of recursive list s."""
        while i > 0:
            s, i = rest(s), i - 1
        return first(s)

現(xiàn)在,我們可以將遞歸列表用作序列了:

>>> len_rlist(counts)
4
>>> getitem_rlist(counts, 1)  # The second item has index 1
2

兩個(gè)實(shí)現(xiàn)都是可迭代的。它們隔離了嵌套偶對(duì)的每個(gè)層級(jí),直到列表的末尾(在len_rlist中),或者到達(dá)了想要的元素(在getitem_rlist中)。

下面的一系列環(huán)境圖示展示了迭代過(guò)程,getitem_rlist通過(guò)它找到了遞歸列表中下標(biāo)1中的元素2。

while頭部中的表達(dá)式求值為真,這會(huì)導(dǎo)致while語(yǔ)句組中的賦值語(yǔ)句被執(zhí)行:

這里,局部名稱(chēng)s現(xiàn)在指向以原列表第二個(gè)元素開(kāi)始的子列表?,F(xiàn)在,while頭中的表達(dá)式求值為假,于是 Python 會(huì)求出getitem_rlist最后一行中返回語(yǔ)句中的表達(dá)式。

最后的環(huán)境圖示展示了調(diào)用first的局部幀,它包含綁定到相同子列表的s。first函數(shù)挑選出值2并返回了它,完成了getitem_rlist的調(diào)用。

這個(gè)例子演示了遞歸列表計(jì)算的常見(jiàn)模式,其中迭代的每一步都操作原列表的一個(gè)逐漸變短的后綴。尋找遞歸列表的長(zhǎng)度和元素的漸進(jìn)式處理過(guò)程需要一些時(shí)間來(lái)計(jì)算。(第三章中,我們會(huì)學(xué)會(huì)描述這種函數(shù)的計(jì)算時(shí)間。)Python 的內(nèi)建序列類(lèi)型以不同方式實(shí)現(xiàn),它對(duì)于計(jì)算序列長(zhǎng)度和獲取元素并不具有大量的計(jì)算開(kāi)銷(xiāo)。

2.3.2 元組 II

實(shí)際上,我們引入用于形成原始偶對(duì)的tuple類(lèi)型本身就是完整的序列類(lèi)型。元組比起我們以函數(shù)式實(shí)現(xiàn)的偶對(duì)抽象數(shù)據(jù)結(jié)構(gòu),本質(zhì)上提供了更多功能。

元組具有任意的長(zhǎng)度,并且也擁有序列抽象的兩個(gè)基本行為:長(zhǎng)度和元素選擇。下面的digits是一個(gè)四元素元組。

>>> digits = (1, 8, 2, 8)
>>> len(digits)
4
>>> digits[3]
8

此外,元素可以彼此相加以及與整數(shù)相乘。對(duì)于元組,加法和乘法操作并不對(duì)元素相加或相乘,而是組合和重復(fù)元組本身。也就是說(shuō),operator模塊中的add函數(shù)(以及+運(yùn)算符)返回兩個(gè)被加參數(shù)連接成的新元組。operator模塊中的mul函數(shù)(以及*運(yùn)算符)接受整數(shù)k和元組,并返回含有元組參數(shù)k個(gè)副本的新元組。

>>> (2, 7) + digits * 2
(2, 7, 1, 8, 2, 8, 1, 8, 2, 8)

映射。將一個(gè)元組變換為另一個(gè)元組的強(qiáng)大手段是在每個(gè)元素上調(diào)用函數(shù),并收集結(jié)果。這一計(jì)算的常用形式叫做在序列上映射函數(shù),對(duì)應(yīng)內(nèi)建函數(shù)map。map的結(jié)果是一個(gè)本身不是序列的對(duì)象,但是可以通過(guò)調(diào)用tuple來(lái)轉(zhuǎn)換為序列。它是元組的構(gòu)造器。

>>> alternates = (-1, 2, -3, 4, -5)
>>> tuple(map(abs, alternates))
(1, 2, 3, 4, 5)

map函數(shù)非常重要,因?yàn)樗蕾?lài)于序列抽象:我們不需要關(guān)心底層元組的結(jié)構(gòu),只需要能夠獨(dú)立訪(fǎng)問(wèn)每個(gè)元素,以便將它作為參數(shù)傳入用于映射的函數(shù)中(這里是abs)。

2.3.4 序列迭代

映射本身就是通用計(jì)算模式的一個(gè)實(shí)例:在序列中迭代所有元素。為了在序列上映射函數(shù),我們不僅僅需要選擇特定的元素,還要依次選擇每個(gè)元素。這個(gè)模式非常普遍,Python 擁有額外的控制語(yǔ)句來(lái)處理序列數(shù)據(jù):for語(yǔ)句。

考慮一個(gè)問(wèn)題,計(jì)算一個(gè)值在序列中出現(xiàn)了多少次。我們可以使用while循環(huán)實(shí)現(xiàn)一個(gè)函數(shù)來(lái)計(jì)算這個(gè)數(shù)量。

>>> def count(s, value):
        """Count the number of occurrences of value in sequence s."""
        total, index = 0, 0
        while index < len(s):
            if s[index] == value:
                total = total + 1
            index = index + 1
        return total
>>> count(digits, 8)
2

Python for語(yǔ)句可以通過(guò)直接迭代元素值來(lái)簡(jiǎn)化這個(gè)函數(shù)體,完全不需要引入index。例如(原文是For example,為雙關(guān)語(yǔ)),我們可以寫(xiě)成:

>>> def count(s, value):
        """Count the number of occurrences of value in sequence s."""
        total = 0
        for elem in s:
            if elem == value:
                total = total + 1
        return total
>>> count(digits, 8)
2

for語(yǔ)句按照以下過(guò)程來(lái)執(zhí)行:

求出頭部表達(dá)式,它必須產(chǎn)生一個(gè)可迭代的值。

對(duì)于序列中的每個(gè)元素值,按順序:

在局部環(huán)境中將變量名綁定到這個(gè)值上。

執(zhí)行語(yǔ)句組。

步驟 1 引用了可迭代的值。序列是可迭代的,它們的元素可看做迭代的順序。Python 的確擁有其他可迭代類(lèi)型,但是我們現(xiàn)在只關(guān)注序列。術(shù)語(yǔ)“可迭代對(duì)象”的一般定義會(huì)在第四章的迭代器一節(jié)中出現(xiàn)。

這個(gè)求值過(guò)程的一個(gè)重要結(jié)果是,在for語(yǔ)句執(zhí)行完畢之后,會(huì)綁定到序列的最后一個(gè)元素上。這個(gè)for循環(huán)引入了另一種方式,其中局部環(huán)境可以由語(yǔ)句來(lái)更新。

序列解構(gòu)。程序中的一個(gè)常見(jiàn)模式是,序列的元素本身就是序列,但是具有固定的長(zhǎng)度。for語(yǔ)句可在頭部中包含多個(gè)名稱(chēng),將每個(gè)元素序列“解構(gòu)”為各個(gè)元素。例如,我們擁有一個(gè)偶對(duì)(也就是二元組)的序列:

>>> pairs = ((1, 2), (2, 2), (2, 3), (4, 4))

下面的for語(yǔ)句的頭部帶有兩個(gè)名詞,會(huì)將每個(gè)名稱(chēng)xy分別綁定到每個(gè)偶對(duì)的第一個(gè)和第二個(gè)元素上。

>>> for x, y in pairs:
        if x == y:
            same_count = same_count + 1
>>> same_count
2

這個(gè)綁定多個(gè)名稱(chēng)到定長(zhǎng)序列中多個(gè)值的模式,叫做序列解構(gòu)。它的模式和我們?cè)谫x值語(yǔ)句中看到的,將多個(gè)名稱(chēng)綁定到多個(gè)值的模式相同。

范圍。range是另一種 Python 的內(nèi)建序列類(lèi)型,它表示一個(gè)整數(shù)范圍。范圍可以使用range函數(shù)來(lái)創(chuàng)建,它接受兩個(gè)整數(shù)參數(shù):所得范圍的第一個(gè)數(shù)值和最后一個(gè)數(shù)值加一。

>>> range(1, 10)  # Includes 1, but not 10
range(1, 10)

在范圍上調(diào)用tuple構(gòu)造器會(huì)創(chuàng)建與范圍具有相同元素的元組,使元素易于查看。

>>> tuple(range(5, 8))
(5, 6, 7)

如果只提供了一個(gè)元素,它會(huì)解釋為最后一個(gè)數(shù)值加一,范圍開(kāi)始于 0。

>>> total = 0
>>> for k in range(5, 8):
        total = total + k
>>> total
18

常見(jiàn)的慣例是將單下劃線(xiàn)字符用于for頭部,如果這個(gè)名稱(chēng)在語(yǔ)句組中不會(huì)使用。

>>> for _ in range(3):
        print("Go Bears!")

Go Bears!
Go Bears!
Go Bears!

要注意對(duì)解釋器來(lái)說(shuō),下劃線(xiàn)只是另一個(gè)名稱(chēng),但是在程序員中具有固定含義,它表明這個(gè)名稱(chēng)不應(yīng)出現(xiàn)在任何表達(dá)式中。

2.3.5 序列抽象

我們已經(jīng)介紹了兩種原生數(shù)據(jù)類(lèi)型,它們實(shí)現(xiàn)了序列抽象:元組和范圍。兩個(gè)都滿(mǎn)足這一章開(kāi)始時(shí)的條件:長(zhǎng)度和元素選擇。Python 還包含了兩種序列類(lèi)型的行為,它們擴(kuò)展了序列抽象。

成員性。可以測(cè)試一個(gè)值在序列中的成員性。Python 擁有兩個(gè)操作符innot in,取決于元素是否在序列中出現(xiàn)而求值為TrueFalse。

>>> digits
(1, 8, 2, 8)
>>> 2 in digits
True
>>> 1828 not in digits
True

所有序列都有叫做indexcount的方法,它會(huì)返回序列中某個(gè)值的下標(biāo)(或者數(shù)量)。

切片。序列包含其中的子序列。我們?cè)陂_(kāi)發(fā)我們的嵌套偶對(duì)實(shí)現(xiàn)時(shí)觀察到了這一點(diǎn),它將序列切分為它的第一個(gè)元素和其余部分。序列的切片是原序列的任何部分,由一對(duì)整數(shù)指定。就像range構(gòu)造器那樣,第一個(gè)整數(shù)表示切片的起始下標(biāo),第二個(gè)表示結(jié)束下標(biāo)加一。

Python 中,序列切片的表示類(lèi)似于元素選擇,使用方括號(hào)。冒號(hào)分割了起始和結(jié)束下標(biāo)。任何邊界上的省略都被當(dāng)作極限值:起始下標(biāo)為 0,結(jié)束下標(biāo)是序列長(zhǎng)度。

>>> digits[0:2]
(1, 8)
>>> digits[1:]
(8, 2, 8)

Python 序列抽象的這些額外行為的枚舉,給我們了一個(gè)機(jī)會(huì)來(lái)反思數(shù)據(jù)抽象通常由什么構(gòu)成。抽象的豐富性(也就是說(shuō)它包含行為的多少)非常重要。對(duì)于使用抽象的用戶(hù),額外的行為很有幫助,另一方面,滿(mǎn)足新類(lèi)型抽象的豐富需求是個(gè)挑戰(zhàn)。為了確保我們的遞歸列表實(shí)現(xiàn)支持這些額外的行為,需要一些工作量。另一個(gè)抽象豐富性的負(fù)面結(jié)果是,它們需要用戶(hù)長(zhǎng)時(shí)間學(xué)習(xí)。

序列擁有豐富的抽象,因?yàn)樗鼈冊(cè)谟?jì)算中無(wú)處不在,所以學(xué)習(xí)一些復(fù)雜的行為是合理的。通常,多數(shù)用戶(hù)定義的抽象應(yīng)該盡可能簡(jiǎn)單。

擴(kuò)展閱讀。切片符號(hào)接受很多特殊情況,例如負(fù)的起始值,結(jié)束值和步長(zhǎng)。Dive Into Python 3 中有一節(jié)叫做列表切片,完整描述了它。這一章中,我們只會(huì)用到上面描述的基本特性。

2.3.6 字符串

文本值可能比數(shù)值對(duì)計(jì)算機(jī)科學(xué)來(lái)說(shuō)更基本。作為一個(gè)例子,Python 程序以文本編寫(xiě)和儲(chǔ)存。Python 中原生的文本數(shù)據(jù)類(lèi)型叫做字符串,相應(yīng)的構(gòu)造器是str。

關(guān)于字符串在 Python 中如何表示和操作有許多細(xì)節(jié)。字符串是豐富抽象的另一個(gè)示例,程序員需要滿(mǎn)足一些實(shí)質(zhì)性要求來(lái)掌握。這一節(jié)是字符串基本行為的摘要。

字符串字面值可以表達(dá)任意文本,被單引號(hào)或者雙引號(hào)包圍。

>>> "I am string!"
"I am string!"
>>> "I"ve got an apostrophe"
"I"ve got an apostrophe"
>>> "您好"
"您好"

我們已經(jīng)在代碼中見(jiàn)過(guò)字符串了,在print的調(diào)用中作為文檔字符串,以及在assert語(yǔ)句中作為錯(cuò)誤信息。

字符串滿(mǎn)足兩個(gè)基本的序列條件,我們?cè)谶@一節(jié)開(kāi)始介紹過(guò)它們:它們擁有長(zhǎng)度并且支持元素選擇。

>>> city = "Berkeley"
>>> len(city)
8
>>> city[3]
"k"

字符串的元素本身就是包含單一字符的字符串。字符是字母表中的任意單一字符,標(biāo)點(diǎn)符號(hào),或者其它符號(hào)。不像許多其它編程語(yǔ)言那樣,Python 沒(méi)有多帶帶的字符類(lèi)型,任何文本都是字符串,表示單一字符的字符串長(zhǎng)度為 1、

就像元組,字符串可以通過(guò)加法和乘法來(lái)組合:

>>> city = "Berkeley"
>>> len(city)
8
>>> city[3]
"k"

字符串的行為不同于 Python 中其它序列類(lèi)型。字符串抽象沒(méi)有實(shí)現(xiàn)我們?yōu)樵M和范圍描述的完整序列抽象。特別地,字符串上實(shí)現(xiàn)了成員性運(yùn)算符in,但是與序列上的實(shí)現(xiàn)具有完全不同的行為。它匹配子字符串而不是元素。

>>> "here" in "Where"s Waldo?"
True

與之相似,字符串上的countindex方法接受子串作為參數(shù),而不是單一字符。count的行為有細(xì)微差別,它統(tǒng)計(jì)字符串中非重疊字串的出現(xiàn)次數(shù)。

>>> "Mississippi".count("i")
4
>>> "Mississippi".count("issi")
1

多行文本。字符串并不限制于單行文本,三個(gè)引號(hào)分隔的字符串字面值可以跨越多行。我們已經(jīng)在文檔字符串中使用了三個(gè)引號(hào)。

>>> """The Zen of Python
claims, Readability counts.
Read more: import this."""
"The Zen of Python
claims, "Readability counts."
Read more: import this."

在上面的打印結(jié)果中, (叫做“反斜杠加 n”)是表示新行的單一元素。雖然它表示為兩個(gè)字符(反斜杠和 n)。它在長(zhǎng)度和元素選擇上被認(rèn)為是單個(gè)字符。

字符串強(qiáng)制。字符串可以從 Python 的任何對(duì)象通過(guò)以某個(gè)對(duì)象值作為參數(shù)調(diào)用str構(gòu)造函數(shù)來(lái)創(chuàng)建,這個(gè)字符串的特性對(duì)于從多種類(lèi)型的對(duì)象中構(gòu)造描述性字符串非常實(shí)用。

>>> str(2) + " is an element of " + str(digits)
"2 is an element of (1, 8, 2, 8)"

str函數(shù)可以以任何類(lèi)型的參數(shù)調(diào)用,并返回合適的值,這個(gè)機(jī)制是后面的泛用函數(shù)的主題。

方法。字符串在 Python 中的行為非常具有生產(chǎn)力,因?yàn)榇罅康姆椒ǘ挤祷刈址淖凅w或者搜索其內(nèi)容。一部分這些方法由下面的示例介紹。

>>> "1234".isnumeric()
True
>>> "rOBERT dE nIRO".swapcase()
"Robert De Niro"
>>> "snakeyes".upper().endswith("YES")
True

擴(kuò)展閱讀。計(jì)算機(jī)中的文本編碼是個(gè)復(fù)雜的話(huà)題。這一章中,我們會(huì)移走字符串如何表示的細(xì)節(jié),但是,對(duì)許多應(yīng)用來(lái)說(shuō),字符串如何由計(jì)算機(jī)編碼的特定細(xì)節(jié)是必要的知識(shí)。Dive Into Python 3 的 4.1 ~ 4.3 節(jié)提供了字符編碼和 Unicode 的描述。

2.3.7 接口約定

在復(fù)合數(shù)據(jù)的處理中,我們強(qiáng)調(diào)了數(shù)據(jù)抽象如何讓我們?cè)O(shè)計(jì)程序而不陷入數(shù)據(jù)表示的細(xì)節(jié),以及抽象如何為我們保留靈活性來(lái)嘗試備用表示。這一節(jié)中,我們引入了另一種強(qiáng)大的設(shè)計(jì)原則來(lái)處理數(shù)據(jù)結(jié)構(gòu) -- 接口約定的用法。

接口約定使在許多組件模塊中共享的數(shù)據(jù)格式,它可以混合和匹配來(lái)展示數(shù)據(jù)。例如,如果我們擁有多個(gè)函數(shù),它們?nèi)拷邮苄蛄凶鳛閰?shù)并且返回序列值,我們就可以把它們每一個(gè)用于上一個(gè)的輸出上,并選擇任意一種順序。這樣,我們就可以通過(guò)將函數(shù)鏈接成流水線(xiàn),來(lái)創(chuàng)建一個(gè)復(fù)雜的過(guò)程,每個(gè)函數(shù)都是簡(jiǎn)單而專(zhuān)一的。

這一節(jié)有兩個(gè)目的,來(lái)介紹以接口約定組織程序的概念,以及展示模塊化序列處理的示例。

考慮下面兩個(gè)問(wèn)題,它們首次出現(xiàn),并且只和序列的使用相關(guān)。

對(duì)前n個(gè)斐波那契數(shù)中的偶數(shù)求和。

列出一個(gè)名稱(chēng)中的所有縮寫(xiě)字母,它包含每個(gè)大寫(xiě)單詞的首字母。

這些問(wèn)題是有關(guān)系的,因?yàn)樗鼈兛梢越鈽?gòu)為簡(jiǎn)單的操作,它們接受序列作為輸入,并產(chǎn)出序列作為輸出。而且,這些操作是序列上的計(jì)算的一般方法的實(shí)例。讓我們思考第一個(gè)問(wèn)題,它可以解構(gòu)為下面的步驟:

 enumerate     map    filter  accumulate
-----------    ---    ------  ----------
naturals(n)    fib    iseven     sum

下面的fib函數(shù)計(jì)算了斐波那契數(shù)(現(xiàn)在使用了for語(yǔ)句更新了第一章中的定義)。

>>> def fib(k):
        """Compute the kth Fibonacci number."""
        prev, curr = 1, 0  # curr is the first Fibonacci number.
        for _ in range(k - 1):
             prev, curr = curr, prev + curr
        return curr

謂詞iseven可以使用整數(shù)取余運(yùn)算符%來(lái)定義。

>>> def iseven(n):
        return n % 2 == 0

mapfilter函數(shù)是序列操作,我們已經(jīng)見(jiàn)過(guò)了map,它在序列中的每個(gè)元素上調(diào)用函數(shù)并且收集結(jié)果。filter函數(shù)接受序列,并且返回序列中謂詞為真的元素。兩個(gè)函數(shù)都返回間接對(duì)象,mapfilter對(duì)象,它們是可以轉(zhuǎn)換為元組或求和的可迭代對(duì)象。

>>> nums = (5, 6, -7, -8, 9)
>>> tuple(filter(iseven, nums))
(6, -8)
>>> sum(map(abs, nums))
35

現(xiàn)在我們可以實(shí)現(xiàn)even_fib,第一個(gè)問(wèn)題的解,使用map、filtersum。

>>> def sum_even_fibs(n):
        """Sum the first n even Fibonacci numbers."""
        return sum(filter(iseven, map(fib, range(1, n+1))))
>>> sum_even_fibs(20)
3382

現(xiàn)在,讓我們思考第二個(gè)問(wèn)題。它可以解構(gòu)為序列操作的流水線(xiàn),包含mapfilter。

enumerate  filter   map   accumulate
---------  ------  -----  ----------
  words    iscap   first    tuple

字符串中的單詞可以通過(guò)字符串對(duì)象上的split方法來(lái)枚舉,默認(rèn)以空格分割。

>>> tuple("Spaces between words".split())
("Spaces", "between", "words")

單詞的第一個(gè)字母可以使用選擇運(yùn)算符來(lái)獲取,確定一個(gè)單詞是否大寫(xiě)的謂詞可以使用內(nèi)建謂詞isupper定義。

>>> def first(s):
        return s[0]
>>> def iscap(s):
        return len(s) > 0 and s[0].isupper()

這里,我們的縮寫(xiě)函數(shù)可以使用mapfilter定義。

>>> def acronym(name):
        """Return a tuple of the letters that form the acronym for name."""
        return tuple(map(first, filter(iscap, name.split())))
>>> acronym("University of California Berkeley Undergraduate Graphics Group")
("U", "C", "B", "U", "G", "G")

這些不同問(wèn)題的相似解法展示了如何使用通用的計(jì)算模式,例如映射、過(guò)濾和累計(jì),來(lái)組合序列的接口約定上的操作。序列抽象讓我們編寫(xiě)出這些簡(jiǎn)明的解法。

將程序表達(dá)為序列操作有助于我們?cè)O(shè)計(jì)模塊化的程序。也就是說(shuō),我們的設(shè)計(jì)由組合相關(guān)的獨(dú)立片段構(gòu)建,每個(gè)片段都對(duì)序列進(jìn)行轉(zhuǎn)換。通常,我們可以通過(guò)提供帶有接口約定的標(biāo)準(zhǔn)組件庫(kù)來(lái)鼓勵(lì)模塊化設(shè)計(jì),接口約定以靈活的方式連接這些組件。

生成器表達(dá)式。Python 語(yǔ)言包含第二個(gè)處理序列的途徑,叫做生成器表達(dá)式。它提供了與mapreduce相似的功能,但是需要更少的函數(shù)定義。

生成器表達(dá)式組合了過(guò)濾和映射的概念,并集成于單一的表達(dá)式中,以下面的形式:

 for  in  if 

為了求出生成器表達(dá)式,Python 先求出,它必須返回一個(gè)可迭代值。之后,對(duì)于每個(gè)元素,按順序?qū)⒃刂到壎ǖ?b>,求出過(guò)濾器表達(dá)式,如果它產(chǎn)生真值,就會(huì)求出映射表達(dá)式。

生成器表達(dá)式的求解結(jié)果值本身是個(gè)可迭代值。累計(jì)函數(shù),比如tuple、sum、maxmin可以將返回的對(duì)象作為參數(shù)。

>>> def acronym(name):
        return tuple(w[0] for w in name.split() if iscap(w))
>>> def sum_even_fibs(n):
        return sum(fib(k) for k in range(1, n+1) if fib(k) % 2 == 0)

生成器表達(dá)式是使用可迭代(例如序列)接口約定的特化語(yǔ)法。這些表達(dá)式包含了mapfilter的大部分功能,但是避免了被調(diào)用函數(shù)的實(shí)際創(chuàng)建(或者,順便也避免了環(huán)境幀的創(chuàng)建需要調(diào)用這些函數(shù))。

歸約。在我們的示例中,我們使用特定的函數(shù)來(lái)累計(jì)結(jié)果,例如tuple或者sum。函數(shù)式編程語(yǔ)言(包括 Python)包含通用的高階累加器,具有多種名稱(chēng)。Python 在functools模塊中包含reduce,它對(duì)序列中的元素從左到右依次調(diào)用二元函數(shù),將序列歸約為一個(gè)值。下面的表達(dá)式計(jì)算了五個(gè)因數(shù)的積。

>>> from operator import mul
>>> from functools import reduce
>>> reduce(mul, (1, 2, 3, 4, 5))
120

使用這個(gè)更普遍的累計(jì)形式,除了求和之外,我們也可以計(jì)算斐波那契數(shù)列中奇數(shù)的積,將序列用作接口約定。

>>> def product_even_fibs(n):
        """Return the product of the first n even Fibonacci numbers, except 0."""
        return reduce(mul, filter(iseven, map(fib, range(2, n+1))))
>>> product_even_fibs(20)
123476336640

mapfilterreduce對(duì)應(yīng)的高階過(guò)程的組合會(huì)再一次在第四章出現(xiàn),在我們思考多臺(tái)計(jì)算機(jī)之間的分布式計(jì)算方法的時(shí)候。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/38119.html

相關(guān)文章

  • SICP Python 描述 1.5 控制

    摘要:函數(shù)體由表達(dá)式組成。我們說(shuō)頭部控制語(yǔ)句組。于是,函數(shù)體內(nèi)的賦值語(yǔ)句不會(huì)影響全局幀。包含了多種假值,包括和布爾值。布爾值表示了邏輯表達(dá)式中的真值。執(zhí)行測(cè)試以及返回布爾值的函數(shù)通常以開(kāi)頭,并不帶下劃線(xiàn)例如等等。返回值之后會(huì)和預(yù)期結(jié)果進(jìn)行比對(duì)。 1.5 控制 來(lái)源:1.5 Control 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 我們現(xiàn)在可以定義的函數(shù)能力有限,因?yàn)槲覀冞€不知...

    mingzhong 評(píng)論0 收藏0
  • SICP Python 描述 3.5 組合語(yǔ)言的解釋器

    摘要:計(jì)算器語(yǔ)言解釋器的核心是叫做的遞歸函數(shù),它會(huì)求解樹(shù)形表達(dá)式對(duì)象。到目前為止,我們?cè)诿枋銮笾颠^(guò)程中所引用的表達(dá)式樹(shù),還是概念上的實(shí)體。解析器實(shí)際上由兩個(gè)組件組成,詞法分析器和語(yǔ)法分析器。標(biāo)記序列由叫做的詞法分析器產(chǎn)生,并被叫做語(yǔ)法分析器使用。 3.5 組合語(yǔ)言的解釋器 來(lái)源:3.5 Interpreters for Languages with Combination 譯者:飛龍 ...

    sanyang 評(píng)論0 收藏0
  • SICP Python 描述 3.3 遞歸數(shù)據(jù)結(jié)構(gòu)

    摘要:遞歸列表可以使用遞歸函數(shù)最為自然地操作,就像它們的名稱(chēng)和結(jié)構(gòu)表示的那樣。處理遞歸列表遞歸列表結(jié)構(gòu)將列表表示為首個(gè)元素和列表的剩余部分的組合。例如,我們可以使用高階遞歸函數(shù)將樹(shù)的每個(gè)葉子平方,它的結(jié)構(gòu)類(lèi)似于。成員測(cè)試會(huì)遞歸遍歷整個(gè)列表。 3.3 遞歸數(shù)據(jù)結(jié)構(gòu) 來(lái)源:3.3 Recursive Data Structures 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 在第二...

    libin19890520 評(píng)論0 收藏0
  • SICP Python 描述 1.3 定義新的函數(shù)

    摘要:到目前為止,我們的環(huán)境只包含全局幀。要注意函數(shù)名稱(chēng)是重復(fù)的,一個(gè)在幀中,另一個(gè)是函數(shù)的一部分。運(yùn)算符字表達(dá)式是全局幀中發(fā)現(xiàn)的名稱(chēng),綁定到了內(nèi)建的加法函數(shù)上。嚴(yán)格來(lái)說(shuō),這并不是問(wèn)題所在不同局部幀中的的綁定是不相關(guān)的。 1.3 定義新的函數(shù) 來(lái)源:1.3 Defining New Functions 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 我們已經(jīng)在 Python 中認(rèn)識(shí)...

    SegmentFault 評(píng)論0 收藏0
  • SICP Python 描述 第五章 序列和協(xié)程

    摘要:消息向迭代器獲取所表示的底層序列的下一個(gè)元素。為了對(duì)方法調(diào)用做出回應(yīng),迭代器可以執(zhí)行任何計(jì)算來(lái)獲取或計(jì)算底層數(shù)據(jù)序列的下一個(gè)元素。這個(gè)迭代器應(yīng)擁有方法,依次返回序列中的每個(gè)元素,最后到達(dá)序列末尾時(shí)產(chǎn)生異常。 第五章 序列和協(xié)程 來(lái)源:Chapter 5: Sequences and Coroutines 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 5.1 引言 在這一章中,我...

    leap_frog 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<