《從零開始PYTHON3》第五講
上一節課重點學習了字元串,并且傳遞了一個重要的理念,就是程式要對開發人員自己和使用者都足夠友好。在這個過程中,利用字元串給出充分、完整、準确的提示是非常重要的一部分。
在Python可以處理的不同資料類型中,每種資料類型都有自己特色的運算方式,比如我們上一節課對比過的數字類型和字元串類型的運算:#數值的運算
>>> 123*3
369
#字元串的運算
>>> "123"*3
'123123123'
兩者的計算方式截然的不同,又具有自己的特點和不同的應用場景。這裡講這個例子,并不隻是想讓大家複習上一講的課程。而是想讓大家思考一下計算機最擅長的工作是什麼?
沒錯,相信大多數人都想到了,計算機最擅長的,一是計算,二是重複。至于繪圖、音樂、視訊等等所謂的高端應用,不過是計算、重複的各種複雜組合。
計算在第二、第三講我們已經說過很多了,後面還會涉及到更進階的一些計算類應用。“重複”則是今天要說到的重點。
While循環
第三講的時候我們學過了計算機執行順序的問題。每個Python程式都是從第一行開始,順序執行,直到程式的最後一句。其中碰到函數定義的時候,會“定義一個函數”,而不是“執行一個函數”。函數真正執行會在函數被調用的時候。
While循環則是讓計算機對某一段的程式代碼在限定條件下重複執行的手段。我們來看一個簡單的例子來幫助我們了解:i = 1
while i <= 999:
i = i + 1
第一行,我們定義了一個變量i,并為它賦一個數字類型的值1。
第二行是while循環的條件部分,用于控制進入循環和繼續循環的條件。簡單說,就是當條件滿足的才開始循環,并且不斷循環下去,直到條件不再被滿足。
“<=”是條件,表示“小于等于”,同樣是因為計算機中沒有傳統用的“≤”符号,是以采用了變通的寫法。關于條件,或者說條件邏輯,我們會在後面詳細講解。
第三行是一條指派語句,第二講我們講到變量的時候已經強調過,“=”是指派操作符,表示把右側表達式的結果值,賦給左側的變量。
不要跟數學的等式弄混。在這裡則是把i目前值,加1的計算結果,指派給變量i,這時候i的值變成了新的值,也是剛才的計算結果。
我們是頭一次見到這種寫法,但隻要弄明白這個是指派語句,不是等式,你就不會困惑了。
這是一個極度簡化的循環模型,第一行可以稱為初始值,通常這個初始值應當滿足循環開始的條件;
第二行稱為循環的條件判斷,用于控制循環的開始和結束;
第三行稱為循環體,循環體應當是循環真正工作的部分,因為簡化,在這個例子中我們看不到有意義的工作。i=i+1則讓循環持續,并最終能夠不再滿足循環繼續的條件,進而退出循環。否則循環會永無止境的繼續下去,這被稱為“死循環”,也是計算機軟體“崩潰”、“當機”最常見的原因。
為了幫助了解,我們來看一個循環的流程圖。
流程圖是研究、分析程式結構的時候,公認非常有效的一種手段。建議你也學習畫,初學者不用糾結流程圖的樣式,而是用這種方式幫你分析程式邏輯方面的問題。
弄明白了循環的邏輯含義,我們再來看一下while循環文法上的特點,我們對比一下函數定義的文法:#函數定義
def 函數名(參數):
函數體
#while循環
while 循環條件:
循環體
上面這種描述程式邏輯的方法,看起來結構清楚,能反映出來想描述的程式問題,但并不能執行。這種方式叫做“僞代碼”,是跟“程式流程圖”一樣用來分析、研究程式邏輯的方法,我們以後還會用到。
上面這個對比中,你能感覺到一些Python文法的邏輯規律。比如,都是用某個關鍵字開始,來引導整個程式塊,函數定義是用def,while循環是用while;接着是各自特色的東西,比如函數名、參數還有循環條件,相似的,都是是用冒号“:”來結尾第一行,并分割下面的函數體、循環體部分;後面甭管是函數體還是循環體,都是縮格書寫,縮格的結束代表整個程式塊的完成。
循環體中的指派操作值得重點說一下。前面已經說過了,通過對可以影響循環條件的變量進行指派,進而讓循環本身有機會退出循環,這是很重要的一個工作。這種指派改變循環條件,幾乎在所有的循環中都會用到。是以這種通過對自身的改變完成對自身指派的方式,又延伸出了一種簡易的寫法:
原有寫法:簡易寫法:i = i + 1i += 1
i = i -1i -= 1
i = i * 2i *= 2
i = i / 2i /= 2
請看上表中,左側是規範的寫法,右側是化簡後的寫法,使用起來會更友善。本質上,這是增加了+=、-=、*=、/=,四種運算指派符,屬于保留字的一種。通常我們見到的加減乘除運算符都是一個字元,這裡是2個字元,看起來不習慣而已。
練習時間
請使用while循環的方法,求整數1、2、3......直到100的和。請先自己思考10分鐘,可以用流程圖或者僞代碼,有了比較明确的思路再向下看。
我們來看代碼:#求整數1-100的和
#求和的結果儲存在c,一開始是0
c = 0
#i儲存整數1循環到100
i = 1
#進入及繼續循環的條件就是i<=100
while i <= 100:
c = c + i #求和一次
i = i + 1 #下一個整數,2/3/4...
#顯示結果
print("整數1-100的和為:",c)
程式中,我們使用了專門的一個變量c來儲存累加的和,一開始沒有開始累加,是以c是0。變量i通過循環的方式,來模拟整數從1開始,每次加1,直到100的變化。循環的主體c=c+i,則是在每次循環中,進行一次求和的操作。最後縮格結束,表示循環的結束,使用print函數列印出來求和結果。運算結果是:整數1-100的和為: 5050
作為練習,你可以試試把循環中的兩次指派,用剛才講過的簡寫的方式來試試。
條件判斷(邏輯判斷)
對于一個循環來說,循環主體當然是循環的目的,是以通常也是循環的重點。但是在循環編寫的時候,仔細的思考和設定循環的開始條件和結束的條件,才是編寫循環的重點。而條件,通常都是由“邏輯判斷”來完成的。
不同于數字和字元串的千變萬化,邏輯條件隻有“符合條件”和“不符合條件”兩種情況。
前者的同義詞可以為“是”、“真”、“對”,後者的同義詞是“否”、“假”、“錯”。
這種隻有兩個值的類型,叫“布爾類型”,相關的運算叫“布爾運算”。在Python中,提供了數字(Number)類型的子類型(bool)來代表此類資料。bool類型隻有兩個值,True表示真,False表示假。
因為bool是Number的子類型,是以如果把bool套用到Number類型中,1就代表真,0就代表假。我們用表格把這種關系再加深一下印象:
符合條件不符合條件真假
truefalse
10
"abc"(有内容)""(無内容,空串)
表格中最後一行,是字元串類型。對應到bool類型的情況,通常應當比較少用。但你得知道,如果字元串為空代表False,有字元,甭管是什麼字元,都是True。
既然布爾類型,同數字、同字元串,都有對應的關系,有必要單獨獨立出來一種資料類型來增加學習量嗎?還真的是很有必要,我們來看一個例子。
比如在一個學生資訊表中,在性别一欄,我們可以使用“男”、“女”,也可以用數字1、2。看起來也可以很完美的實作我們的目的,但其實這種做法很不可取。
你想象一下在表格的輸入中,有人輸入成了“男生”,意思沒有變,但這一點小的改變,可能讓計算機無所适從。比如數字敲錯成了“3”,計算機同樣也就無法知道這代表的究竟是什麼性别。
如果使用布爾變量,isMan=True代表男生,剛才碰到的那些問題,都不會出現。
此外布爾運算作為數學中重要的一個分支,有完備的理論體系,在計算機中也有計算速度快、相容性好的優點。
剛才講的是“邏輯”的表達方式,下面看看邏輯判斷的方式:
比較運算符含義>大于
>=大于等于
<小于
<=小于等于
==等于(注意同指派操作=區分)
!=不等于
上表中,大于、大于等于、小于、小于等于都好記。邏輯相等的判斷,要跟指派操作的等号差別開,因為這是完全不同的運算符,或者說是不同的Python關鍵字。
不等于符号,同樣是由于計算機中沒有“≠”符号的原因進行了合理的變化。這些都是運算符,運算符不一定隻有一個字元。
下面我們來看幾個邏輯判斷的例子:
邏輯判斷表達式結果1 < 2
1 > 2
2.2 != 2.1
"a" > "b"
"bcd" < "bd"
a="hello"
a == "hello"
2 = 2
請思考後,在本講結尾看答案。
除了這些常用的邏輯判斷,Python還有自己的特色的一種判斷方式,叫做連續判斷,這是其它常見的程式語言不具備的:
邏輯判斷表達式結果1 < 2 < 7True
1 > 2 > 1False
連續判斷經常用于對一個資料進行範圍界定性判斷,是以經常也被稱為“範圍判斷”。
挑戰:棋盤麥粒問題在古代有一個國王,他擁有至高無上的權力和難以計數的财富。但是權力和财富最終使他對生活感到厭倦,渴望着有新鮮的刺激。
某天,一位老人帶着自己發明的國際象棋來朝見。國王對這新奇的玩意非常喜歡,非常迷戀,并感到非常滿足。
于是對老人說:“你給了我無窮的樂趣。為了獎賞你,你可以從我這兒得到你所要的任何東西”。
老人的要求是:請您在棋盤上的第一個格子上放1粒麥子,第二個格子上放2粒,第三個格子上放4粒,第四個格子上放8粒……即每一個次序在後的格子中放的麥粒都必須是前一個格子麥粒數目的倍數,直到最後一個格子放滿為止。
國王哈哈大笑,慷慨地答應了老人這個卑微的請求。
然而,國王最終發現,按照與老人的約定,全國的麥子竟然連棋盤一小半格子數目都不夠。
老人索要的麥粒數目實際上是天文數字,總數将是一個十九位數,折算重量約為2000多億噸,即使現代,全球小麥的年産量也不過是數億噸。
我們要使用while循環作為主體,來幫助國王算一下,放滿一個國際象棋棋盤,究竟需要多少粒麥子。
同樣,請先仔細進行思考,可以使用流程圖或者僞代碼的方式,有了比較清晰的思路再向下看。如果隻是看看答案,缺少了思考,你很難真正掌握一門程式設計語言。
看起來很長的一個問題,其實用程式解決起來無比的容易。當然對于初學者來講,有一個清晰的思路比什麼都重要。不然就好像看心靈雞湯文,看了很多的道理,但仍然過不好這一生。
這裡介紹一種很常用的設計方法,叫做“快速原型法”。快速原型法其實并不是指什麼特定的概念。其核心思想是,把需求先弄清楚,在整理需求的過程中,通過常識性的思考,快速的把已知的部分羅列出來,不能很快弄明白的可以先空着,直到最後,逐漸将空白的部分填補完成,這時候形成的其實是僞代碼。最後用程式代替所有的僞代碼,形成最終的結果。
說起來容易,真正實踐起來,還是需要很多的經驗磨砺,才能得心應手。不過你先有一個概念就好,逐漸的多讀程式,多練習編寫程式,就能做到。
下面我們也嘗試用這種方法來編寫這個程式:
1.理清需求。我們直接把需求寫到程式的注釋中:"""
國際象棋有8行8列共64格,
第1個格子放1粒麥子,第2個格子放2粒麥子,
以後每格都比前面格子數量多一倍,
求麥子總數。
"""
你看,那麼長的文字,真正理順弄清楚,真正對程式有影響的,并不多。
2.想想在循環之前,我們都應當有什麼初始的值要先考慮?#定義一個變量來儲存總的麥子數量,開始為0
c=0
#定義一個變量,循環1-64,來代表每一個格子
i=1
#假設每個格子中的麥子數量為x,初始也是1
x=1
這一步就相當于循環的初始值,雖然隻有i是用來控制循環,但既然我們用循環來幫助運算,那每一個跟循環有關的變量,不可能沒有初始值。
3.循環的過程部分,思考起來,似乎有點複雜,我們先想循環結束應當是什麼?當然是顯示結果:#顯示結果
print("64個格子,總的麥粒數量為:",c)
4.雖然循環比較複雜,但就剩這一部分了,也不得不開始考慮。首先考慮循環的進入和退出條件。循環是從第一個格子,循環到第64個格子,因為包含第64個格子本身,是以循環條件肯定是<=64,如果是<64,那循環到第63個格子就結束了:while i<=64:
5.循環的邊界條件定義好了,現在考慮真正的計算,也就是循環體的部分:c += x #總數 累計上 這一個格子的麥粒數
i += 1 #下一個格子
x = x*2 #下一個格子的麥粒數是這一個格子的2倍
其實總共就是這樣三行不能再少的運算。注意這裡我們都使用了變量自身指派的簡寫形式。
還有一點就是我們前面的例子都是把i = i+1放到循環體最後,其實這并不是必須的,隻要在循環體中修改了循環條件相關的變量,不會導緻死循環就可以。在哪裡為循環變量指派,是程式人員根據友善程度決定的。
好了,完整的貼一遍程式:"""
國際象棋有8行8列共64格,
第1個格子放1粒麥子,第2個格子放2粒麥子,
以後每格都比前面格子數量多一倍,
求最終麥子總數。
"""
#定義一個變量來儲存總的麥子數量,開始為0
c=0
#定義一個變量,循環1-64,來代表每一個格子
i=1
#假設每個格子中的麥子數量為x,初始也是1
x=1
#循環
while i<=64:
c += x #總數累計上這一個格子的麥粒數
i += 1 #下一個格子
x = x*2 #下一個格子的麥粒數是這一個格子的2倍
#顯示結果
print("64個格子,總的麥粒數量為:",c)
執行的結果一定要自己把程式輸入進去之後,自己看一看。
練習時間
練習1:由使用者輸入一個整數n,用while循環求整數1直至n的和。(提示,上一講介紹過函數input())
練習2:請将練習1的程式函數化,要求求和部分單獨為一個函數。
練習3:請将棋盤麥粒問題函數化,以便求出1至指定格子的麥粒數量總和。因為過大的數字會超出Python的計算範圍,我們假定允許使用者輸入的格子為1-64。
本講小結計算機适合做枯燥、重複、大量的工作,循環在這種情況下起着重要的作用。while循環是較為自由的一種循環方式,用途很廣泛
循環的初始值和邊界條件非常重要,讓計算機執行正确,自己需要先設想自己處于計算機的位置上,想清楚
循環的邊界條件必須是可以變化的,需要循環的時候能循環,需要退出循環的時候要能變化條件,是以隻能是變量
判斷邊界條件,需要使用“比較運算符”
比較運算符傳回的是布爾值:True(真)、False(假),因為有了布爾值,計算機才能差別于電腦。特别注意差別比較運算的相等符号和指派指令的等号
參考答案
中間的判斷表達式及其結果:
邏輯判斷表達式結果1 < 2True
1 > 2False
2.2 != 2.1True
"a" > "b"False
"bcd" < "bd"True
a="hello"指派表達式,不能當做邏輯條件使用
a == "hello"True
2 = 2錯誤:2是字元,不是變量,不能被指派
最後的三個練習請參考源碼ex1.py/ex2.py/ex3.py