ESS多版本 【TA】【ESS】03 方法

TAAAAAAA

天王
管理成员
2024/06/16
200
3
27
1,210
3: Methods 方法

In short, all methods are, are shortcuts of longer bits of code. To get straight into creating your own method, methods are created by writing the def keyword (as in “define”) followed by the name you want to give your method, and then closed with end, just like with if-statements.

简单的说,方法就是一段代码的快捷方式。
当你想要在多个地方运行相同的代码的时候,你实际上并不需要在每个地方都复制粘贴上那一段代码,而是你可以为那一段代码写一个方法,只需要复制粘贴这个方法就好了。
因为这个方法是这段代码的快捷方式,所以当你运行这个方法的时候,就相当于是运行了这一段代码。
除了可以减少代码的行数之外,使用方法还可以让你更加方便的修改代码。
比如说,在你写好代码之后你想要修改,如果你没有使用方法的话,你需要在每一个你复制粘贴的地方都进行修改,而如果你使用了方法的话,你就只需要在方法里修改就好了。

你可以使用def xxx这样的格式来新定义一个方法,其中def就是define(定义)的简写,xxx就是你的方法名,我们来看下面这个例子:

Ruby:
def my_method
  print "Printing from my_method!"
end

my_method #=> "Printing from my_method!"

In this example, we’ve created a new method called my_method, which prints out "Printing from my_method!". We then close that method with end and call/run/execute the code inside that method by simply writing down the name of that method, my_method.

在上面的例子中,我们新写了,或者说创建了,或者说定义了一个新的方法,这个方法的名字叫做my_method(我的方法),同时也可以发现,定义方法也和if条件句一样,你需要在结尾使用end。

额外补充,定义方法其实也和if条件句一样,并不是一定需要end,在某些情况下是可以省略end的,就比如说例子的这种情况就是可以省略end的,但是,我还是希望你和记if-end对一样,定义方法记def-end对。

接着,我们运行了my_method这个方法,这个方法会打印出“Printing from my_method!”(来自我的方法的打印)。
也就是说此时,不管你运行my_method,还是运行print "Printing from my_method!",它们的结果是一样的,因为my_method这个方法就是print "Printing from my_method!"。

In various other languages, to call a method you have to follow the method name with (). In Ruby, those parentheses are optional. They make it an explicit call; more on this near the end of this tutorial.
 
最后编辑:

TAAAAAAA

天王
管理成员
2024/06/16
200
3
27
1,210
Method scope 方法的作用域

Let’s try to use a variable we created outside of the method inside the method.

让我们在方法里面使用一个在方法外面创建的变量,请看下面的例子:

Ruby:
a = 10

def work
  print a
end

work #=> NameError: Undefined variable or method 'a'
# 命名错误,a是一个没有被定义的变量或者方法。

The work method is trying to access the a variable we created before defining the method. But methods cannot access normal variables that were created outside the method.

我们在work(工作)这个方法之前创建了一个叫做a的变量。
接着,work方法正在试图访问a这个变量,但是work方法无法访问在work之外创建的变量。
所以,在运行work时,程序会在work内部寻找a变量,但是,因为程序内部并没有一个叫做a的变量,所以程序报错了。
我们继续来看下面的这个例子:

Ruby:
def work
  a = 10
end

work

print a #=> NameError: Undefined variable or method 'a'

In this example, we defined and called a method called work, which creates a new variable called a inside that method. That variable then exists inside that method, but when you try to call it outside of that method, it raises an error. The variable only exists inside that method.
All this is because methods introduce a new scope. This means you can’t access variables created inside that new scope, and when inside that new scope, you can’t access variables created outside that scope.

在这个例子中,我们新定义了一个叫做work的方法,接着运行work。
通过运行work,我们在work内部创建了一个叫做a的变量,并且a的值是10。
接着,我们试图打印a,这同样也引发了错误。
因为a在work的内部,我们无法在work的外部访问a。

简单的说就是,不同的作用域之间,并不互通,我们来看下面的例子:

Ruby:
# This is the main scope.
# 这里是主作用域。

def work
  # This is the scope of the 'work' method
# 这里是work这个方法的作用域。
end

# This is the main scope.
# 这里是主作用域。

也就是说,当定义一个方法时,相当于新开辟了一块专门用于这个方法的作用域。

Similarly, this is totally possible:

我们再来看下面的这个例子:

Ruby:
a = 10
print a #=> 10

def work
  a = 2
  print a
end

print a #=> 10
# 这里print的a不是work里面的a。

work # This method prints out '2'.
# 运行work,会print出work里面的a的值,也就是2。

# But the 'a' variable remains unchanged.
print a #=> 10
# 我们再次print出a时,a依然是10。
# 因为在work里面的a = 2的赋值行为并不会影响到work外面的a的值。

Whenever the work method is called, it creates a new a variable inside that new scope, which only exists inside that scope. When the method ends, it goes back to the main scope and the a variable of the work scope no longer exists. But since we’re back in the main scope, we still have the a variable of that main scope, which is still 10 because it wasn’t changed.
 
最后编辑:

TAAAAAAA

天王
管理成员
2024/06/16
200
3
27
1,210
Method arguments 方法的参数

Although we can’t actually access variables created outside of a method, we can still give information to that method by giving it an argument.

我们在调用方法时,同时可以给方法传递参数。
有些人也把方法叫做函数,这并不是没有道理的,因为使用函数的概念确实更容易理解。
你其实可以想一下数学里什么是函数,函数其实是一个类似于f(x)这样的结构,f其实就是方法名,而括号里的x其实就是f这个方法,或者说这个函数的参数。
我们来看下面这个例子:

Ruby:
def work(my_argument)
  print my_argument
end

work(1) # Prints out '1'
# 结果会打印出一个1。

When defining a new method, you can specify arguments between parentheses after the name (separated with commas). When calling a method, you can give the method an argument by putting a variable between parentheses after the call to the method, just like work(1).

当我们在定义方法时,我们可以在方法的名字后面加一个括号,括号里的内容就是这个方法的参数。
比如说上面这个例子,work的参数就是my_argument(我的参数)。
并且,work目前只有一个参数,如果你想要多个参数的话,你需要把各个参数用逗号分隔开,当然这里的逗号指的是英文的逗号,比如说这样def work(my_argument1, my_argument2, my_argument3)。
接着,当我们在运行这个方法时,我们需要指定参数的值,在上面的例子中,我们指定了一个1,所以最后的结果就是会打印出一个1。

另外需要说一点,在Ruby中,不管是在定义方法还是调用方法时,不管方法本身有没有参数,参数的括号都是可有可无的。
也就是说,对于一个没有参数的方法,你依然可以加括号,比如这样aaa(),括号里面什么都没有,对于一个有参数的方法,你也可以不加括号,比如这样 aaa :bbb,:bbb是aaa的参数。
但是,有一种情况下括号不能省略,那就是会引起歧义的时候,所以,为了你自己能够“万无一失”,不管你看到的别人的代码是怎么写的,对于你自己来说,你最好都把参数的括号给加上。

To further clarify the method above, this is essentially what happens internally:

为了更好的说明上面的例子,我们来看下面的这段代码:

Ruby:
def work
  my_argument = 1
  print my_argument
end

The method expects 1 argument, which it calls my_argument. The first argument passed when we call the method is 1, so it essentially executes my_argument = 1 inside that method. This means it creates a new variable and allows you to use it inside the method. This also works for multiple arguments:

所以,work(1)其实就是把work的my_argument这个参数指定为1。
我们来看下面这个使用了多个参数的例子:

Ruby:
def work(a, b, c)
  print a * b * c
end

work(1, 2, 3) #=> 6

Look at it like so:

(a, b, c)
|| || ||
(1, 2, 3)

a = 1, b = 2, c = 3

这个例子中的work使用了3个参数,分别是a、b和c。
所以,你在运行work时,同时需要为a,b和c指定值。
并且你指定值的顺序和方法中参数的顺序是一一对应的,正是因为这样,所以这些参数也被称为位置型参数。

The amount of arguments the method expects and the amount of arguments given have to match up though. The work method above expects three arguments (a, b, and c).

work(1, 2, 3) is valid because it provides three arguments.
work(1, 2) is invalid because it provides 2 out of 3 arguments (ArgumentError).
work(1, 2, 3, 4) is invalid because it provides 4 for 3 arguments (ArgumentError).

因为work目前有3个参数,所以你在调用work时,也必须提供3个参数值,不能多,也不能少,否则就会报错。

An exception to this, though, are methods that have default arguments. This means that a certain argument will take on a certain value if no value is given.

但是,有一种情况下,你提供的参数值的数量可以比方法的参数的数量少,那就是当参数具有默认值的时候。
当参数具有默认值时,如果你提供了值,那么参数就会使用你的提供值,而如果你没有提供值的话,那么参数就会使用它的默认值。
我们来看下面这个例子:

Ruby:
def mult(a, b = 1)
  print a * b
end

mult(6, 1) #=> 6
mult(6) #=> 6 (b defaults to 1)
mult(6, 2) #=> 12

However, mult(6, 2, 5) will still raise an ArgumentError, because it’s giving too many arguments. So would mult(), because it doesn’t give any arguments at all.

在上面的例子中,mult(乘)这个方法有两个参数,其中第二个参数b具有默认值1。
也就是说,你在调用mult时,是否指定b的值是可选的,也就是说,mult接受1个或者2个参数。
 
最后编辑:

TAAAAAAA

天王
管理成员
2024/06/16
200
3
27
1,210
Explicit and implicit calls 显式和隐式运行

You can actually have variables and methods with the same name. Let’s say we have a method called var and a variable called var. If you wanted to refer to the variable, you’d write down just var. This normally calls the method, but since there’s also a variable with that name, it’ll give priority to that variable. If you want to call the method though, you’d only have to add () and it would call the method.

变量的名字和方法的名字是可以一样的。
我们来看下面的例子:

Ruby:
var = "Variable!"

def var
  print "Method!"
end

print var #=> "Variable!"
print var() #=> "Method!"
# 可以看到,如果你直接写var的话,会打印出var这个变量。
# 也就是说,同名的变量会有更高的优先级。
# 在存在同名的方法和变量的时候,如果你确实想要的是方法而不是变量,
# 你需要在名字后面加()。

When we use var to refer to a method, we’re basically calling it with implicit syntax. When we use var(), we call that explicit - it can’t be anything else, it’s not ambiguous, and it makes perfectly clear that it’s supposed to call a method. Therefore, if there isn’t a method called var and we use var(), it’ll treat it as a method, even if it doesn’t exist.

你直接写var的写法,叫做隐式运行,程序会先查找是否有叫var的变量,如果没有,再查找是否有叫var的方法,如果有,就会运行var方法,如果方法也没有找到,就会报错。
而你写var()的这种写法,叫做显式运行,因为只有方法后面才会有参数的括号,这种写法就是确定了你就是要运行一个叫做var的方法,当然,如果没有一个叫做var的方法,那么程序就会直接报错。

Ruby:
ar = "Variable!"

print var #=> "Variable!"
print var() #=> NameError: undefined method 'var'
 
最后编辑:

TAAAAAAA

天王
管理成员
2024/06/16
200
3
27
1,210
Return value 返回值

Whenever you call a method, that method call will return something. This means that, when the method call is done, it will give you something. A number, true/false, a string, nothing (nil), or else.

当你调用一个方法时,方法其实会返回xxx,xxx是什么具体取决于方法是怎么写的。
你可以认为xxx就是这个方法的结果,也就是说,方法会在运行之后返回一个结果。
我们来看下面的这个例子:

Ruby:
def work(a_number)
  return a_number
end

work(6) # Nothing happens
# 此时无事发生。

print work(6) #=> 6
# 因为work(6)其实就是,a_number(一个数字)是6, 接着return(返回)6。
# 也就是说,work(6)会返回6,但是如果你要显示work(6)的结果的话,
# 你需要把work(6)给打印出来。

Whenever we call work with an argument, it returns the given argument. So work(6) is basically the same as just 6. And as you can imagine, when you just write down 6 without anything, no assignments, no calls, no nothing, it calls the method and it’s done. It doesn’t do anything, just like work(6) # Nothing happens.

However, we can also make it do something else to better illustrate the use-cases for this.

我们继续来看下面这个例子:

Ruby:
def mult(a, b)
  return a * b
end
# 这个方法会返回a和b相乘的积。

print mult(5, 6) #=> 30

var = 0
print var #=> 0
var = mult(5, 6)
# 重新指定var的值,因为mult(5, 6)会返回30,所以var此时就是30。
print var #=> 30

The line var = mult(5, 6) is essentially turned into var = 30, because mult(5, 6) returns 30. It gives back 30 to where it’s called from.

return also immediately quits the method. Any code that would come after it is never called.

另外还有一点非常重要,就是说return会返回方法的结果,但是方法并不一定需要全部的代码运行完才会得到一个结果。
比如说,在我们之前说过的用else分情况讨论的例子,就不可能完全运行方法里的全部代码,我们只需要运行一部分代码,就能得到结果了。
实际上,return可以返回任何内容,无非是返回的内容是否有意义罢了,return实际上会返回return后面的部分,并且当return了之后,return之后的代码将不会再运行,而是直接退出当前方法。
我们来看下面这个例子:

Ruby:
def work
  return 1
  print "This will never be printed!"
  # 打印出“这行代码永远不会被打印!”,但是这行代码永远不会运行。
  # 所以“这行代码永远不会被打印!”永远不会被打印出来。
  return -1
  # 永远不会返回-1。
end

print work #=> 1
# 因为在work中直接就return了1,所以后面的两行代码永远不会运行。

这里额外再说点关于return的事情,就是你最好避免在方法的中间使用return,尽量早返回(Early Return),即在方法的开头就返回,以及最后隐式返回(请看下面的小节),这个原因就不说了。
如果你发现你需要在方法的中间多次返回的话,那很可能你需要重新组织优化你的方法中的代码。
 
最后编辑:

TAAAAAAA

天王
管理成员
2024/06/16
200
3
27
1,210
Nil 无

The print method itself returns nil, which is another data type (NilClass). You can assign it to variables, too.

关于nil我们在之前的时候已经说过了,这里就不再赘述了,我们来看下面的这个例子:

Ruby:
var = print 1 # Prints out 1
print var #=> nil
print print 1 #=> nil
print print print 1 #=> nil
print print var #=> nil
# print方法本身会返回nil。

Yes, you can call methods in method arguments. And even methods in those. And methods in those.

另外,你也可以把方法的结果作为参数进行传递,我们来看下面的例子:

Ruby:
def mult(a, b = 2)
  return a * b
end

print mult(mult(mult(mult(2)), mult(2))) #=> 64
# 这里看上去有点复杂,你需要一层一层往外展开,请看下面解析。
# mult(mult(mult(4), 4))
# mult(mult(8, 4))
# mult(32)
# 64
# 所以结果就是64。

Wouldn’t suggest ever doing this though. It’s not exactly readable.

虽然可以这么做,但是不建议你这么做,因为这样的写法并不是正常的人类能一下子完全理解的。
 

TAAAAAAA

天王
管理成员
2024/06/16
200
3
27
1,210
Explicit and implicit 显示和隐式

In the examples above we’re explicitly using the return keyword to give back information to where the method was called from, but there’s also an implicit way to do so. By default, methods will return the very last line of code inside the method.

在上面的例子中,我们通过return关键字来返回某些东西,这种写法叫做显式。
实际上,如果你不写return的话,方法会默认返回最后一行,这叫做隐式。
请看下面的这个例子:

Ruby:
def mult(a, b = 2)
  a * b
end

var = mult(2, 2)
print var #=> 4
# 在这个例子中,并没有显式的写return a * b。

So if you were to add anything below the line you’d want to be returned, it would return the new last line instead. This is called implicit.

再来看下面这个例子:

Ruby:
def mult(a, b = 2)
  a * b
  -1
end

var = mult(2, 2)
print var #=> -1
# 因为方法中的最后一行是一个-1,所以这个方法返回的是-1,而不是a * b。

But then again, you could just use the explicit return keyword and all would be good.

你可以使用return来返回你想要返回的内容,请看下面的这样的写法:

Ruby:
def mult(a, b = 2)
  return a * b
  -1 # This is never executed
end

var = mult(2, 2)
print var #=> 4
# 这里通过显式的return,return了a * b。

You can also just write return without anything after it. In that case, it’ll just give back nil and act as though it said return nil.

当return后面没有任何内容的时候,实际上会返回nil。
也就是说,直接return和return nil,是一样的。

A few use-case examples:

请看下面的一些使用return的例子:

Ruby:
def get_seconds(minute)
  if minute < 0
    return "You cannot have a negative amount of minutes!"
    # 返回 “分钟数不可能是一个负数”。
  end
  return minute * 60
  # The 'return' is optional here. It would work fine without.
  # 这一行的return是可选的,因为这一行是方法的最后一行,不写也会隐式返回。
end

print get_seconds(4) #=> 240
print get_seconds(1) #=> 60
print get_seconds(0) #=> 0
print get_seconds(-1) #=> "You cannot have a negative amount of minutes!"
# 可以发现,这个get_second(得到秒数)是一个将minute(分钟)转化为second(秒)的方法。
# 根据输入的minute是否小于0,进而返回不同的值。
#
# 但是,因为前面也说了最好避免在方法中间使用return,
# 所以这个例子是可以简化成下面这样的:
def get_seconds(minute)
  return "You cannot have a negative amount of minutes!"  if minute < 0 # 早返回
  minute * 60 # 隐式返回
end
# 这样4行就完成了,而且更加简洁流畅。

Ruby:
def seconds_lived(age)
  if age < 0
    return 0
  elsif age > 122
    return "Nobody has ever lived longer than 122 years!"
    # 返回 “没有人可以活超过122岁!”。
  end
  return 60 * 60 * 24 * 365 * age
end

print seconds_lived(16) #=> 504576000
print seconds_lived(121) #=> 3815856000
print seconds_lived(122) #=> 3847392000
print seconds_lived(123) #=> "Nobody has ever lived longer than 122 years!"
print seconds_lived(0) #=> 0
print seconds_lived(-1) #=> 0
# 可以发现,这个seconds_lived(活了的秒数)是一个将age转化为second的方法。
# 根据输入的age是否小于0、大于122和大于等于0小于等于122,
# 进而返回不同的值。
#
# 本章作业,请试着优化修改一下这个方法中的return。

本章完。
 
最后编辑:

在线成员

论坛统计

主题
474
消息
2,137
成员
2,909
最新成员
小灵喵~