跳轉至內容

應用程式設計/函式

75% developed
來自華夏公益教科書

函式是將語句分組在一起的結構化元素,通常在計算機程式中多次使用以重複一項任務。如果沒有這些函式,重用程式碼的替代方法是複製它並將其適應不同的環境,這將是一個糟糕的主意。應該避免冗餘程式碼——在這種情況下是重複程式碼。使用函式通常會提高程式的可理解性和質量。它還降低了軟體開發和維護的成本。函式在不同的程式語言中有多種名稱(例如,子例程、例程、過程、方法或子程式)。

讓我們看看以下程式碼

print("Program starts")
    
print("Hi Peter")
print("Nice to see you again!")
print("Enjoy our video!")
    
print("Hi Sarah")
print("Nice to see you again!")
print("Enjoy our video!")
    
print("Hi Dominque")
print("Nice to see you again!"
print("Enjoy our video!")

輸出

''Program starts''
''Hi Peter''
''Nice to see you again!''
''Enjoy our video!''
''Hi Sarah''
''Nice to see you again!''
''Enjoy our video!''
''Hi Dominque''
''Nice to see you again!''
''Enjoy our video!''


讓我們仔細看看上面的程式碼。您可以在程式碼中看到我們正在向三個人打招呼。我們使用三個幾乎相同的列印語句;只是名字不同。這就是我們所說的冗餘程式碼。我們重複了三次程式碼。這是一個可以而且應該使用函式來消除冗餘程式碼的例子。

以下 Python 程式碼使用一個包含名為“name”的引數的函式。冗餘被消除,程式碼更加簡潔。看一下

def greet(name):
    print("Hi " + name)
    print("Nice to see you again!")
    print("Enjoy our video!")
    print("Program starts")
    greet("Peter")         
    greet("Sarah")      
    greet("Dominque")</nowiki>

輸出

''Program starts''
''Hi Peter''
''Nice to see you again!''
''Enjoy our video!''
''Hi Sarah''
''Nice to see you again!''
''Enjoy our video!''
''Hi Dominque''
''Nice to see you again!''
''Enjoy our video!''

在上面的示例中,使用函式導致程式碼行減少,具體來說減少了 20%。任何進一步的函式重用都會提供更高的效率。

Python 中的函式由 def 語句定義。一般語法如下

     '''def''' function_name(parameter list):
          statements and the function body

引數列表包含零個或多個引數。引數呼叫要使用的實際資料(引數),如果函式使用任何資料。函式主體其餘部分包含縮排語句。每次呼叫函式時,函式主體都會執行。

函式中的區域性變數和全域性變數範圍

[編輯 | 編輯原始碼]

當在函式中定義時,變數預設情況下對函式的作用域是區域性的。

這是一個例子

     def f(): 
         print(s)
     s = "chimichurri"
     f()

輸出

chimichurri

例子

     def f(): 
         s = "chimichanga"
         print(s)
     f()

輸出

chimichanga

例子

     s = "chimichurri"
     f()
     print(s)

輸出

chimichanga
chimichurri

例子

     def f(): 
         print(s)
         s = "chimichanga"
         print(s)
     s = "chimichurri" 
     f()
     print(s)

輸出

    UnboundLocalError                         Traceback (most recent call last)
    <ipython-input-25-81b2fbbc4d42> in <module>
         6 
         7 s = "chimichurri"
    ---->8 f()
         9 print(s)

    <ipython-input-25-81b2fbbc4d42> in f()
         1 def f():
    ---->2     print(s)
         3     s = "chimichanga"
         4     print(s)
         5 
    UnboundLocalError: local variable 's' referenced before assignment

如果我們執行前面的指令碼,我們會收到錯誤訊息:UnboundLocalError: local variable 's' referenced before assignment。

變數 's' 在 f() 中是不明確的,即在 f() 中的第一個列印中,可以使用全域性 's',其值為“chimichurri”。在此之後,我們用賦值 s = "chimichanga" 定義了一個區域性變數 s。

     def f():
         global s
         print(s)
         s = "dog"
         print(s) 
     s = "cat" 
     f()
     print(s)

輸出

cat
dog
dog

我們在指令碼中使變數 's' 變為全域性變數。因此,我們在函式體內部執行的任何操作都將對函式體外部的全域性變數進行操作。 [1]

模組化程式設計

[編輯 | 編輯原始碼]

什麼是模組化程式設計?

[編輯 | 編輯原始碼]

模組化程式設計是一種軟體設計技術,它強調將程式的功能分離成獨立的、可互換的模組,這樣每個模組都包含執行所需功能的某個方面所需的一切。 [2] 它可用於各種應用程式和函式,並與系統的其他元件一起使用。類似的功能被分組在同一個程式設計程式碼單元中,而單獨的函式則被開發為單獨的程式碼單元,以便程式碼可以被其他應用程式重用。 [3]

為什麼要使用模組化程式設計?

[編輯 | 編輯原始碼]

模組化程式設計的目的是透過將大型軟體程式分解成更小的部分來簡化大型軟體程式的開發和維護。模組化程式設計通常使您的程式碼更容易閱讀,因為它意味著將程式碼分離成僅處理整體功能的一個方面的函式。與單片程式碼相比,它可以使您的檔案更小,更易於理解。被分成不同模組的軟體也適合測試。這是因為當您測試功能較少的小函式時,與測試執行許多操作的大函式相比,測試可以更加嚴格和詳細。如果您只能測試函式的輸出,而不是檢視它遵循的步驟,這一點尤其如此。以這種方式分離函式可以使您在以後更快更容易地找到所需的內容。 [4]

模組化程式設計與結構化程式設計和麵向物件程式設計

[編輯 | 編輯原始碼]

模組化程式設計與結構化程式設計和麵向物件程式設計密切相關,它們都具有相同的目標,即透過分解成更小的部分來促進大型軟體程式和系統的構建。更具體地說,模組化程式設計指的是對整個程式程式碼進行高階分解:結構化程式設計指的是對低階程式碼使用結構化控制流,而面向物件程式設計指的是對資料使用物件,一種資料結構。 [5]

程式碼由許多不同的程式碼模組組成,這些模組是單獨開發的。這允許不同的開發人員承擔系統的不同部分,設計和實現它們,而無需理解其餘部分。但是,要有效地從模組構建大型程式,我們需要能夠編寫與程式其餘部分隔離的模組。我們不需要在開發程式碼模組時考慮程式的每個其他部分,而需要能夠使用區域性推理。也就是說,僅推理模組,以及它需要滿足的與程式其餘部分相關的契約。如果每個人都完成了他們的工作,單獨開發的程式碼模組可以組合在一起形成一個工作的程式,而無需每個開發人員都瞭解團隊中每個其他開發人員完成的一切。這是模組化程式設計的關鍵思想。 [6]

函式或過程通常需要一些關於其被呼叫環境的資訊。環境和函式之間的關係涉及特殊的變數,這些變數被稱為引數。透過使用這些引數,可以在函式內部使用各種“外部”物件。引數宣告的語法以及將引數傳遞給函式引數的語義取決於程式語言。

通常情況下,引數和引數這兩個術語被同義使用,但它們之間存在明顯的區別。引數存在於函式或過程中,而引數用於過程呼叫(即在執行時傳遞給函式的值)。

引數的求值策略(即函式呼叫中的引數如何傳遞給函式的引數)在不同的程式語言中有所不同。最常見的求值策略是“按值呼叫”和“按引用呼叫”。

子程式

[編輯 | 編輯原始碼]

它是什麼?

[編輯 | 編輯原始碼]

子程式是一系列程式指令,這些指令執行特定任務,並作為一個單元打包。然後,這個單元可以在程式中任何需要執行該任務的地方使用。子程式可以在程式中定義,也可以在可呼叫的庫中單獨定義。在不同的程式語言中,子程式可以被稱為例程、子程式、函式、方法或過程。從技術上講,這些術語都有不同的定義。有時也使用通用術語“可呼叫單元”。[7]

子程式有兩種型別:過程和函式。過程是一個執行特定任務的子程式。當任務完成時,子程式結束,主程式從停止的地方繼續執行。例如,可以編寫一個過程來將陣列的所有值重置為零,或者清除螢幕。另一方面,函式的工作方式與過程相同,只是它會操作資料並返回結果到主程式。[8]

按值呼叫

[編輯 | 編輯原始碼]

最常見的策略是按值呼叫求值,有時也稱為傳值。例如,這種策略在C和C++中使用。在按值呼叫中,引數表示式被求值,然後將該求值的結果繫結到函式中相應的變數。因此,如果表示式是一個變數,它的值將被賦值(複製)到相應的引數。這確保了當函式返回時,呼叫者作用域中的變數將保持不變。換句話說,會建立傳遞給函式的引數的副本。當對副本進行任何操作時,實際值不會改變。它只改變了在函式內部建立的副本的值。以下是一個無偏的 Python 例子

 def test(string):
     string = "Python is cool"
     print("Inside function: " + string)
 
 string = "Python"
 print("Before function call: " + string)
 test(string)
 print("After function call: " + string)

輸出

 Before function call: Python
 Inside function: Python is cool
 After function call: Python

如您所見,會建立傳遞給函式的引數的副本。當對副本進行任何操作時,實際值不會改變。它只改變了在函式內部建立的副本的值。

按引用呼叫

[編輯 | 編輯原始碼]

在按引用呼叫求值中,也稱為傳引用,函式會獲得對引數的隱式引用,而不是其值的副本。因此,函式可以修改引數,即呼叫者作用域中變數的值可以被改變。透過使用按引用呼叫,我們節省了計算時間和記憶體空間,因為引數不需要被複制。另一方面,這也有一個缺點,即變數可以在函式呼叫中“意外”地改變。因此,必須格外小心才能“保護”不應該被改變的值。許多程式語言支援按引用呼叫,例如 C 或 C++,但 Perl 預設使用它。在 ALGOL 60 和 COBOL 中,有一個不同的概念叫做按名呼叫,這種概念在現代語言中不再使用。

Python 使用一種被稱為“按物件呼叫”的機制,有時也稱為“按物件引用呼叫”或“按共享呼叫”。如果您將不可變引數(如整數、字串或元組)傳遞給函式,則傳遞行為類似於按值呼叫。物件引用被傳遞給函式引數。它們不能在函式內部改變,因為它們根本無法改變(即它們是不可變的)。如果您傳遞可變引數,則情況不同。它們也是按物件引用傳遞的,但它們可以在函式內部原地改變。如果您將列表傳遞給函式,則需要考慮兩種情況:列表元素可以在原地改變(即列表即使在呼叫者的作用域中也會被改變);如果將新列表分配給名稱,則舊列表不會受到影響(即呼叫者的作用域中的列表將保持不變)。[9] 一個 Python 例子

 def add_list(a):
     a.append('world')
     print("Inside function call: " + str(a))
 
 a = ['Hello']
 print("Before function call: " + str(a))
 add_list(a)
 print("After function call: " + str(a))

輸出

 Before function call: ['Hello']
 Inside function call: ['Hello', 'world']
 After function call: ['Hello', 'world']

在這種對原始值的引用傳遞時,當對引數執行任何操作時,實際值也會改變。它會改變函式作用域和全域性作用域中的值。[10]

按結果呼叫

[編輯 | 編輯原始碼]

按值結果呼叫

[編輯 | 編輯原始碼]

按名呼叫

[編輯 | 編輯原始碼]

按常量值呼叫

[編輯 | 編輯原始碼]

命名約定

[編輯 | 編輯原始碼]

它是什麼?

[編輯 | 編輯原始碼]

命名約定是為程式的變數、函式等預先確定的一組規則或指南。它們是在建立軟體程式設計文字指令碼時應用的一般規則。它們有許多不同的目的,例如為指令碼增加清晰度和一致性、提高第三方應用程式的可讀性,以及在某些語言和應用程式中的功能。[11]

為什麼需要它們

[編輯 | 編輯原始碼]

遵循命名規範的常見原因包括:減少閱讀和理解原始碼所需的工作量,使程式碼審查能夠專注於比爭論語法和命名標準更重要的問題,並使程式碼質量審查工具能夠將其報告主要集中在除語法和樣式偏好以外的重要問題上。[12]

常見規範

[編輯 | 編輯原始碼]

駝峰命名法 - 除了第一個單詞以外,每個單詞的首字母都大寫。單詞之間沒有空格。示例

 exampleVariable = 1
 def myFunction():

帕斯卡命名法 - 每個單詞的首字母都大寫。單詞之間沒有空格。示例

 Example Variable = 1
 def MyFunction():

蛇形命名法 - 每個單詞都小寫。單詞之間用下劃線隔開。示例

 example_variable = 1
 def my_function():

我應該使用哪種方法

[編輯 | 編輯原始碼]

C++、Java 和 JavaScript 通常使用駝峰命名法,帕斯卡命名法保留用於庫和類。C# 主要使用帕斯卡命名法,駝峰命名法用於引數。Python 在大多數識別符號中使用蛇形命名法。此外,以下規則適用

  • 不要以下劃線開頭(用於技術程式設計)
  • 常量全部大寫(通常為 UPPER_SNAKE_CASE)[13]

參考資料

[編輯 | 編輯原始碼]
華夏公益教科書