18.md 15.7 KB
Newer Older
W
init  
wizardforcel 已提交
1 2
## 9.功能

W
wizardforcel 已提交
3
当你多次使用同一段代码时,可以将它们分组为一个函数,你可以在程序中的任何位置调用该分组代码以执行特定任务。 让我们以一个现实世界为例,你去旅馆,侍者走近,你点了一份炸鱼,就明白了。 订购后,你不会受到任何影响。
W
init  
wizardforcel 已提交
4

W
wizardforcel 已提交
5
一旦订购了鱼苗,就会发生很多程序。 服务员记下你的订单,他去厨房,将订单交给厨师,厨师告诉他要煮熟的菜肴会花费很多时间。 服务员想着如何避免让你感到无聊,他来到你的餐桌旁,为你推荐开胃菜和/或开胃菜,为你提供可以在吃菜前喝得很好的饮料,他汇集了厨房以查看菜是否 准备好了。 如果菜已经准备好并且你已经完成了开胃菜,那么他将为你服务。 如果你还没吃完开胃菜,他会告诉厨房让菜保温,然后等你煮完。 一旦他把盘子拿到你的桌子上,他就放下装有盘子和餐具的盘子。
W
init  
wizardforcel 已提交
6

W
wizardforcel 已提交
7
你要做的只是订购炸鱼,而完全忽略了后台正在运行的功能。 你向服务员下了订单(输入),然后拿到了盘子(输出)。 根据服务员的训练,服务员会预先计划或训练做什么和不做什么。
W
init  
wizardforcel 已提交
8 9 10

让我们开始使用 Ruby 中的函数。 我们将看一个程序,其中将编写一个名为`print_line`的函数,该函数将打印一行。 在文本编辑器中键入以下程序并运行它。

W
wizardforcel 已提交
11
```rb
W
init  
wizardforcel 已提交
12 13 14 15 16 17 18 19 20 21 22 23 24
# function.rb

def print_line
  puts '_'*20
end

print_line
puts "This program prints lines"
print_line
```

输出量

W
wizardforcel 已提交
25
```rb
W
init  
wizardforcel 已提交
26 27 28 29 30 31 32
____________________
This program prints lines
____________________
```

让我们分析程序。 考虑以下代码:

W
wizardforcel 已提交
33
```rb
W
init  
wizardforcel 已提交
34 35 36 37 38
def print_line
        puts '_'*20
end
```

W
wizardforcel 已提交
39
`def`告诉你正在定义一个功能。 函数必须具有名称,该名称紧随`def`关键字之后。 在这种情况下,函数的名称为`print_line`。 你可以在其中编写所需的代码。 在这种情况下,我们将创建一个包含 20 个下划线字符的行。
W
init  
wizardforcel 已提交
40 41 42

因此,我们创建了一个函数,并向其中添加了代码。 现在我们需要做的就是从程序中调用该函数。 只需在程序中键入函数名称即可,如下所示

W
wizardforcel 已提交
43
```rb
W
init  
wizardforcel 已提交
44 45 46 47 48 49 50 51 52
print_line
puts "This program prints lines"
print_line
```

从输出中可以看到,在字符串`“This program prints lines”`的上方和下方都打印了由 20 个下划线字符组成的行。

### 9.1。 参数传递

W
wizardforcel 已提交
53
让我们对本节中的功能进行更多控制。 有时你不去酒店订购一顿菜,你可以订购多少或更少。 如果你和朋友一起去,可以订购更多份量;如果你一个人,则订购的份量会更少。 为什么用`print_line`功能不能做同样的事情? 为什么我们不能更改其长度,这样做将是一件很了不起的事情,我们可以根据自己的选择打印长度的行。
W
init  
wizardforcel 已提交
54 55 56

看下面的代码,我们在函数名称后键入了一个名为`length`的东西,它称为参数。 像函数一样,参数也有一个名称,在这种情况下,我们将其命名为`length`。 我们可以将任何值传递给它以改变打印行的`length`。 输入以下代码并执行

W
wizardforcel 已提交
57
```rb
W
init  
wizardforcel 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
# 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
```

W
wizardforcel 已提交
73
你将获得如下所示的设计模式
W
init  
wizardforcel 已提交
74

W
wizardforcel 已提交
75
```rb
W
init  
wizardforcel 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88
__________
____________________
______________________________
________________________________________
__________________________________________________
________________________________________
______________________________
____________________
__________
```

看下面的代码

W
wizardforcel 已提交
89
```rb
W
init  
wizardforcel 已提交
90 91 92 93 94 95 96 97 98 99 100 101 102
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。 默认参数

W
wizardforcel 已提交
103
[function_1.rb](code/function_1.rb) 中,你了解了如何将参数传递给函数。 如果假设你无法通过辩论,该怎么办? 如果这样做,将会产生一个错误,一个好的程序员将不会希望发生这种错误。 为了防止这种情况并使编程变得容易一些,最好为函数提供默认参数。 注意下面在 [function_default_argument.rb](code/function_default_argument.rb) 中给出的代码
W
init  
wizardforcel 已提交
104

W
wizardforcel 已提交
105
```rb
W
init  
wizardforcel 已提交
106 107 108 109 110 111 112 113 114 115 116 117
# function_default_argument.rb

def print_line length = 20
  puts '_'*length
end

print_line
print_line 40
```

执行程序并观察结果

W
wizardforcel 已提交
118
```rb
W
init  
wizardforcel 已提交
119 120 121 122
____________________
________________________________________
```

W
wizardforcel 已提交
123
你可以在程序中通过输入`length = 20`来查看函数`print_line`,这表明如果未传递任何参数,则该函数必须假定`length`的值为 20。 如果通过,则此值将被你传递的任何值所覆盖。 正如你在第一个函数调用中看到的那样,我们仅通过函数名称`print_line`来调用函数,我们无需理会将长度值传递给它,但是我们看到在输出中打印出一行长度为 20 个单位的行 。
W
init  
wizardforcel 已提交
124 125 126

### 9.3。 将数组传递给函数

W
wizardforcel 已提交
127
通常,当你将变量传递给函数时,会创建该变量的副本。 而且,如果你对变量进行了更改,则该函数外部的变量不会受到影响。 因此,让我们看看将数组传递给函数时会发生什么。 在下面的程序中键入并运行它。
W
init  
wizardforcel 已提交
128

W
wizardforcel 已提交
129
```rb
W
init  
wizardforcel 已提交
130 131 132 133 134 135 136 137 138 139 140 141 142 143
# 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

W
wizardforcel 已提交
144
```rb
W
init  
wizardforcel 已提交
145 146 147 148
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
```

W
wizardforcel 已提交
149
如果你是编程新手,这可能并不奇怪,但是当将变量传递给函数时,其值不应更改。 但是在数组的情况下,在函数`array_changer`中,我们向其中添加了一个元素,并且它会更改。 好吧,这是数组传递给函数的特殊行为。
W
init  
wizardforcel 已提交
150 151 152

为避免这种情况,请尝试以下程序

W
wizardforcel 已提交
153
```rb
W
init  
wizardforcel 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166 167
# 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

W
wizardforcel 已提交
168
```rb
W
init  
wizardforcel 已提交
169 170 171 172 173 174 175 176 177 178 179 180
[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) ,研究代码,键入并执行它。

W
wizardforcel 已提交
181
```rb
W
init  
wizardforcel 已提交
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
#! /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

W
wizardforcel 已提交
197
```rb
W
init  
wizardforcel 已提交
198 199 200 201 202 203 204
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`,恰好也是最后一个语句。

W
wizardforcel 已提交
205
```rb
W
init  
wizardforcel 已提交
206 207 208 209 210 211 212 213 214 215 216 217
#!/usr/bin/ruby
# function_last_gets_returned.rb

def square_it x
  x**2
end

puts square_it 5
```

输入程序并执行。

W
wizardforcel 已提交
218
```rb
W
init  
wizardforcel 已提交
219 220 221
25
```

W
wizardforcel 已提交
222
如你所见,我们调用了`square_it 5`,结果为 25。 这可能是因为在 Ruby 中,默认情况下返回上一次执行的语句的结果。
W
init  
wizardforcel 已提交
223 224 225 226 227

### 9.5。 关键字参数

要了解关键字参数,请在下面键入程序并执行:

W
wizardforcel 已提交
228
```rb
W
init  
wizardforcel 已提交
229 230 231 232 233 234 235 236 237 238 239 240 241
# 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

W
wizardforcel 已提交
242
```rb
W
init  
wizardforcel 已提交
243 244 245 246 247 248 249
Hello Joseph your age is 7
Hello Vignesh your age is 21
Hello Martin your age is 33
```

因此,要查看此功能的工作原理,请分析代码。 查看 say_hello 的函数定义,如下所示

W
wizardforcel 已提交
250
```rb
W
init  
wizardforcel 已提交
251 252 253 254 255 256 257
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”`,我们取出了等号并替换为冒号。 那有什么用呢? 现在看一下函数调用的部分

W
wizardforcel 已提交
258
```rb
W
init  
wizardforcel 已提交
259 260 261 262 263 264 265 266 267
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`只是显示如果缺少参数会发生什么,因为我们已经指定了默认值,所以它采用了默认值。 是否可以将关键字参数与默认值一起使用? 绝对没错。 试试下面的程序,看看自己

W
wizardforcel 已提交
268
```rb
W
init  
wizardforcel 已提交
269 270 271 272 273 274 275 276 277 278 279 280 281 282
 # 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。 递归函数

W
wizardforcel 已提交
283
让我们看看另一件事。 你可能想知道为什么我要进行所有数学运算? 某些程序员确实写了一些书,这些书尽可能地排除了数学知识,我不是专业的数学家,但我很欣赏数学。 计算机是基于数学的。 所有计算机都使用称为布尔代数的东西来执行所有任务。 我并不是说你必须是数学家才能成为程序员,但是了解数学确实会有所帮助。
W
init  
wizardforcel 已提交
284 285 286 287 288

好,什么是阶乘? 取一个数字,取 3,现在将是 3 X 2 X 1,即六个! 是不是很简单? 6 是 3 的阶乘。好吧,我们现在取 4,所以 4 X 3 X 2 X 1 将是 24,以类似的方式,2 的阶乘将是 2 X 1,即 2。有了这些知识,我们现在将构造一个 该程序将为我们提供一定数量的阶乘。

研究下面给出的程序。 专注于名为阶乘的函数

W
wizardforcel 已提交
289
```rb
W
init  
wizardforcel 已提交
290 291 292 293 294 295 296 297 298 299 300 301 302 303
# factorial.rb

def factorial num
  fact = 1
  1.upto(num) { |a|
    fact = fact * a
  }
  fact
end

number = 17
puts "Factorial of #{number} = #{factorial number}"
```

W
wizardforcel 已提交
304
执行上面的代码,这就是你得到的输出
W
init  
wizardforcel 已提交
305

W
wizardforcel 已提交
306
```rb
W
init  
wizardforcel 已提交
307 308 309 310 311
Factorial of 17 = 355687428096000
```

在上面的示例中(在阶乘函数中),我们将所有数字从一个取到特定的数字,将其相乘得到阶乘。 现在研究如下所示的代码 [factorial_1.rb](code/factorial_1.rb)

W
wizardforcel 已提交
312
```rb
W
init  
wizardforcel 已提交
313 314 315 316 317 318 319 320 321 322 323 324 325
# 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

W
wizardforcel 已提交
326
```rb
W
init  
wizardforcel 已提交
327 328 329 330 331
Factorial of 17 = 355687428096000
```

输出与先前程序 [factorial.rb](code:factorial.rb) 相同。 请仔细查看上述程序中名为`factorial`的功能。 我列出来看看

W
wizardforcel 已提交
332
```rb
W
init  
wizardforcel 已提交
333 334 335 336 337 338 339 340 341 342
def factorial num
        return 1 if num == 1
        return num * factorial(num-1)
end
```

该功能令人困惑,不是吗? 当我发疯并且想学习编程时,我学习了 C。递归函数的概念是使用阶乘解释的,而且我很长一段时间都没有理解它。 为了避免痛苦,让我详细解释。

取数字 1。它的阶乘为 1。因此,如果遇到 1,则返回 1,如下代码所示

W
wizardforcel 已提交
343
```rb
W
init  
wizardforcel 已提交
344 345 346 347 348 349 350 351
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)`被执行

W
wizardforcel 已提交
352
```rb
W
init  
wizardforcel 已提交
353 354 355 356 357 358 359 360 361 362 363 364
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。 可变数量的参数

W
wizardforcel 已提交
365
假设你不知道向一个函数传递了多少个参数,假设你正在编写一个要添加 N 个数字的函数,则 N 的值未知,那么如何获得可变数量的参数。 键入下面给出的程序 [function_variable_arguments.rb](code/function_variable_arguments.rb) 并执行它。
W
init  
wizardforcel 已提交
366

W
wizardforcel 已提交
367
```rb
W
init  
wizardforcel 已提交
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
# 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

W
wizardforcel 已提交
383
```rb
W
init  
wizardforcel 已提交
384 385 386 387 388 389 390 391
1
Others are:
2
3
4
5
```

W
wizardforcel 已提交
392
因此,程序的输出如上所示。 如你所见,我们将 1,2,3,4,5 作为参数传递,那么`a`只是一个变量,因此它取值为 1,其他变量被变量`**others**` **吸收( 注意,变量名前的星号** 是一种特殊的自变量,它将所有未被前一个自变量吸收的其余自变量存储在其他变量名中(作为数组)。 现在在下面的代码中
W
init  
wizardforcel 已提交
393

W
wizardforcel 已提交
394
```rb
W
init  
wizardforcel 已提交
395 396 397 398 399 400 401 402 403 404 405
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}`中调用此函数,执行它并 注意输出

W
wizardforcel 已提交
406
```rb
W
init  
wizardforcel 已提交
407 408 409 410 411 412 413 414 415 416 417 418 419
# 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

W
wizardforcel 已提交
420
```rb
W
init  
wizardforcel 已提交
421 422 423 424 425 426 427
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) ,执行该程序,其输出将 与上面的程序相同

W
wizardforcel 已提交
428
```rb
W
init  
wizardforcel 已提交
429 430 431 432 433 434 435 436 437 438 439 440 441
# 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`称为

W
wizardforcel 已提交
442
```rb
W
init  
wizardforcel 已提交
443 444 445 446
some_function "Yoda", jedi: 100, sword: 100, seeing_future: 100
```

在函数中,我们将第二个参数作为哈希值进行传递,但是如上所示,它应予注意,请注意,我们已避免使用花括号,并且它仍然有效。 这才是重点。