企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## 9.功能 当你多次使用同一段代码时,可以将它们分组为一个函数,你可以在程序中的任何位置调用该分组代码以执行特定任务。 让我们以一个现实世界为例,你去旅馆,侍者走近,你点了一份炸鱼,就明白了。 订购后,你不会受到任何影响。 一旦订购了鱼苗,就会发生很多程序。 服务员记下你的订单,他去厨房,将订单交给厨师,厨师告诉他要煮熟的菜肴会花费很多时间。 服务员想着如何避免让你感到无聊,他来到你的餐桌旁,为你推荐开胃菜和/或开胃菜,为你提供可以在吃菜前喝得很好的饮料,他汇集了厨房以查看菜是否 准备好了。 如果菜已经准备好并且你已经完成了开胃菜,那么他将为你服务。 如果你还没吃完开胃菜,他会告诉厨房让菜保温,然后等你煮完。 一旦他把盘子拿到你的桌子上,他就放下装有盘子和餐具的盘子。 你要做的只是订购炸鱼,而完全忽略了后台正在运行的功能。 你向服务员下了订单(输入),然后拿到了盘子(输出)。 根据服务员的训练,服务员会预先计划或训练做什么和不做什么。 让我们开始使用 Ruby 中的函数。 我们将看一个程序,其中将编写一个名为`print_line`的函数,该函数将打印一行。 在文本编辑器中键入以下程序并运行它。 ```rb # function.rb def print_line puts '_'*20 end print_line puts "This program prints lines" print_line ``` 输出量 ```rb ____________________ This program prints lines ____________________ ``` 让我们分析程序。 考虑以下代码: ```rb def print_line puts '_'*20 end ``` `def`告诉你正在定义一个功能。 函数必须具有名称,该名称紧随`def`关键字之后。 在这种情况下,函数的名称为`print_line`。 你可以在其中编写所需的代码。 在这种情况下,我们将创建一个包含 20 个下划线字符的行。 因此,我们创建了一个函数,并向其中添加了代码。 现在我们需要做的就是从程序中调用该函数。 只需在程序中键入函数名称即可,如下所示 ```rb print_line puts "This program prints lines" print_line ``` 从输出中可以看到,在字符串`“This program prints lines”`的上方和下方都打印了由 20 个下划线字符组成的行。 ### 9.1。 参数传递 让我们对本节中的功能进行更多控制。 有时你不去酒店订购一顿菜,你可以订购多少或更少。 如果你和朋友一起去,可以订购更多份量;如果你一个人,则订购的份量会更少。 为什么用`print_line`功能不能做同样的事情? 为什么我们不能更改其长度,这样做将是一件很了不起的事情,我们可以根据自己的选择打印长度的行。 看下面的代码,我们在函数名称后键入了一个名为`length`的东西,它称为参数。 像函数一样,参数也有一个名称,在这种情况下,我们将其命名为`length`。 我们可以将任何值传递给它以改变打印行的`length`。 输入以下代码并执行 ```rb # function_1.rb def print_line length puts '_'*length end 10.step(50,10) do |x| print_line x end 40.step(10,-10) do |x| print_line x end ``` 你将获得如下所示的设计模式 ```rb __________ ____________________ ______________________________ ________________________________________ __________________________________________________ ________________________________________ ______________________________ ____________________ __________ ``` 看下面的代码 ```rb 10.step(50,10) do |x| print_line x end 40.step(10,-10) do |x| print_line x end ``` 我们已经使用`step`循环将捕获的值递增或递减到循环内的`x`变量中,通过将`x`传递给`print_line`函数,方法是将其放置在调用后,如上突出显示 。 因此,每次打印一条不同长度的线(由`x`决定)。 以某种方式构造程序,以便生成模式。 ### 9.2。 默认参数 在 [function_1.rb](code/function_1.rb) 中,你了解了如何将参数传递给函数。 如果假设你无法通过辩论,该怎么办? 如果这样做,将会产生一个错误,一个好的程序员将不会希望发生这种错误。 为了防止这种情况并使编程变得容易一些,最好为函数提供默认参数。 注意下面在 [function_default_argument.rb](code/function_default_argument.rb) 中给出的代码 ```rb # function_default_argument.rb def print_line length = 20 puts '_'*length end print_line print_line 40 ``` 执行程序并观察结果 ```rb ____________________ ________________________________________ ``` 你可以在程序中通过输入`length = 20`来查看函数`print_line`,这表明如果未传递任何参数,则该函数必须假定`length`的值为 20。 如果通过,则此值将被你传递的任何值所覆盖。 正如你在第一个函数调用中看到的那样,我们仅通过函数名称`print_line`来调用函数,我们无需理会将长度值传递给它,但是我们看到在输出中打印出一行长度为 20 个单位的行 。 ### 9.3。 将数组传递给函数 通常,当你将变量传递给函数时,会创建该变量的副本。 而且,如果你对变量进行了更改,则该函数外部的变量不会受到影响。 因此,让我们看看将数组传递给函数时会发生什么。 在下面的程序中键入并运行它。 ```rb # array_passed_to_function.rb def array_changer array array << 6 end some_array = [1, 2, 3, 4, 5] p some_array array_changer some_array p some_array ``` Output ```rb [1, 2, 3, 4, 5] [1, 2, 3, 4, 5, 6] ``` 如果你是编程新手,这可能并不奇怪,但是当将变量传递给函数时,其值不应更改。 但是在数组的情况下,在函数`array_changer`中,我们向其中添加了一个元素,并且它会更改。 好吧,这是数组传递给函数的特殊行为。 为避免这种情况,请尝试以下程序 ```rb # array_copy.rb def array_changer array array << 6 end some_array = [1, 2, 3, 4, 5] p some_array array_changer Marshal.load(Marshal.dump(some_array)) p some_array ``` Output ```rb [1, 2, 3, 4, 5] [1, 2, 3, 4, 5] ``` 在这里,数组没有改变,请看`array_changer Marshal.load(Marshal.dump(some_array))`行,这段代码将`some_array`复制到 function 参数,以便即使在函数内部对其进行了更改,也不会在函数外部进行更改。 ////在这里写函数式编程 ### 9.4。 返回值 到目前为止,我们已经看到函数接受了参数,现在我们看到函数可以返回可用于某些目的的值。 现在让我们来看程序 [function_return.rb](/code/function_return.rb) ,研究代码,键入并执行它。 ```rb #! /usr/bin/ruby # function_return.rb def addition x, y sum = x+y return sum end a, b = 3, 5 puts addition a,b ``` Output ```rb 8 ``` 执行后得到的输出为 8。请注意上述程序中名为 addition 的方法。 它接受两个参数`x`和`y`,在该方法内部,我们声明一个名为`sum`的变量,该变量分配给`x`与`y`的加法。 下一个语句是此处的主人公,请参见我们使用了关键字`return`,这将返回该函数的值。 在上面的程序中,我们返回`sum`,因此当我们得到答案时。 不是我们必须使用`return`语句返回值。 默认情况下,返回在 Ruby 函数中执行的最后一条语句。 考虑下面显示的程序 [function_last_gets_returned.rb](code/function_last_gets_returned.rb) 。 在其中,我们有一个名为`square_it`的方法,该方法接受一个名为`x`的单个参数。 它只有一个语句`x**2`,恰好也是最后一个语句。 ```rb #!/usr/bin/ruby # function_last_gets_returned.rb def square_it x x**2 end puts square_it 5 ``` 输入程序并执行。 ```rb 25 ``` 如你所见,我们调用了`square_it 5`,结果为 25。 这可能是因为在 Ruby 中,默认情况下返回上一次执行的语句的结果。 ### 9.5。 关键字参数 要了解关键字参数,请在下面键入程序并执行: ```rb # keyword_argument.rb def say_hello name: "Martin", age: 33 puts "Hello #{name} your age is #{age}" end say_hello name: "Joseph", age: 7 say_hello age: 21, name: "Vignesh" say_hello ``` Output ```rb Hello Joseph your age is 7 Hello Vignesh your age is 21 Hello Martin your age is 33 ``` 因此,要查看此功能的工作原理,请分析代码。 查看 say_hello 的函数定义,如下所示 ```rb def say_hello name: "Martin", age: 33 puts "Hello #{name} your age is #{age}" end ``` 查看突出显示的部分`def say_hello name: "Martin", age: 33`。 在这里,我们不将参数指定为`def say_hello name= "Martin", age= 33`,而是使用特殊的`name: “Martin”`,我们取出了等号并替换为冒号。 那有什么用呢? 现在看一下函数调用的部分 ```rb say_hello name: "Joseph", age: 7 say_hello age: 21, name: "Vignesh" say_hello ``` 第一行是`say_hello name: "Joseph", age: 7`,此处第一个参数是`name`,第二个参数是`age`。 但是看看代码`say_hello age: 21, name: "Vignesh"`,这里的第一个参数是`age`,第二个参数是`name`。 但是由于参数是由关键字提示的,因此它的位置无关紧要,并且该方法将按预期输出字符串。 第三行`say_hello`只是显示如果缺少参数会发生什么,因为我们已经指定了默认值,所以它采用了默认值。 是否可以将关键字参数与默认值一起使用? 绝对没错。 试试下面的程序,看看自己 ```rb # keyword_argument_no_defaults.rb def say_hello name:, age: puts "Hello #{name} your age is #{age}" end say_hello name: "Joseph", age: 7 say_hello age: 21, name: "Vignesh" # say_hello # uncomment it and try it out # say_hello "Karthik", 32 # uncomment it and try it out ``` ### 9.6。 递归函数 让我们看看另一件事。 你可能想知道为什么我要进行所有数学运算? 某些程序员确实写了一些书,这些书尽可能地排除了数学知识,我不是专业的数学家,但我很欣赏数学。 计算机是基于数学的。 所有计算机都使用称为布尔代数的东西来执行所有任务。 我并不是说你必须是数学家才能成为程序员,但是了解数学确实会有所帮助。 好,什么是阶乘? 取一个数字,取 3,现在将是 3 X 2 X 1,即六个! 是不是很简单? 6 是 3 的阶乘。好吧,我们现在取 4,所以 4 X 3 X 2 X 1 将是 24,以类似的方式,2 的阶乘将是 2 X 1,即 2。有了这些知识,我们现在将构造一个 该程序将为我们提供一定数量的阶乘。 研究下面给出的程序。 专注于名为阶乘的函数 ```rb # factorial.rb def factorial num fact = 1 1.upto(num) { |a| fact = fact * a } fact end number = 17 puts "Factorial of #{number} = #{factorial number}" ``` 执行上面的代码,这就是你得到的输出 ```rb Factorial of 17 = 355687428096000 ``` 在上面的示例中(在阶乘函数中),我们将所有数字从一个取到特定的数字,将其相乘得到阶乘。 现在研究如下所示的代码 [factorial_1.rb](code/factorial_1.rb) ```rb # factorial_1.rb def factorial num return 1 if num == 1 return num * factorial(num-1) end number = 17 puts "Factorial of #{number} = #{factorial number}" ``` Execute the code above and this is what you get as output ```rb Factorial of 17 = 355687428096000 ``` 输出与先前程序 [factorial.rb](code:factorial.rb) 相同。 请仔细查看上述程序中名为`factorial`的功能。 我列出来看看 ```rb def factorial num return 1 if num == 1 return num * factorial(num-1) end ``` 该功能令人困惑,不是吗? 当我发疯并且想学习编程时,我学习了 C。递归函数的概念是使用阶乘解释的,而且我很长一段时间都没有理解它。 为了避免痛苦,让我详细解释。 取数字 1。它的阶乘为 1。因此,如果遇到 1,则返回 1,如下代码所示 ```rb def factorial num return 1 if num == 1 return num * factorial(num-1) end ``` 现在考虑数字 2 的阶乘 2 X 1,它是 2 乘以 1 的阶乘。换句话说,我们可以将其写为 2 乘以 2-1 的阶乘。 因此,如果在函数`factorial`中遇到第二个数字,它将跳过第一个 if 语句,而下面的第二个语句`return num * factorial(num-1)`被执行 ```rb def factorial num return 1 if num == 1 return num * factorial(num-1) end ``` 在这个有趣的事情发生了。 这里调用阶乘(2-1),即阶乘函数本身。 因此,当 2-1 的阶乘,即 1 的阶乘被调用时,它返回 1,该 1 乘以 2 并返回,因此在这种情况下最终返回 2。 现在取数字 3。其阶乘为 3 X 2 X1。这可以写成 3 乘以阶乘 2。阶乘 2 转换为 2 乘以阶乘 1。因此最终得到结果。 对于任何大于 1 的数字,阶乘函数都会反复调用其自身。 函数调用本身的过程称为递归。 ### 9.7。 可变数量的参数 假设你不知道向一个函数传递了多少个参数,假设你正在编写一个要添加 N 个数字的函数,则 N 的值未知,那么如何获得可变数量的参数。 键入下面给出的程序 [function_variable_arguments.rb](code/function_variable_arguments.rb) 并执行它。 ```rb # function_variable_arguments.rb def some_function a, *others puts a puts "Others are:" for x in others puts x end end some_function 1,2,3,4,5 ``` Output ```rb 1 Others are: 2 3 4 5 ``` 因此,程序的输出如上所示。 如你所见,我们将 1,2,3,4,5 作为参数传递,那么`a`只是一个变量,因此它取值为 1,其他变量被变量`**others**` **吸收( 注意,变量名前的星号** 是一种特殊的自变量,它将所有未被前一个自变量吸收的其余自变量存储在其他变量名中(作为数组)。 现在在下面的代码中 ```rb for x in others puts x end ``` 就是这样。 现在尝试编写一个函数以查找 n 个数的最大值,并编写另一个函数以查找 n 个数的最小值,并编写一个程序查找一堆数字的最大值和最小值。 ### 9.8。 哈希作为函数参数 潜入多个参数到函数中的另一种方法是将它们作为哈希传递。 看下面的程序,我们有一个名为`some_function`的函数,该函数有两个参数,第一个名为`first_arg`,第二个名为`others_as_hash`,我们在下一行`some_function "Yoda", {jedi: 100, sword: 100, seeing_future: 100}`中调用此函数,执行它并 注意输出 ```rb # hashes_to_functions.rb def some_function first_arg, others_as_hash puts "Your first argument is: #{first_arg}" puts "Other arguments are:" p others_as_hash end some_function "Yoda", {jedi: 100, sword: 100, seeing_future: 100} ``` Output ```rb Your first argument is: Yoda Other arguments are: {:jedi=>100, :sword=>100, :seeing_future=>100} ``` 如我们所料,程序将打印第一个参数并传递给`others_as_hash`的哈希,这并不奇怪,但请看下面的程序 [hashes_to_function_1.rb](code/hashes_to_function_1.rb) ,执行该程序,其输出将 与上面的程序相同 ```rb # hashes_to_functions_1.rb def some_function first_arg, others_as_hash puts "Your first argument is: #{first_arg}" puts "Other arguments are:" p others_as_hash end some_function "Yoda", jedi: 100, sword: 100, seeing_future: 100 ``` 但是请注意这部分,我们已经将`some_function`称为 ```rb some_function "Yoda", jedi: 100, sword: 100, seeing_future: 100 ``` 在函数中,我们将第二个参数作为哈希值进行传递,但是如上所示,它应予注意,请注意,我们已避免使用花括号,并且它仍然有效。 这才是重点。