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

資訊專欄INFORMATION COLUMN

SICP Python 描述 2.4 可變數(shù)據(jù)

kamushin233 / 1631人閱讀

摘要:可變對(duì)象可以在程序執(zhí)行期間改變。這個(gè)賦值語(yǔ)句將名稱綁定到全局幀中的返回函數(shù)上所返回的函數(shù),內(nèi)部叫做,和定義所在位置即的局部環(huán)境相關(guān)聯(lián)。賦值語(yǔ)句始終改變現(xiàn)有幀中的綁定。

2.4 可變數(shù)據(jù)

來(lái)源:2.4 Mutable Data

譯者:飛龍

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

我們已經(jīng)看到了抽象在幫助我們應(yīng)對(duì)大型系統(tǒng)的復(fù)雜性時(shí)如何至關(guān)重要。有效的程序整合也需要一些組織原則,指導(dǎo)我們構(gòu)思程序的概要設(shè)計(jì)。特別地,我們需要一些策略來(lái)幫助我們構(gòu)建大型系統(tǒng),使之模塊化。也就是說(shuō),它們可以“自然”劃分為可以分離開(kāi)發(fā)和維護(hù)的各個(gè)相關(guān)部分。

我們用于創(chuàng)建模塊化程序的強(qiáng)大工具之一,是引入可能會(huì)隨時(shí)間改變的新類型數(shù)據(jù)。這樣,單個(gè)數(shù)據(jù)可以表示獨(dú)立于其他程序演化的東西。對(duì)象行為的改變可能會(huì)由它的歷史影響,就像世界中的實(shí)體那樣。向數(shù)據(jù)添加狀態(tài)是這一章最終目標(biāo):面向?qū)ο缶幊痰囊亍?/p>

我們目前引入的原生數(shù)據(jù)類型 -- 數(shù)值、布爾值、元組、范圍和字符串 -- 都是不可變類型的對(duì)象。雖然名稱的綁定可以在執(zhí)行過(guò)程中修改為環(huán)境中不同的值,但是這些值本身不會(huì)改變。這一章中,我們會(huì)介紹一組可變數(shù)據(jù)類型??勺儗?duì)象可以在程序執(zhí)行期間改變。

2.4.1 局部狀態(tài)

我們第一個(gè)可變對(duì)象的例子就是局部狀態(tài)。這個(gè)狀態(tài)會(huì)在程序執(zhí)行期間改變。

為了展示函數(shù)的局部狀態(tài)是什么東西,讓我們對(duì)從銀行取錢的情況進(jìn)行建模。我們會(huì)通過(guò)創(chuàng)建叫做withdraw的函數(shù)來(lái)實(shí)現(xiàn)它,它將要取出的金額作為參數(shù)。如果賬戶中有足夠的錢來(lái)取出,withdraw應(yīng)該返回取錢之后的余額。否則,withdraw應(yīng)該返回消息"Insufficient funds"。例如,如果我們以賬戶中的$100開(kāi)始,我們希望通過(guò)調(diào)用withdraw來(lái)得到下面的序列:

>>> withdraw(25)
75
>>> withdraw(25)
50
>>> withdraw(60)
"Insufficient funds"
>>> withdraw(15)
35

觀察表達(dá)式withdraw(25),求值了兩次,產(chǎn)生了不同的值。這是一種用戶定義函數(shù)的新行為:它是非純函數(shù)。調(diào)用函數(shù)不僅僅返回一個(gè)值,同時(shí)具有以一些方式修改函數(shù)的副作用,使帶有相同參數(shù)的下次調(diào)用返回不同的結(jié)果。我們所有用戶定義的函數(shù),到目前為止都是純函數(shù),除非他們調(diào)用了非純的內(nèi)建函數(shù)。它們?nèi)耘f是純函數(shù),因?yàn)樗鼈儾⒉辉试S修改任何在局部環(huán)境幀之外的東西。

為了使withdraw有意義,它必須由一個(gè)初始賬戶余額創(chuàng)建。make_withdraw函數(shù)是個(gè)高階函數(shù),接受起始余額作為參數(shù),withdraw函數(shù)是它的返回值。

>>> withdraw = make_withdraw(100)

make_withdraw的實(shí)現(xiàn)需要新類型的語(yǔ)句:nonlocal語(yǔ)句。當(dāng)我們調(diào)用make_withdraw時(shí),我們將名稱balance綁定到初始值上。之后我們定義并返回了局部函數(shù),withdraw,它在調(diào)用時(shí)更新并返回balance的值。

>>> def make_withdraw(balance):
        """Return a withdraw function that draws down balance with each call."""
        def withdraw(amount):
            nonlocal balance                 # Declare the name "balance" nonlocal
            if amount > balance:
                return "Insufficient funds"
            balance = balance - amount       # Re-bind the existing balance name
            return balance
        return withdraw

這個(gè)實(shí)現(xiàn)的新奇部分是nonlocal語(yǔ)句,無(wú)論什么時(shí)候我們修改了名稱balance的綁定,綁定都會(huì)在balance所綁定的第一個(gè)幀中修改。回憶一下,在沒(méi)有nonlocal語(yǔ)句的情況下,賦值語(yǔ)句總是會(huì)在環(huán)境的第一個(gè)幀中綁定名稱。nonlocal語(yǔ)句表明,名稱出現(xiàn)在環(huán)境中不是第一個(gè)(局部)幀,或者最后一個(gè)(全局)幀的其它地方。

我們可以將這些修改使用環(huán)境圖示來(lái)可視化。下面的環(huán)境圖示展示了每個(gè)調(diào)用的效果,以上面的定義開(kāi)始。我們省略了函數(shù)值中的代碼,以及不在我們討論中的表達(dá)式樹(shù)。

我們的定義語(yǔ)句擁有平常的效果:它創(chuàng)建了新的用戶定義函數(shù),并且將名稱make_withdraw在全局幀中綁定到那個(gè)函數(shù)上。

下面,我們使用初始的余額參數(shù)20來(lái)調(diào)用make_withdraw。

>>> wd = make_withdraw(20)

這個(gè)賦值語(yǔ)句將名稱wd綁定到全局幀中的返回函數(shù)上:

所返回的函數(shù),(內(nèi)部)叫做withdraw,和定義所在位置即make_withdraw的局部環(huán)境相關(guān)聯(lián)。名稱balance在這個(gè)局部環(huán)境中綁定。在例子的剩余部分中,balance名稱只有這一個(gè)綁定,這非常重要。

下面,我們求出以總數(shù)5調(diào)用withdraw的表達(dá)式的值:

>>> wd(5)
15

名稱wd綁定到了withdraw函數(shù)上,所以withdraw的函數(shù)體在新的環(huán)境中求值,新的環(huán)境擴(kuò)展自withdraw定義所在的環(huán)境。跟蹤withdraw求值的效果展示了 Python 中nonlocal語(yǔ)句的效果。

withdraw的賦值語(yǔ)句通常在withdraw的局部幀中為balance創(chuàng)建新的綁定。由于nonlocal語(yǔ)句,賦值運(yùn)算找到了balance定義位置的第一幀,并在那里重新綁定名稱。如果balance之前沒(méi)有綁定到值上,那么nonlocal語(yǔ)句會(huì)產(chǎn)生錯(cuò)誤。

通過(guò)修改balance綁定的行為,我們也修改了withdraw函數(shù)。下次withdraw調(diào)用的時(shí)候,名稱balance會(huì)求值為15而不是20。

當(dāng)我們第二次調(diào)用wd時(shí),

>>> wd(3)
12

我們發(fā)現(xiàn)綁定到balance的值的修改可在兩個(gè)調(diào)用之間積累。

這里,第二次調(diào)用withdraw會(huì)創(chuàng)建第二個(gè)局部幀,像之前一樣,但是,withdraw的兩個(gè)幀都擴(kuò)展自make_withdraw的環(huán)境,它們都包含balance的綁定。所以,它們共享特定的名稱綁定,調(diào)用withdraw具有改變環(huán)境的副作用,并且會(huì)由之后的withdraw調(diào)用繼承。

實(shí)踐指南。通過(guò)引入nonlocal語(yǔ)句,我們發(fā)現(xiàn)了賦值語(yǔ)句的雙重作用。它們修改局部綁定,或者修改非局部綁定。實(shí)際上,賦值語(yǔ)句已經(jīng)有了兩個(gè)作用:創(chuàng)建新的綁定,或者重新綁定現(xiàn)有名稱。Python 賦值的許多作用使賦值語(yǔ)句的執(zhí)行效果變得模糊。作為一個(gè)程序員,你應(yīng)該用文檔清晰記錄你的代碼,使賦值的效果可被其它人理解。

2.4.2 非局部賦值的好處

非局部賦值是將程序作為獨(dú)立和自主的對(duì)象觀察的重要步驟,對(duì)象彼此交互,但是各自管理各自的內(nèi)部狀態(tài)。

特別地,非局部賦值提供了在函數(shù)的局部范圍中維護(hù)一些狀態(tài)的能力,這些狀態(tài)會(huì)在函數(shù)之后的調(diào)用中演化。和特定withdraw函數(shù)相關(guān)的balance在所有該函數(shù)的調(diào)用中共享。但是,withdraw實(shí)例中的balance綁定對(duì)程序的其余部分不可見(jiàn)。只有withdraw關(guān)聯(lián)到了make_withdraw的幀,withdraw在那里被定義。如果make_withdraw再次調(diào)用,它會(huì)創(chuàng)建多帶帶的幀,帶有多帶帶的balance綁定。

我們可以繼續(xù)以我們的例子來(lái)展示這個(gè)觀點(diǎn)。make_withdraw的第二個(gè)調(diào)用返回了第二個(gè)withdraw函數(shù),它關(guān)聯(lián)到了另一個(gè)環(huán)境上。

>>> wd2 = make_withdraw(7)

第二個(gè)withdraw函數(shù)綁定到了全局幀的名稱wd2上。我們使用星號(hào)來(lái)省略了表示這個(gè)綁定的線?,F(xiàn)在,我們看到實(shí)際上有兩個(gè)balance的綁定。名稱wd仍舊綁定到余額為12withdraw函數(shù)上,而wd2綁定到了余額為7的新的withdraw函數(shù)上。

最后,我們調(diào)用綁定到wd2上的第二個(gè)withdraw函數(shù):

>>> wd2(6)
1

這個(gè)調(diào)用修改了非局部名稱balance的綁定,但是不影響在全局幀中綁定到名稱wd的第一個(gè)withdraw。

這樣,withdraw的每個(gè)實(shí)例都維護(hù)它自己的余額狀態(tài),但是這個(gè)狀態(tài)對(duì)程序中其它函數(shù)不可見(jiàn)。在更高層面上觀察這個(gè)情況,我們創(chuàng)建了銀行賬戶的抽象,它管理自己的內(nèi)部狀態(tài),但以一種方式對(duì)真實(shí)世界的賬戶進(jìn)行建模:它基于自己的歷史提取請(qǐng)求來(lái)隨時(shí)間變化。

2.4.3 非局部賦值的代價(jià)

我們擴(kuò)展了我們的計(jì)算環(huán)境模型,用于解釋非局部賦值的效果。但是,非局部復(fù)制與我們思考名稱和值的方式有一些細(xì)微差異。

之前,我們的值并沒(méi)有改變,僅僅是我們的名稱和綁定發(fā)生了變化。當(dāng)兩個(gè)名稱ab綁定到4上時(shí),它們綁定到了相同的4還是不同的4并不重要。我們說(shuō),只有一個(gè)4對(duì)象,并且它永不會(huì)改變。

但是,帶有狀態(tài)的函數(shù)不是這樣的。當(dāng)兩個(gè)名稱wdwd2都綁定到withdraw函數(shù)時(shí),它們綁定到相同函數(shù)還是函數(shù)的兩個(gè)不同實(shí)例,就很重要了??紤]下面的例子,它與我們之前分析的那個(gè)正好相反:

>>> wd = make_withdraw(12)
>>> wd2 = wd
>>> wd2(1)
11
>>> wd(1)
10

這里,通過(guò)wd2調(diào)用函數(shù)會(huì)修改名稱為wd的函數(shù)的值,因?yàn)閮蓚€(gè)名稱都指向相同的函數(shù)。這些語(yǔ)句執(zhí)行之后的環(huán)境圖示展示了這個(gè)現(xiàn)象:

兩個(gè)名稱指向同一個(gè)值在世界上不常見(jiàn),但我們程序中就是這樣。但是,由于值會(huì)隨時(shí)間改變,我們必須非常仔細(xì)來(lái)理解其它名稱上的變化效果,它們可能指向這些值。

正確分析帶有非局部賦值代碼的關(guān)鍵是,記住只有函數(shù)調(diào)用可以創(chuàng)建新的幀。賦值語(yǔ)句始終改變現(xiàn)有幀中的綁定。這里,除非make_withdraw調(diào)用了兩次,balance還是只有一個(gè)綁定。

變與不變。這些細(xì)微差別出現(xiàn)的原因是,通過(guò)引入修改非局部環(huán)境的非純函數(shù),我們改變了表達(dá)式的本質(zhì)。只含有純函數(shù)的表達(dá)式是引用透明(referentially transparent)的。如果我們將它的子表達(dá)式換成子表達(dá)式的值,它的值不會(huì)改變。

重新綁定的操作違反了引用透明的條件,因?yàn)樗鼈儾粌H僅返回一個(gè)值。它們修改了環(huán)境。當(dāng)我們引入任意重綁定的時(shí)候,我們就會(huì)遇到一個(gè)棘手的認(rèn)識(shí)論問(wèn)題:它對(duì)于兩個(gè)相同的值意味著什么。在我們的計(jì)算環(huán)境模型中,兩個(gè)分別定義的函數(shù)并不是相同的,因?yàn)槠渲幸粋€(gè)的改變并不影響另一個(gè)。

通常,只要我們不會(huì)修改數(shù)據(jù)對(duì)象,我們就可以將復(fù)合數(shù)據(jù)對(duì)象看做其部分的總和。例如,有理數(shù)可以通過(guò)提供分子和分母來(lái)確定。但是這個(gè)觀點(diǎn)在變化出現(xiàn)時(shí)不再成立了,其中復(fù)合數(shù)據(jù)對(duì)象擁有一個(gè)“身份”,不同于組成它的各個(gè)部分。即使我們通過(guò)取錢來(lái)修改了余額,某個(gè)銀行賬戶還是“相同”的銀行賬戶。相反,我們可以讓兩個(gè)銀行賬戶碰巧具有相同的余額,但它們是不同的對(duì)象。

盡管它引入了新的困難,非局部賦值是個(gè)創(chuàng)建模塊化編程的強(qiáng)大工具,程序的不同部分,對(duì)應(yīng)不同的環(huán)境幀,可以在程序執(zhí)行中獨(dú)立演化。而且,使用帶有局部狀態(tài)的函數(shù),我們就能實(shí)現(xiàn)可變數(shù)據(jù)類型。在這一節(jié)的剩余部分,我們介紹了一些最實(shí)用的 Python 內(nèi)建數(shù)據(jù)類型,以及使用帶有非局部賦值的函數(shù),來(lái)實(shí)現(xiàn)這些數(shù)據(jù)類型的一些方法。

2.4.4 列表

list是 Python 中最使用和靈活的洗了類型。列表類似于元組,但是它是可變的。方法調(diào)用和賦值語(yǔ)句都可以修改列表的內(nèi)容。

我們可以通過(guò)一個(gè)展示(極大簡(jiǎn)化的)撲克牌歷史的例子,來(lái)介紹許多列表編輯操作。例子中的注釋描述了每個(gè)方法的效果。

撲克牌發(fā)明于中國(guó),大概在 9 世紀(jì)。早期的牌組中有三個(gè)花色,它們對(duì)應(yīng)錢的三個(gè)面額。

>>> chinese_suits = ["coin", "string", "myriad"]  # A list literal
>>> suits = chinese_suits                         # Two names refer to the same list

撲克牌傳到歐洲(也可能通過(guò)埃及)之后,西班牙的牌組(oro)中之只保留了硬幣的花色。

>>> suits.pop()             # Removes and returns the final element
"myriad"
>>> suits.remove("string")  # Removes the first element that equals the argument

然后又添加了三個(gè)新的花色(它們的設(shè)計(jì)和名稱隨時(shí)間而演化),

>>> suits.append("cup")              # Add an element to the end
>>> suits.extend(["sword", "club"])  # Add all elements of a list to the end

意大利人把劍叫做“黑桃”:

>>> suits[2] = "spade"  # Replace an element

下面是傳統(tǒng)的意大利牌組:

>>> suits
["coin", "cup", "spade", "club"]

我們現(xiàn)在在美國(guó)使用的法式變體修改了前兩個(gè):

>>> suits[0:2] = ["heart", "diamond"]  # Replace a slice
>>> suits
["heart", "diamond", "spade", "club"]

也存在用于插入、排序和反轉(zhuǎn)列表的操作。所有這些修改操作都改變了列表的值,它們并不創(chuàng)建新的列表對(duì)象。

共享和身份。由于我們修改了一個(gè)列表,而不是創(chuàng)建新的列表,綁定到名稱chinese_suits上的對(duì)象也改變了,因?yàn)樗c綁定到suits上的對(duì)象是相同的列表對(duì)象。

>>> chinese_suits  # This name co-refers with "suits" to the same list
["heart", "diamond", "spade", "club"]

列表可以使用list構(gòu)造函數(shù)來(lái)復(fù)制。其中一個(gè)的改變不會(huì)影響另一個(gè),除非它們共享相同的結(jié)構(gòu)。

>>> nest = list(suits)  # Bind "nest" to a second list with the same elements
>>> nest[0] = suits     # Create a nested list

在最后的賦值之后,我們只剩下下面的環(huán)境,其中列表使用盒子和指針的符號(hào)來(lái)表示:

根據(jù)這個(gè)環(huán)境,修改由suites指向的列表會(huì)影響nest第一個(gè)元素的嵌套列表,但是不會(huì)影響其他元素:

>>> suits.insert(2, "Joker")  # Insert an element at index 2, shifting the rest
>>> nest
[["heart", "diamond", "Joker", "spade", "club"], "diamond", "spade", "club"]

與之類似,在next的第一個(gè)元素上撤銷這個(gè)修改也會(huì)影響到suit。

由于這個(gè)pop方法的調(diào)用,我們返回到了上面描述的環(huán)境。

由于兩個(gè)列表具有相同內(nèi)容,但是實(shí)際上是不同的列表,我們需要一種手段來(lái)測(cè)試兩個(gè)對(duì)象是否相同。Python 引入了兩個(gè)比較運(yùn)算符,叫做isis not,測(cè)試了兩個(gè)表達(dá)式實(shí)際上是否求值為同一個(gè)對(duì)象。如果兩個(gè)對(duì)象的當(dāng)前值相等,并且一個(gè)對(duì)象的改變始終會(huì)影響另一個(gè),那么兩個(gè)對(duì)象是同一個(gè)對(duì)象。身份是個(gè)比相等性更強(qiáng)的條件。

譯者注:兩個(gè)對(duì)象當(dāng)且僅當(dāng)在內(nèi)存中的位置相同時(shí)為同一個(gè)對(duì)象。CPython 的實(shí)現(xiàn)直接比較對(duì)象的地址來(lái)確定。

>>> suits is nest[0]
True
>>> suits is ["heart", "diamond", "spade", "club"]
False
>>> suits == ["heart", "diamond", "spade", "club"]
True

最后的兩個(gè)比較展示了is==的區(qū)別,前者檢查身份,而后者檢查內(nèi)容的相等性。

列表推導(dǎo)式。列表推導(dǎo)式使用擴(kuò)展語(yǔ)法來(lái)創(chuàng)建列表,與生成器表達(dá)式的語(yǔ)法相似。

例如,unicodedata模塊跟蹤了 Unicode 字母表中每個(gè)字符的官方名稱。我們可以查找與名稱對(duì)應(yīng)的字符,包含這些卡牌花色的字符。

>>> from unicodedata import lookup
>>> [lookup("WHITE " + s.upper() + " SUIT") for s in suits]
["?", "?", "?", "?"]

列表推導(dǎo)式使用序列的接口約定增強(qiáng)了數(shù)據(jù)處理的范式,因?yàn)榱斜硎且环N序列數(shù)據(jù)類型。

擴(kuò)展閱讀。Dive Into Python 3 的推導(dǎo)式一章包含了一些示例,展示了如何使用 Python 瀏覽計(jì)算機(jī)的文件系統(tǒng)。這一章介紹了os模塊,它可以列出目錄的內(nèi)容。這個(gè)材料并不是這門課的一部分,但是推薦給任何想要增加 Python 知識(shí)和技巧的人。

實(shí)現(xiàn)。列表是序列,就像元組一樣。Python 語(yǔ)言并不提供給我們列表實(shí)現(xiàn)的直接方法,只提供序列抽象,和我們?cè)谶@一節(jié)介紹的可變方法。為了克服這一語(yǔ)言層面的抽象界限,我們可以開(kāi)發(fā)列表的函數(shù)式實(shí)現(xiàn),再次使用遞歸表示。這一節(jié)也有第二個(gè)目的:加深我們對(duì)調(diào)度函數(shù)的理解。

我們會(huì)將列表實(shí)現(xiàn)為函數(shù),它將一個(gè)遞歸列表作為自己的局部狀態(tài)。列表需要有一個(gè)身份,就像任何可變值那樣。特別地,我們不能使用None來(lái)表示任何空的可變列表,因?yàn)閮蓚€(gè)空列表并不是相同的值(例如,向一個(gè)列表添加元素并不會(huì)添加到另一個(gè)),但是None is None。另一方面,兩個(gè)不同的函數(shù)足以區(qū)分兩個(gè)兩個(gè)空列表,它們都將empty_rlist作為局部狀態(tài)。

我們的可變列表是個(gè)調(diào)度函數(shù),就像我們偶對(duì)的函數(shù)式實(shí)現(xiàn)也是個(gè)調(diào)度函數(shù)。它檢查輸入“信息”是否為已知信息,并且對(duì)每個(gè)不同的輸入執(zhí)行相應(yīng)的操作。我們的可變列表可響應(yīng)五個(gè)不同的信息。前兩個(gè)實(shí)現(xiàn)了序列抽象的行為。接下來(lái)的兩個(gè)添加或刪除列表的第一個(gè)元素。最后的信息返回整個(gè)列表內(nèi)容的字符串表示。

>>> def make_mutable_rlist():
        """Return a functional implementation of a mutable recursive list."""
        contents = empty_rlist
        def dispatch(message, value=None):
            nonlocal contents
            if message == "len":
                return len_rlist(contents)
            elif message == "getitem":
                return getitem_rlist(contents, value)
            elif message == "push_first":
                contents = make_rlist(value, contents)
            elif message == "pop_first":
                f = first(contents)
                contents = rest(contents)
                return f
            elif message == "str":
                return str(contents)
        return dispatch

我們也可以添加一個(gè)輔助函數(shù),來(lái)從任何內(nèi)建序列中構(gòu)建函數(shù)式實(shí)現(xiàn)的遞歸列表。只需要以遞歸順序添加每個(gè)元素。

>>> def to_mutable_rlist(source):
        """Return a functional list with the same contents as source."""
        s = make_mutable_rlist()
        for element in reversed(source):
            s("push_first", element)
        return s

在上面的定義中,函數(shù)reversed接受并返回可迭代值。它是使用序列的接口約定的另一個(gè)示例。

這里,我們可以構(gòu)造函數(shù)式實(shí)現(xiàn)的列表,要注意列表自身也是個(gè)函數(shù)。

>>> s = to_mutable_rlist(suits)
>>> type(s)

>>> s("str")
"("heart", ("diamond", ("spade", ("club", None))))"

另外,我們可以像列表s傳遞信息來(lái)修改它的內(nèi)容,比如移除第一個(gè)元素。

>>> s("pop_first")
"heart"
>>> s("str")
"("diamond", ("spade", ("club", None)))"

原則上,操作push_firstpop_first足以對(duì)列表做任意修改。我們總是可以清空整個(gè)列表,之后將它舊的內(nèi)容替換為想要的結(jié)果。

消息傳遞。給予一些時(shí)間,我們就能實(shí)現(xiàn)許多實(shí)用的 Python 列表可變操作,比如extendinsert。我們有一個(gè)選擇:我們可以將它們?nèi)繉?shí)現(xiàn)為函數(shù),這會(huì)使用現(xiàn)有的消息pop_firstpush_first來(lái)實(shí)現(xiàn)所有的改變操作。作為代替,我們也可以向dispatch函數(shù)體添加額外的elif子句,每個(gè)子句檢查一個(gè)消息(例如"extend"),并且直接在contents上做出合適的改變。

第二個(gè)途徑叫做消息傳遞,它把數(shù)據(jù)值上面所有操作的邏輯封裝在一個(gè)函數(shù)中,這個(gè)函數(shù)響應(yīng)不同的消息。一個(gè)使用消息傳遞的程序定義了調(diào)度函數(shù),每個(gè)函數(shù)都擁有局部狀態(tài),通過(guò)傳遞“消息”作為第一個(gè)參數(shù)給這些函數(shù)來(lái)組織計(jì)算。消息是對(duì)應(yīng)特定行為的字符串。

可以想象,在dispatch的函數(shù)體中通過(guò)名稱來(lái)枚舉所有這些消息非常無(wú)聊,并且易于出現(xiàn)錯(cuò)誤。Python 的字典提供了一種數(shù)據(jù)類型,會(huì)幫助我們管理消息和操作之間的映射,它會(huì)在下一節(jié)中介紹。

2.4.5 字典

字典是 Python 內(nèi)建數(shù)據(jù)類型,用于儲(chǔ)存和操作對(duì)應(yīng)關(guān)系。字典包含了鍵值對(duì),其中鍵和值都可以是對(duì)象。字典的目的是提供一種抽象,用于儲(chǔ)存和獲取下標(biāo)不是連續(xù)整數(shù),而是描述性的鍵的值。

字符串通常用作鍵,因?yàn)樽址ǔS糜诒硎臼挛锩Q。這個(gè)字典字面值提供了不同羅馬數(shù)字的值。

>>> numerals = {"I": 1.0, "V": 5, "X": 10}

我們可以使用元素選擇運(yùn)算符,來(lái)通過(guò)鍵查找值,我們之前將其用于序列。

>>> numerals["X"]
10

字典的每個(gè)鍵最多只能擁有一個(gè)值。添加新的鍵值對(duì)或者修改某個(gè)鍵的已有值,可以使用賦值運(yùn)算符來(lái)完成。

>>> numerals["I"] = 1
>>> numerals["L"] = 50
>>> numerals
{"I": 1, "X": 10, "L": 50, "V": 5}

要注意,"L"并沒(méi)有添加到上面輸出的末尾。字典是無(wú)序的鍵值對(duì)集合。當(dāng)我們打印字典時(shí),鍵和值都以某種順序來(lái)渲染,但是對(duì)語(yǔ)言的用戶來(lái)說(shuō),不應(yīng)假設(shè)順序總是這樣。

字典抽象也支持多種方法,來(lái)從整體上迭代字典中的內(nèi)容。方法keys、valuesitems都返回可迭代的值。

>>> sum(numerals.values())
66

通過(guò)調(diào)用dict構(gòu)造函數(shù),鍵值對(duì)的列表可以轉(zhuǎn)換為字典。

>>> dict([(3, 9), (4, 16), (5, 25)])
{3: 9, 4: 16, 5: 25}

字典也有一些限制:

字典的鍵不能是可變內(nèi)建類型的對(duì)象。

一個(gè)給定的鍵最多只能有一個(gè)值。

第一條限制被綁定到了 Python 中字典的底層實(shí)現(xiàn)上。這個(gè)實(shí)現(xiàn)的細(xì)節(jié)并不是這門課的主題。直覺(jué)上,鍵告訴了 Python 應(yīng)該在內(nèi)存中的哪里尋找鍵值對(duì);如果鍵發(fā)生改變,鍵值對(duì)就會(huì)丟失。

第二個(gè)限制是字典抽象的結(jié)果,它為儲(chǔ)存和獲取某個(gè)鍵的值而設(shè)計(jì)。如果字典中最多只存在一個(gè)這樣的值,我們只能獲取到某個(gè)鍵的一個(gè)值。

由字典實(shí)現(xiàn)的一個(gè)實(shí)用方法是get,如果鍵存在的話,它返回鍵的值,否則返回一個(gè)默認(rèn)值。get的參數(shù)是鍵和默認(rèn)值。

>>> numerals.get("A", 0)
0
>>> numerals.get("V", 0)
5

字典也擁有推導(dǎo)式語(yǔ)法,和列表和生成器表達(dá)式類似。求解字典推導(dǎo)式會(huì)產(chǎn)生新的字典對(duì)象。

>>> {x: x*x for x in range(3,6)}
{3: 9, 4: 16, 5: 25}

實(shí)現(xiàn)。我們可以實(shí)現(xiàn)一個(gè)抽象數(shù)據(jù)類型,它是一個(gè)記錄的列表,與字典抽象一致。每個(gè)記錄都是兩個(gè)元素的列表,包含鍵和相關(guān)的值。

>>> def make_dict():
        """Return a functional implementation of a dictionary."""
        records = []
        def getitem(key):
            for k, v in records:
                if k == key:
                    return v
        def setitem(key, value):
            for item in records:
                if item[0] == key:
                    item[1] = value
                    return
            records.append([key, value])
        def dispatch(message, key=None, value=None):
            if message == "getitem":
                return getitem(key)
            elif message == "setitem":
                setitem(key, value)
            elif message == "keys":
                return tuple(k for k, _ in records)
            elif message == "values":
                return tuple(v for _, v in records)
        return dispatch

同樣,我們使用了傳遞方法的消息來(lái)組織我們的實(shí)現(xiàn)。我們已經(jīng)支持了四種消息:getitem、setitem、keysvalues。要查找某個(gè)鍵的值,我們可以迭代這些記錄來(lái)尋找一個(gè)匹配的鍵。要插入某個(gè)鍵的值,我們可以迭代整個(gè)記錄來(lái)觀察是否已經(jīng)存在帶有這個(gè)鍵的記錄。如果沒(méi)有,我們會(huì)構(gòu)造一條新的記錄。如果已經(jīng)有了帶有這個(gè)鍵的記錄,我們將這個(gè)記錄的值設(shè)為新的值。

我們現(xiàn)在可以使用我們的實(shí)現(xiàn)來(lái)儲(chǔ)存和獲取值。

>>> d = make_dict()
>>> d("setitem", 3, 9)
>>> d("setitem", 4, 16)
>>> d("getitem", 3)
9
>>> d("getitem", 4)
16
>>> d("keys")
(3, 4)
>>> d("values")
(9, 16)

這個(gè)字典實(shí)現(xiàn)并不為快速的記錄檢索而優(yōu)化,因?yàn)槊總€(gè)響應(yīng)getitem消息都必須迭代整個(gè)records列表。內(nèi)建的字典類型更加高效。

2.4.6 示例:傳播約束

可變數(shù)據(jù)允許我們模擬帶有變化的系統(tǒng),也允許我們構(gòu)建新的抽象類型。在這個(gè)延伸的實(shí)例中,我們組合了非局部賦值、列表和字典來(lái)構(gòu)建一個(gè)基于約束的系統(tǒng),支持多個(gè)方向上的計(jì)算。將程序表達(dá)為約束是一種聲明式編程,其中程序員聲明需要求解的問(wèn)題結(jié)構(gòu),但是抽象了問(wèn)題解決方案如何計(jì)算的細(xì)節(jié)。

計(jì)算機(jī)程序通常組織為單方向的計(jì)算,它在預(yù)先設(shè)定的參數(shù)上執(zhí)行操作,來(lái)產(chǎn)生合理的輸出。另一方面,我們通常希望根據(jù)數(shù)量上的關(guān)系對(duì)系統(tǒng)建模。例如,我們之前考慮過(guò)理想氣體定律,它通過(guò)波爾茲曼常數(shù)k關(guān)聯(lián)了理想氣體的氣壓p,體積v,數(shù)量n以及溫度t

p * v = n * k * t

這樣一個(gè)方程并不是單方向的。給定任何四個(gè)數(shù)量,我們可以使用這個(gè)方程來(lái)計(jì)算第五個(gè)。但將這個(gè)方程翻譯為某種傳統(tǒng)的計(jì)算機(jī)語(yǔ)言會(huì)強(qiáng)迫我們選擇一個(gè)數(shù)量,根據(jù)其余四個(gè)計(jì)算出來(lái)。所以計(jì)算氣壓的函數(shù)應(yīng)該不能用于計(jì)算溫度,即使二者的計(jì)算通過(guò)相同的方程完成。

這一節(jié)中,我們從零開(kāi)始設(shè)計(jì)線性計(jì)算的通用模型。我們定義了數(shù)量之間的基本約束,例如adder(a, b, c)會(huì)嚴(yán)格保證數(shù)學(xué)關(guān)系a + b = c。

我們也定義了組合的手段,使基本約束可以被組合來(lái)表達(dá)更復(fù)雜的關(guān)系。這樣,我們的程序就像一種編程語(yǔ)言。我們通過(guò)構(gòu)造網(wǎng)絡(luò)來(lái)組合約束,其中約束由連接器連接。連接器是一種對(duì)象,它“持有”一個(gè)值,并且可能會(huì)參與一個(gè)或多個(gè)約束。

例如,我們知道華氏和攝氏溫度的關(guān)系是:

9 * c = 5 * (f - 32)

這個(gè)等式是cf之間的復(fù)雜約束。這種約束可以看做包含adder、multipliercontant約束的網(wǎng)絡(luò)。

這張圖中,我們可以看到,左邊是一個(gè)帶有三個(gè)終端的乘法器盒子,標(biāo)記為a,bc。它們將乘法器連接到網(wǎng)絡(luò)剩余的部分:終端a鏈接到了連接器celsius上,它持有攝氏溫度。終端b鏈接到了連接器w上,w也鏈接到持有9的盒子上。終端c,被乘法器盒子約束為ab的乘積,鏈接到另一個(gè)乘法器盒子上,它的b鏈接到常數(shù)5上,以及它的a連接到了求和約束的一項(xiàng)上。

這個(gè)網(wǎng)絡(luò)上的計(jì)算會(huì)如下進(jìn)行:當(dāng)連接器被提供一個(gè)值時(shí)(被用戶或被鏈接到它的約束器),它會(huì)喚醒所有相關(guān)的約束(除了剛剛喚醒的約束)來(lái)通知它們它得到了一個(gè)值。每個(gè)喚醒的約束之后會(huì)調(diào)查它的連接器,來(lái)看看是否有足夠的信息來(lái)為連接器求出一個(gè)值。如果可以,盒子會(huì)設(shè)置這個(gè)連接器,連接器之后會(huì)喚醒所有相關(guān)的約束,以此類推。例如,在攝氏溫度和華氏溫度的轉(zhuǎn)換中,w、xy會(huì)被常量盒子9532立即設(shè)置。連接器會(huì)喚醒乘法器和加法器,它們判斷出沒(méi)有足夠的信息用于處理。如果用戶(或者網(wǎng)絡(luò)的其它部分)將celsis連接器設(shè)置為某個(gè)值(比如25),最左邊的乘法器會(huì)被喚醒,之后它會(huì)將u設(shè)置為25 * 9 = 225。之后u會(huì)喚醒第二個(gè)乘法器,它會(huì)將v設(shè)置為45,之后v會(huì)喚醒加法器,它將fahrenheit連接器設(shè)置為77。

使用約束系統(tǒng)。為了使用約束系統(tǒng)來(lái)計(jì)算出上面所描述的溫度計(jì)算,我們首先創(chuàng)建了兩個(gè)具名連接器,celsiusfahrenheit,通過(guò)調(diào)用make_connector構(gòu)造器。

>>> celsius = make_connector("Celsius")
>>> fahrenheit = make_connector("Fahrenheit")

之后,我們將這些連接器鏈接到網(wǎng)絡(luò)中,這個(gè)網(wǎng)絡(luò)反映了上面的圖示。函數(shù)make_converter組裝了網(wǎng)絡(luò)中不同的連接器和約束:

>>> def make_converter(c, f):
        """Connect c to f with constraints to convert from Celsius to Fahrenheit."""
        u, v, w, x, y = [make_connector() for _ in range(5)]
        multiplier(c, w, u)
        multiplier(v, x, u)
        adder(v, y, f)
        constant(w, 9)
        constant(x, 5)
        constant(y, 32)
>>> make_converter(celsius, fahrenheit)

我們會(huì)使用消息傳遞系統(tǒng)來(lái)協(xié)調(diào)約束和連接器。我們不會(huì)使用函數(shù)來(lái)響應(yīng)消息,而是使用字典。用于分發(fā)的字典擁有字符串類型的鍵,代表它接受的消息。這些鍵關(guān)聯(lián)的值是這些消息的響應(yīng)。

約束是不帶有局部狀態(tài)的字典。它們對(duì)消息的響應(yīng)是非純函數(shù),這些函數(shù)會(huì)改變所約束的連接器。

連接器是一個(gè)字典,持有當(dāng)前值并響應(yīng)操作該值的消息。約束不會(huì)直接改變連接器的值,而是會(huì)通過(guò)發(fā)送消息來(lái)改變,于是連接器可以提醒其他約束來(lái)響應(yīng)變化。這樣,連接器代表了一個(gè)數(shù)值,同時(shí)封裝了連接器的行為。

我們可以發(fā)送給連接器的一種消息是設(shè)置它的值。這里,我們("user")將celsius的值設(shè)置為25

>>> celsius["set_val"]("user", 25)
Celsius = 25
Fahrenheit = 77.0

不僅僅是celsius的值變成了25,它的值也在網(wǎng)絡(luò)上傳播,于是fahrenheit的值也發(fā)生變化。這些變化打印了出來(lái),因?yàn)槲覀冊(cè)跇?gòu)造這兩個(gè)連接器的時(shí)候命名了它們。

現(xiàn)在我們可以試著將fahrenheit設(shè)置為新的值,比如212。

>>> fahrenheit["set_val"]("user", 212)
Contradiction detected: 77.0 vs 212

連接器報(bào)告說(shuō),它察覺(jué)到了一個(gè)矛盾:它的值是77.0,但是有人嘗試將其設(shè)置為212。如果我們真的想以新的值復(fù)用這個(gè)網(wǎng)絡(luò),我們可以讓celsius忘掉舊的值。

>>> celsius["forget"]("user")
Celsius is forgotten
Fahrenheit is forgotten

連接器celsius發(fā)現(xiàn)了user,一開(kāi)始設(shè)置了它的值,現(xiàn)在又想撤銷這個(gè)值,所以celsius同意丟掉這個(gè)值,并且通知了網(wǎng)絡(luò)的其余部分。這個(gè)消息最終傳播給fahrenheit,它現(xiàn)在發(fā)現(xiàn)沒(méi)有理由繼續(xù)相信自己的值為77。于是,它也丟掉了它的值。

現(xiàn)在fahrenheit沒(méi)有值了,我們就可以將其設(shè)置為212

>>> fahrenheit["set_val"]("user", 212)
Fahrenheit = 212
Celsius = 100.0

這個(gè)新值在網(wǎng)絡(luò)上傳播,并強(qiáng)迫celsius持有值100。我們已經(jīng)使用了非常相似的網(wǎng)絡(luò),提供fahrenheit來(lái)計(jì)算celsius,以及提供celsius來(lái)計(jì)算fahrenheit。這個(gè)無(wú)方向的計(jì)算就是基于約束的網(wǎng)絡(luò)的特征。

實(shí)現(xiàn)約束系統(tǒng)。像我們看到的那樣,連接器是字典,將消息名稱映射為函數(shù)和數(shù)據(jù)值。我們將要實(shí)現(xiàn)響應(yīng)下列消息的連接器:

connector["set_val"](source, value) 表示source請(qǐng)求連接器將當(dāng)前值設(shè)置為該值。

connector["has_val"]() 返回連接器是否已經(jīng)有了一個(gè)值。

connector["val"] 是連接器的當(dāng)前值。

connector["forget"](source) 告訴連接器,source請(qǐng)求它忘掉當(dāng)前值。

connector["connect"](source) 告訴連接器參與新的約束source。

約束也是字典,接受來(lái)自連接器的以下兩種消息:

constraint["new_val"]() 表示連接到約束的連接器有了新的值。

constraint["forget"]() 表示連接到約束的連接器需要忘掉它的值。

當(dāng)約束收到這些消息時(shí),它們適當(dāng)?shù)貙⑺鼈儌鞑ソo其它連接器。

adder函數(shù)在兩個(gè)連接器上構(gòu)造了加法器約束,其中前兩個(gè)連接器必須加到第三個(gè)上:a + b = c。為了支持多方向的約束傳播,加法器必須也規(guī)定從c中減去a會(huì)得到b,或者從c中減去b會(huì)得到a。

>>> from operator import add, sub
>>> def adder(a, b, c):
        """The constraint that a + b = c."""
        return make_ternary_constraint(a, b, c, add, sub, sub)

我們希望實(shí)現(xiàn)一個(gè)通用的三元(三個(gè)方向)約束,它使用三個(gè)連接器和三個(gè)函數(shù)來(lái)創(chuàng)建約束,接受new_valforget消息。消息的響應(yīng)是局部函數(shù),它放在叫做constraint的字典中。

>>> def make_ternary_constraint(a, b, c, ab, ca, cb):
        """The constraint that ab(a,b)=c and ca(c,a)=b and cb(c,b) = a."""
        def new_value():
            av, bv, cv = [connector["has_val"]() for connector in (a, b, c)]
            if av and bv:
                c["set_val"](constraint, ab(a["val"], b["val"]))
            elif av and cv:
                b["set_val"](constraint, ca(c["val"], a["val"]))
            elif bv and cv:
                a["set_val"](constraint, cb(c["val"], b["val"]))
        def forget_value():
            for connector in (a, b, c):
                connector["forget"](constraint)
        constraint = {"new_val": new_value, "forget": forget_value}
        for connector in (a, b, c):
            connector["connect"](constraint)
        return constraint

叫做constraint的字典是個(gè)分發(fā)字典,也是約束對(duì)象自身。它響應(yīng)兩種約束接收到的消息,也在對(duì)連接器的調(diào)用中作為source參數(shù)傳遞。

無(wú)論約束什么時(shí)候被通知,它的連接器之一擁有了值,約束的局部函數(shù)new_value都會(huì)被調(diào)用。這個(gè)函數(shù)首先檢查是否ab都擁有值,如果是這樣,它告訴c將值設(shè)為函數(shù)ab的返回值,在adder中是add。約束,也就是adder對(duì)象,將自身作為source參數(shù)傳遞給連接器。如果ab不同時(shí)擁有值,約束會(huì)檢查ac,以此類推。

如果約束被通知,連接器之一忘掉了它的值,它會(huì)請(qǐng)求所有連接器忘掉它們的值(只有由約束設(shè)置的值會(huì)被真正丟掉)。

multiplieradder類似:

>>> from operator import mul, truediv
>>> def multiplier(a, b, c):
        """The constraint that a * b = c."""
        return make_ternary_constraint(a, b, c, mul, truediv, truediv)

常量也是約束,但是它不會(huì)發(fā)送任何消息,因?yàn)樗话粋€(gè)單一的連接器,在構(gòu)造的時(shí)候會(huì)設(shè)置它。

>>> def constant(connector, value):
        """The constraint that connector = value."""
        constraint = {}
        connector["set_val"](constraint, value)
        return constraint

這三個(gè)約束足以實(shí)現(xiàn)我們的溫度轉(zhuǎn)換網(wǎng)絡(luò)。

表示連接器。連接器表示為包含一個(gè)值的字典,但是同時(shí)擁有帶有局部狀態(tài)的響應(yīng)函數(shù)。連接器必須跟蹤向它提供當(dāng)前值的informant,以及它所參與的constraints列表。

構(gòu)造器make_connector是局部函數(shù),用于設(shè)置和忘掉值,它響應(yīng)來(lái)自約束的消息。

>>> def make_connector(name=None):
        """A connector between constraints."""
        informant = None
        constraints = []
        def set_value(source, value):
            nonlocal informant
            val = connector["val"]
            if val is None:
                informant, connector["val"] = source, value
                if name is not None:
                    print(name, "=", value)
                inform_all_except(source, "new_val", constraints)
            else:
                if val != value:
                    print("Contradiction detected:", val, "vs", value)
        def forget_value(source):
            nonlocal informant
            if informant == source:
                informant, connector["val"] = None, None
                if name is not None:
                    print(name, "is forgotten")
                inform_all_except(source, "forget", constraints)
        connector = {"val": None,
                     "set_val": set_value,
                     "forget": forget_value,
                     "has_val": lambda: connector["val"] is not None,
                     "connect": lambda source: constraints.append(source)}
        return connector

同時(shí),連接器是一個(gè)分發(fā)字典,用于分發(fā)五個(gè)消息,約束使用它們來(lái)和連接器通信。前四個(gè)響應(yīng)都是函數(shù),最后一個(gè)響應(yīng)就是值本身。

局部函數(shù)set_value在請(qǐng)求設(shè)置連接器的值時(shí)被調(diào)用。如果連接器當(dāng)前并沒(méi)有值,它會(huì)設(shè)置該值并將informant記為請(qǐng)求設(shè)置該值的source約束。之后連接器會(huì)提醒所有參與的約束,除了請(qǐng)求設(shè)置該值的約束。這通過(guò)使用下列迭代函數(shù)來(lái)完成。

>>> def inform_all_except(source, message, constraints):
        """Inform all constraints of the message, except source."""
        for c in constraints:
            if c != source:
                c[message]()

如果一個(gè)連接器被請(qǐng)求忘掉它的值,它會(huì)調(diào)用局部函數(shù)forget_value,這個(gè)函數(shù)首先執(zhí)行檢查,來(lái)確保請(qǐng)求來(lái)自之前設(shè)置該值的同一個(gè)約束。如果是的話,連接器通知相關(guān)的約束來(lái)丟掉當(dāng)前值。

對(duì)has_val消息的響應(yīng)表示連接器是否擁有一個(gè)值。對(duì)connect消息的響應(yīng)將source約束添加到約束列表中。

我們?cè)O(shè)計(jì)的約束程序引入了許多出現(xiàn)在面向?qū)ο缶幊痰母拍?。約束和連接器都是抽象,它們通過(guò)消息來(lái)操作。當(dāng)連接器的值由消息改變時(shí),消息不僅僅改變了它的值,還對(duì)其驗(yàn)證(檢查來(lái)源)并傳播它的影響。實(shí)際上,在這一章的后面,我們會(huì)使用相似的字符串值的字典結(jié)構(gòu)和函數(shù)值來(lái)實(shí)現(xiàn)面向?qū)ο笙到y(tǒng)。

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

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

相關(guān)文章

  • SICP Python 描述 第三章 計(jì)算機(jī)程序的構(gòu)造和解釋 3.1 引言

    摘要:為通用語(yǔ)言設(shè)計(jì)解釋器的想法可能令人畏懼。但是,典型的解釋器擁有簡(jiǎn)潔的通用結(jié)構(gòu)兩個(gè)可變的遞歸函數(shù),第一個(gè)求解環(huán)境中的表達(dá)式,第二個(gè)在參數(shù)上調(diào)用函數(shù)。這一章接下來(lái)的兩節(jié)專注于遞歸函數(shù)和數(shù)據(jù)結(jié)構(gòu),它們是理解解釋器設(shè)計(jì)的基礎(chǔ)。 3.1 引言 來(lái)源:3.1 Introduction 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 第一章和第二章描述了編程的兩個(gè)基本元素:數(shù)據(jù)和函數(shù)之間的...

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

    摘要:遞歸列表可以使用遞歸函數(shù)最為自然地操作,就像它們的名稱和結(jié)構(gòu)表示的那樣。處理遞歸列表遞歸列表結(jié)構(gòu)將列表表示為首個(gè)元素和列表的剩余部分的組合。例如,我們可以使用高階遞歸函數(shù)將樹(shù)的每個(gè)葉子平方,它的結(jié)構(gòu)類似于。成員測(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 描述 第二章 使用對(duì)象構(gòu)建抽象 2.1 引言

    摘要:對(duì)象表示信息,但是同時(shí)和它們所表示的抽象概念行為一致。通過(guò)綁定行為和信息,對(duì)象提供了可靠獨(dú)立的日期抽象。名稱來(lái)源于實(shí)數(shù)在中表示的方式浮點(diǎn)表示。另一方面,對(duì)象可以表示很大范圍內(nèi)的分?jǐn)?shù),但是不能表示所有有理數(shù)。 2.1 引言 來(lái)源:2.1 Introduction 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 在第一章中,我們專注于計(jì)算過(guò)程,以及程序設(shè)計(jì)中函數(shù)的作用。我們看到了...

    phoenixsky 評(píng)論0 收藏0
  • SICP Python 描述 1.4 實(shí)踐指南:函數(shù)的藝術(shù)

    摘要:實(shí)踐指南函數(shù)的藝術(shù)來(lái)源譯者飛龍協(xié)議函數(shù)是所有程序的要素,無(wú)論規(guī)模大小,并且在編程語(yǔ)言中作為我們表達(dá)計(jì)算過(guò)程的主要媒介。目前為止,我們討論了函數(shù)的形式特性,以及它們?nèi)绾问褂?。第一行描述函?shù)的任務(wù)。 1.4 實(shí)踐指南:函數(shù)的藝術(shù) 來(lái)源:1.4 Practical Guidance: The Art of the Function 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 函...

    lemon 評(píng)論0 收藏0
  • SICP Python描述 1.1 引言

    摘要:另一個(gè)賦值語(yǔ)句將名稱關(guān)聯(lián)到出現(xiàn)在莎士比亞劇本中的所有去重詞匯的集合,總計(jì)個(gè)。表達(dá)式是一個(gè)復(fù)合表達(dá)式,計(jì)算出正序或倒序出現(xiàn)的莎士比亞詞匯集合。在意圖上并沒(méi)有按照莎士比亞或者回文來(lái)設(shè)計(jì),但是它極大的靈活性讓我們用極少的代碼處理大量文本。 1.1 引言 來(lái)源:1.1 Introduction 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 計(jì)算機(jī)科學(xué)是一個(gè)極其寬泛的學(xué)科。全球的分布...

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

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

0條評(píng)論

kamushin233

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<