===》點我返回目錄《===
這個題目有點趣味,組合了前面兩個題目。
雖然內心有點疑惑,但是同古人同樣不辭辛勞的你把上面兩個題目的程序拼在一起:
n=int(input("enter a number:"))
result = 1
while n>1 :
result = result * n
n = n - 1
print (result)
n=result
if n>=0:
y=n/2
while y**2-n>0.0001 or y**2-n<-0.0001:
y=(y+n/y)/2
print(y)
else:
print("Error: negative number")
運行結果,輸入10,結果輸出
3628800
1904.9409439665096
沒有錯。
雖然如此,你心裏還是覺得不對勁:計算機不應該這麼笨吧?它不能把以前寫的程序用某種方式複用一下嗎?
你的感覺是對的,有辦法的。而且這個辦法在程序出現的時候就有了,這就是Ada提出來的子過程的概念。我們可以把一段程序封裝成一個單獨的子過程,接受輸入,計算處理後,輸出結果,這個子過程可以被別的程序反覆調用。
Python裏面是這麼定義子過程的:
def functionname(p1,p2,...):
...
解釋一下,def關鍵字代表這裏要定義一個子過程,後面是子過程的名字,接下來的括號是參數(輸入值,數學上叫自變量)說明,後面過程代碼段。用return命令輸出結果(數學上叫做因變量)。
按照這個定義,我們先改寫階乘程序(factorial.py):
def factorial(n):
result = 1
while n>1 :
result = result * n
n = n - 1
return result
print (factorial(10))
程序裏先用def定義一個叫做factorial的子過程,計算後return result。
使用這個子過程的代碼很簡單,直接調用factorial(10)。由於子程序是給人調用的,所以最後提供出來的時候要把print()語句去掉,否則會往屏幕上輸出結果。
同樣照此辦理,我們改寫牛頓逼近算法程序爲(sqrt.py):
def sqrt(n):
if n>=0:
y=n/2
while y**2-n>0.000001 or y**2-n<-0.000001:
y=(y+n/y)/2
return y
else:
return -1
print (sqrt(10))
測試運行,表明新創建的sqrt()子過程也是對的。
注意這個子過程的實現,else裏面是return -1。以前我們對負數是屏幕輸出錯誤。但是子過程的目的是供別的程序調用,所以子過程自己不要這麼處理,返回一個永遠不可能的值(-1,不考慮複數),錯誤處理交給調用者。
以前調用exit退出程序,那個辦法不好,到現在我們看到了新的處理方法。
好,我們手頭現在有了這兩個子過程,我們這個題目就可以直接使用了。
一開頭,你大概會這麼編寫:
n=int(input("enter a number:"))
x = factorial(n)
y = sqrt(x)
print(y)
運行一下,出錯了:
NameError: name 'factorial' is not defined
計算機不知道這個factorial()子過程,同理也不知道sqrt()子過程。
可是,我們明明定義了啊。問題出在程序空間,本題的程序不能識別以前的題目編寫的程序,爲了識別,重用以前的程序,必須import一下。程序如下:
import factorial
import sqrt
n=int(input("enter a number:"))
x = factorial.factorial(n)
y = sqrt.sqrt(x)
print(y)
程序開始的兩行import了子過程程序文件,factorial.py和sqrt.py,這樣把以前編寫的程序引入了。調用的時候不直接寫子過程名字,而是在前面加了程序文件名,如factorial.factorial(),表示使用factorial這個程序裏的factorial子過程。
更加好的做法,是把相關的子過程寫在一個程序文件中,作爲一個函數包供大家調用。
我們可以這麼提供一個maths程序(maths.py):
#this is a mathematics library
#square root function
def sqrt(n):
if n>=0:
y=n/2
while y**2-n>0.000001 or y**2-n<-0.000001:
y=(y+n/y)/2
return y
else:
return -1
#factorial function
def factorial(n):
result = 1
while n>1 :
result = result * n
n = n - 1
return result
利用這個maths數學包,我們把本題的程序改寫爲:
import maths
n=int(input("enter a number:"))
x = maths.factorial(n)
y = maths.sqrt(x)
print(y)
好了,我們自己也可以寫庫了。其實,你也會想到,作爲一個應用面如此之廣的編程語言,Python不會不提供這些庫的。數學上常用的它都有,在math包中。你只要import math就可以了。
Python自帶的math包裏面常見的函數有:
向上取整
>>> math.ceil(4.12)
5
向下取整
>>> math.floor(4.9)
4
取整數
>>> math.trunc(6.789)
6
Cos餘弦
>>> math.cos(math.pi/4)
0.7071
Sin正弦
>>> math.sin(math.pi/4)
0.70710678
tan正切
>>> math.tan(math.pi/4)
0.99999
e的x次方
>>> math.exp(2)
7.389056
絕對值
>>> math.fabs(-0.03)
0.03
階乘
>>> math.factorial(4)
24
求和
>>> math.fsum((1,2,3,4))
10.0
最大公約數
>>> math.gcd(8,6)
2
以a爲底的對數返回。
>>> math.log(32,2)
5.0
平方根
>>> math.sqrt(100)
10.0
後面我們如果要用到類似的功能,就直接用Python自帶的math庫。
通過庫的概念,我們看到了,如何通過子過程複用和簡化問題。實際上,一個大一點的程序就是仔細分解子程序,最後通過某種結構將子程序組合在一起,積木式進行程序構建。最微型的原子子過程,組合成功能大一點的次小子過程,再進一步組合成大一些的子過程,這麼一級級分解組合。看起來程序像一臺機器,有很多組成部分,每一部分又分成許多部件,每一個部件又由許多零件組成。這樣就可以由專門的人負責專門的子過程了,大家的工作通過調用的參數和輸出關聯在一起,這種組織方式叫“軟件工程”。
我在這裏說得輕巧,其實“軟件工程”是相當複雜的事情,公認的看法,軟件開發是人類有史以來最複雜的工程任務。正是因爲這個原因,一個實際的軟件產品的開發是非常費時費力的,永遠在超期超預算同時質量沒有保證。《The Mythical Man-Month》(《人月神話》)這本名著也是偉大的技術專家Brooks自己項目失敗的經驗和教訓的總結。
文科生所把持的新聞媒體,一般喜歡傳奇的故事,會把事情說得神乎其神,一兩個天才在地下室或者車庫裏面埋頭半年一年功夫,發明了改變人類歷史的產品,這些神話不絕於耳。實際上你沒有看到幾個著名的公司是坐落在車庫裏的,它們仍然要組織成千上萬人做一款產品。一兩個天才是能想出改變歷史的點子來,然而要實現它,是需要無數人艱辛的努力的。