成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

SICP Python 描述 2.3 序列

AlexTuan / 3094人閱讀

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

2.3 序列

來源:2.3 Sequences

譯者:飛龍

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

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

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

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

長度。序列擁有有限的長度。

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

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

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

2.3.1 嵌套偶對

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

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

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

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

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

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

2.3.2 遞歸列表

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

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

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

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

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

序列的其余部分。

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

由于我們的列表表示是遞歸的,我們在實(shí)現(xiàn)中叫它rlist,以便不會和 Python 內(nèi)建的list類型混淆,我們會稍后在這一章介紹它。一個(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ù)類型的遞歸列表。遞歸列表唯一的行為條件是,就像偶對那樣,它的構(gòu)造器和選擇器是相反的函數(shù)。

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

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

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

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

>>> 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)都是可迭代的。它們隔離了嵌套偶對的每個(gè)層級,直到列表的末尾(在len_rlist中),或者到達(dá)了想要的元素(在getitem_rlist中)。

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

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

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

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

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

2.3.2 元組 II

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

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

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

此外,元素可以彼此相加以及與整數(shù)相乘。對于元組,加法和乘法操作并不對元素相加或相乘,而是組合和重復(fù)元組本身。也就是說,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ù),對應(yīng)內(nèi)建函數(shù)map。map的結(jié)果是一個(gè)本身不是序列的對象,但是可以通過調(diào)用tuple來轉(zhuǎn)換為序列。它是元組的構(gòu)造器。

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

map函數(shù)非常重要,因?yàn)樗蕾囉谛蛄谐橄螅何覀儾恍枰P(guān)心底層元組的結(jié)構(gòu),只需要能夠獨(dú)立訪問每個(gè)元素,以便將它作為參數(shù)傳入用于映射的函數(shù)中(這里是abs)。

2.3.4 序列迭代

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

考慮一個(gè)問題,計(jì)算一個(gè)值在序列中出現(xiàn)了多少次。我們可以使用while循環(huán)實(shí)現(xiàn)一個(gè)函數(shù)來計(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語句可以通過直接迭代元素值來簡化這個(gè)函數(shù)體,完全不需要引入index。例如(原文是For example,為雙關(guān)語),我們可以寫成:

>>> 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語句按照以下過程來執(zhí)行:

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

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

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

執(zhí)行語句組

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

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

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

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

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

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

這個(gè)綁定多個(gè)名稱到定長序列中多個(gè)值的模式,叫做序列解構(gòu)。它的模式和我們在賦值語句中看到的,將多個(gè)名稱綁定到多個(gè)值的模式相同。

范圍。range是另一種 Python 的內(nèi)建序列類型,它表示一個(gè)整數(shù)范圍。范圍可以使用range函數(shù)來創(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)造器會創(chuàng)建與范圍具有相同元素的元組,使元素易于查看。

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

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

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

常見的慣例是將單下劃線字符用于for頭部,如果這個(gè)名稱在語句組中不會使用。

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

Go Bears!
Go Bears!
Go Bears!

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

2.3.5 序列抽象

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

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

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

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

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

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

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

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

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

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

2.3.6 字符串

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

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

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

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

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

字符串滿足兩個(gè)基本的序列條件,我們在這一節(jié)開始介紹過它們:它們擁有長度并且支持元素選擇。

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

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

就像元組,字符串可以通過加法和乘法來組合:

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

字符串的行為不同于 Python 中其它序列類型。字符串抽象沒有實(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è)引號分隔的字符串字面值可以跨越多行。我們已經(jīng)在文檔字符串中使用了三個(gè)引號。

>>> """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)。它在長度和元素選擇上被認(rèn)為是單個(gè)字符。

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

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

str函數(shù)可以以任何類型的參數(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ù)雜的話題。這一章中,我們會移走字符串如何表示的細(xì)節(jié),但是,對許多應(yīng)用來說,字符串如何由計(jì)算機(jī)編碼的特定細(xì)節(jié)是必要的知識。Dive Into Python 3 的 4.1 ~ 4.3 節(jié)提供了字符編碼和 Unicode 的描述。

2.3.7 接口約定

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

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

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

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

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

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

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

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

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

>>> 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)算符%來定義。

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

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

>>> 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è)問題的解,使用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è)問題。它可以解構(gòu)為序列操作的流水線,包含mapfilter。

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

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

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

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

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

這里,我們的縮寫函數(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")

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

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

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

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

 for  in  if 

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

生成器表達(dá)式的求解結(jié)果值本身是個(gè)可迭代值。累計(jì)函數(shù),比如tuple、sum、maxmin可以將返回的對象作為參數(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á)式是使用可迭代(例如序列)接口約定的特化語法。這些表達(dá)式包含了mapfilter的大部分功能,但是避免了被調(diào)用函數(shù)的實(shí)際創(chuàng)建(或者,順便也避免了環(huán)境幀的創(chuàng)建需要調(diào)用這些函數(shù))。

歸約。在我們的示例中,我們使用特定的函數(shù)來累計(jì)結(jié)果,例如tuple或者sum。函數(shù)式編程語言(包括 Python)包含通用的高階累加器,具有多種名稱。Python 在functools模塊中包含reduce,它對序列中的元素從左到右依次調(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對應(yīng)的高階過程的組合會再一次在第四章出現(xiàn),在我們思考多臺計(jì)算機(jī)之間的分布式計(jì)算方法的時(shí)候。

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

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/38119.html

相關(guān)文章

  • SICP Python 描述 1.5 控制

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

    mingzhong 評論0 收藏0
  • SICP Python 描述 3.5 組合語言的解釋器

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

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

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

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

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

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

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

    leap_frog 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<