跳转至内容

应用编程/函数

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]

参考资料

[编辑 | 编辑源代码]
华夏公益教科书