Ruby 表达式
最后修改于 2023 年 10 月 18 日
在本 Ruby 教程中,我们将介绍表达式。
表达式由操作数和运算符构成。表达式的运算符指示对操作数应用哪些操作。表达式中运算符的求值顺序由运算符的优先级和结合性决定。
运算符是一个特殊的符号,表示要执行某个过程。编程语言中的运算符取自数学。程序员使用数据。运算符用于处理数据。一个操作数是运算符的输入(参数)之一。
Ruby 运算符
下表显示了常见的 Ruby 运算符,按优先级排序(优先级从高到低)
类别 | 符号 |
---|---|
解析、访问运算符 | :: . |
数组运算符 | [ ] [ ]= |
求幂 | ** |
非、补码、一元加、减 | ! ~ + - |
乘、除、取模 | * / % |
加法、减法 | + - |
移位运算符 | << >> |
按位与 | & |
按位或、逻辑或 | ^ | |
关系运算符 | > >= < <= |
按位或、逻辑或 | ^ | |
相等、模式匹配运算符 | <=> == === != =~ !~ |
逻辑与 | && |
逻辑或 | || |
范围运算符 | .. ... |
三元 | ?: |
赋值运算符 | = += -= *= **= /= %= &= |=
^= <<= >>= ||= &&= |
备用否定 | not |
备用逻辑或、与 | or and |
表格同一行上的运算符具有相同的优先级。
一个运算符通常有一个或两个操作数。那些只处理一个操作数的运算符称为一元运算符。那些处理两个操作数的被称为二元运算符。还有一个三元运算符 ?:
,它处理三个操作数。
某些运算符可能用于不同的上下文中。例如 +
运算符。从上表可以看出,它用于不同的情况下。它添加数字,连接字符串,指示数字的符号。我们说运算符被重载。
Ruby 符号运算符
有两个符号运算符:+
和 -
。它们用于表示或改变值的符号。
#!/usr/bin/ruby puts +2 puts -2
+ 和 - 符号表示值的符号。加号可用于表示我们有一个正数。它可以省略,并且通常是这样做的。
在下面的示例中,我们使用减号。
#!/usr/bin/ruby a = 1 puts a puts -(a) puts -(-(a))
减号会改变值的符号。
$ ./minus_oper.rb 1 -1 1
Ruby 赋值运算符
赋值运算符 = 将一个值赋给一个变量。变量是值的占位符。在数学中,=
运算符具有不同的含义。在等式中,=
运算符是一个相等运算符。等式的左边等于右边。
x = 1 puts x # prints 1
在这里,我们将一个数字赋给 x
变量。
x = x + 1 puts x # prints 2
前面的表达式在数学上没有意义。但它在编程中是合法的。该表达式将 1 加到 x
变量。右边等于 2,并将 2 赋给 x
。
3 = x;
此代码示例会导致语法错误。我们不能将值赋给字面量。
Ruby 解析、成员访问运算符
这两个运算符在运算符层次结构中具有最高的优先级。这意味着它们总是先被求值。
#!/usr/bin/ruby class MyMath Pi = 3.1415926535 end module People Name = "People" end puts MyMath::Pi puts People::Name
在第一个示例中,我们介绍了 ::
命名空间解析运算符。它允许访问在另一个类或模块内定义的常量、模块或类。它用于提供命名空间,以便方法和类名不会因不同作者的其他类而发生冲突。
class MyMath Pi = 3.1415926535 end module People Name = "People" end
我们有一个简单的模块和一个类。每个都有一个常量定义。
puts MyMath::Pi puts People::Name
我们使用 ::
运算符访问两者的常量。
$ ./resolution.rb 3.1415926535 People
点 .
运算符是一个成员访问运算符。它用于调用对象的方法。
#!/usr/bin/ruby class Person def initialize name, age @name = name @age = age end def info "#{@name} is #{@age} years old" end end p = Person.new "Jane", 17 puts p.info puts "ZetCode".reverse
在我们的示例中,我们有两个对象。一个用户定义的和一个预定义的。我们使用点运算符来处理这些对象。
p = Person.new "Jane", 17 puts p.info
在这两行中,点运算符调用了两种方法:new 和 info。
puts "ZetCode".reverse
字符串是一个内置对象,它有一个反转方法。正在调用它。
$ ./member_access.rb Jane is 17 years old edoCteZ
Ruby 连接字符串
在 Ruby 中,+
运算符也用于连接字符串。当运算符在不同的上下文中以不同的方式使用时,我们说它被重载。
#!/usr/bin/ruby puts "Return " + "of " + "the " + "King" puts "Return ".+"of ".+ "the ".+"King"
我们使用字符串连接运算符将三个字符串连接在一起。
puts "Return " + "of " + "the " + "King"
我们使用 + 运算符连接四个字符串。
puts "Return ".+"of ".+ "the ".+"King"
在幕后,+
运算符是一个 Ruby 方法。字符串字面量是一个对象。我们使用访问 .
运算符调用对象的方法。
$ ./catstrings.rb Return of the King Return of the King
这就是我们运行 catstrings.rb
程序时得到的结果。
Ruby 递增、递减运算符
Ruby 没有这样的运算符。
x++; x = x + 1; ... y--; y = y - 1;
这些是 C 中的递增、递减运算符。
如果您熟悉 Java、C、C++,您就会知道这些运算符。它们在 Ruby 中不可用。Python 语言也没有它们。
Ruby 算术运算符
以下是 Ruby 中的算术运算符表。
符号 | 名称 |
---|---|
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 余数 |
** | 功率 |
在下一个示例中,我们使用算术运算。
#!/usr/bin/ruby a = 10 b = 11 c = 12 puts a + b + c puts c - a puts a * b puts c / 3 puts c % a puts c ** a
在前面的示例中,我们使用了加法、减法、乘法、除法和余数运算。这都是数学中熟悉的。
puts c % a
%
运算符称为余数或模运算符。它找到一个数除以另一个数的余数。例如,9 % 4
,9 模 4 是 1,因为 4 进入 9 两次,余数为 1。
$ ./arithmetic.rb 33 2 110 4 2 61917364224
接下来,我们展示整数和浮点数除法之间的区别。
#!/usr/bin/ruby puts 5 / 2 puts 5 / 2.0 puts 5.0 / 2 puts 5.to_f / 2
在前面的示例中,我们对两个数进行了除法运算。
puts 5 / 2
表达式中的两个操作数都是整数。我们已经进行了整数除法。除法运算返回的值是一个整数。当我们除以两个整数时,结果是一个整数。
puts 5 / 2.0 puts 5.0 / 2 puts 5.to_f / 2
如果其中一个值是浮点数(或两者都是),我们执行浮点除法。浮点值有一个小数点。我们也可以调用 to_f
方法将整数转换为浮点数。
$ ./division.rb 2 2.5 2.5 2.5
在这里我们看到了 division.rb 程序的结果。
Ruby 有其他执行除法的方法。这些可用作方法调用。
#!/usr/bin/ruby puts 5.div 2.0 puts 5.fdiv 2 puts 5.quo 2 puts 5.0.quo 2.0
在上面的例子中,我们有 div
、fdiv
和 quo
方法。
puts 5.div 2.0
div
方法总是执行整数除法。即使操作数是浮点值。
puts 5.fdiv 2
fdiv
方法总是执行浮点除法。
puts 5.quo 2 puts 5.0.quo 2.0
quo
方法执行最精确的除法。如果任何操作数是浮点数,则返回一个浮点数,否则返回有理数。
$ ./division2.rb 2 2.5 5/2 2.5
Ruby 布尔运算符
在 Ruby 中,我们有以下逻辑运算符。布尔运算符也称为逻辑运算符。
符号 | 名称 |
---|---|
&& | 逻辑与 |
|| | 逻辑或 |
! | 逻辑非 |
布尔运算符处理真值。Ruby 有额外的备用布尔运算符。它们是 and
、or
& not
。它们做同样的事情,除了它们具有较低的优先级。这种重复来自 Perl 语言,Perl 语言需要具有较低优先级的布尔运算符。
#!/usr/bin/ruby x = 3 y = 8 puts x == y puts y > x if y > x then puts "y is greater than x" end
许多表达式都产生布尔值。布尔值用于条件语句。
puts x == y puts y > x
关系运算符始终产生布尔值。这两行打印 false 和 true。
if y > x then puts "y is greater than x" end
仅当括号内的条件满足时,才执行 if
语句的主体。表达式 x > y
返回 true,因此消息“y is greater than x”将打印到终端。
下一个示例显示了逻辑 and
运算符。
#!/usr/bin/ruby puts true && true puts true && false puts false && true puts false && false
只有当两个操作数都为真时,and
运算符才计算为真。
$ ./and_operator.rb true false false false
只有一个表达式的结果是真。
如果任何一个操作数为真,则逻辑或 ||
运算符的计算结果为真。
#!/usr/bin/ruby puts true || true puts true || false puts false || true puts false || false
如果运算符的任一边为 true,则操作的结果为 true。
$ ./or_operator.rb true true true false
三个表达式的结果为布尔真。
否定 !
使真为假,假为真。
#!/usr/bin/ruby puts !0 puts !1 puts !true puts !false puts ! (4<3) puts ! "Ruby".include?("a")
该示例展示了否定运算符的实际应用。
$ ./negation.rb false false false true true true
||
和 &&
运算符是短路求值的。短路求值意味着仅当第一个参数不足以确定表达式的值时,才会对第二个参数进行求值:当逻辑与的第一个参数求值为假时,整体值必须为假;并且当逻辑或的第一个参数求值为真时,整体值必须为真。短路求值主要用于提高性能。
一个例子可以更清楚地说明这一点。
#!/usr/bin/ruby def one puts "Inside one" false end def two puts "Inside two" true end puts "Short circuit" if one && two puts "Pass" end puts "##############################" if two || one puts "Pass" end
我们在示例中有两种方法。它们用作布尔表达式中的操作数。我们看看是否调用了它们。
if one && two puts "Pass" end
一种方法返回 false。短路 &&
不计算第二种方法。没有必要。一旦一个操作数为假,逻辑结论的结果就总是假。只有“Inside one”打印到控制台。
puts "##############################" if two || one puts "Pass" end
在第二种情况下,我们使用 ||
运算符并将两种方法用作第一个操作数。在这种情况下,将“Inside two”和“Pass”字符串打印到终端。再次没有必要对第二个操作数求值,因为一旦第一个操作数求值为真,逻辑或就总是真。
$ ./short_circuit.rb Short circuit Inside one ############################## Inside two Pass
我们看到了 shortcircuit.rb 程序的结果。
Ruby 关系运算符
关系运算符用于比较值。这些运算符始终产生布尔值。
符号 | 含义 |
---|---|
< | 小于 |
<= | 小于或等于 |
> |
大于 |
>= | 大于或等于 |
关系运算符也称为比较运算符。
#!/usr/bin/ruby p 3 < 4 p 3 > 5 p 3 >= 3
表达式 3 < 4
返回 true,因为 3 小于 4。表达式 3 > 5
返回 false,因为它不是真的 3 大于 5。
Ruby 按位运算符
十进制数对人类来说是自然的。二进制数对计算机是原生的。二进制、八进制、十进制或十六进制符号只是相同数字的表示法。按位运算符处理二进制数的位。
符号 | 含义 |
---|---|
~ | 按位取反 |
^ | 按位异或 |
& | 按位与 |
| | 按位或 |
<< | 左移 |
>> | 右移 |
按位运算符很少用于像 Ruby 这样的高级语言中。
#!/usr/bin/ruby puts ~ 7 # prints -8 puts ~ -8 # prints 7 puts 6 & 3 # prints 2 puts 3 & 6 # prints 2 puts 6 ^ 3 # prints 5 puts 3 ^ 6 # prints 5 puts 6 | 3 # prints 7 puts 3 | 6 # prints 7 puts 6 << 1 # prints 12 puts 1 << 6 # prints 64 puts 6 >> 1 # prints 3 puts 1 >> 6 # prints 0
在上面的代码示例中,我们展示了所有 6 个运算符。
puts ~ 7 # prints -8 puts ~ -8 # prints 7
按位取反运算符将每个 1 变为 0,将 0 变为 1。运算符反转数字 7 的所有位。其中一个位也确定了数字是否为负数。如果我们再次否定所有位,我们将再次得到数字 7。
puts 6 & 3 # prints 2 puts 3 & 6 # prints 2
按位与运算符对两个数字执行逐位比较。只有当操作数中对应的位都为 1 时,结果的该位才为 1。
puts 6 ^ 3 # prints 5 puts 3 ^ 6 # prints 5
按位异或运算符对两个数字执行逐位比较。当操作数中对应的位任一为 1 但不是两者都为 1 时,结果的该位为 1。
puts 6 | 3 # prints 7 puts 3 | 6 # prints 7
按位或运算符在两个数字之间执行按位比较。如果操作数中相应的位之一为 1,则位位置的结果为 1。
puts 6 << 1 # prints 12 puts 1 << 6 # prints 64 puts 6 >> 1 # prints 3 puts 1 >> 6 # prints 0
按位移位运算符将位向右或向左移动。这些运算符也称为算术移位。
Ruby 复合赋值运算符
复合赋值运算符由两个运算符组成。它们是简写运算符。
#!/usr/bin/ruby a = 0 a = a + 1 a += 1 puts a b = 0 b = b - 8 b -= 8 puts b
+=
和 -=
复合运算符是这些简写运算符之一。它们不如完整的表达式可读,但经验丰富的程序员经常使用它们。
a = a + 1 a += 1
这两行做同样的事情;它们将 1 加到 a 变量。
其他复合运算符是
-= *= **= /= %= &= |= <<= >>=
Ruby 运算符优先级
运算符优先级告诉我们哪些运算符首先被求值。优先级级别对于避免表达式中的歧义是必需的。
以下表达式的结果是 28 还是 40?
3 + 5 * 5
就像在数学中一样,乘法运算符的优先级高于加法运算符。所以结果是 28。
(3 + 5) * 5
要更改求值顺序,我们可以使用括号。括号内的表达式始终首先被求值。
#!/usr/bin/ruby puts 3 + 5 * 5 puts (3 + 5) * 5 puts ! true | true puts ! (true | true)
在此代码示例中,我们展示了一些常见的表达式。每个表达式的结果都取决于优先级级别。
puts 3 + 5 * 5
这行打印 28。乘法运算符的优先级高于加法。首先计算 5*5
的乘积。然后加上 3。
puts ! true | true
在这种情况下,否定运算符具有更高的优先级。首先,第一个真值被否定为假,然后 | 运算符将假和真结合起来,最终得到真。
$ ./precedence.rb 28 40 true false
Ruby 结合性
有时,优先级不足以确定表达式的结果。还有另一条规则称为结合性。运算符的结合性决定了具有相同优先级级别的运算符的求值顺序。
9 / 3 * 3
此表达式的结果是什么,9 还是 1?乘法、除法和模运算符都是从左到右关联的。因此,表达式的求值方式如下:(9 / 3) * 3
,结果为 9。
算术、布尔、关系和按位运算符都具有从左到右的结合性。
另一方面,赋值运算符是右结合的。
a = b = c = d = 0 print a, b, c, d # prints 0000
如果结合律是从左到右,那么前面的表达式将无法实现。
复合赋值运算符是右到左结合的。
j = 0 j *= 3 + 1 puts j
您可能期望结果是 1。但实际结果是 0,因为结合性。右侧的表达式首先被求值,然后应用复合赋值运算符。
Ruby 范围运算符
Ruby 有两个范围运算符。它们用于快速创建对象的范围。最常见的是数字或字母的范围。
..
范围运算符(两个点)创建包含范围。...
运算符(三个点)创建一个排除范围,其中范围的高值被排除。
#!/usr/bin/ruby p (1..3).to_a p (1...3).to_a p ('a' .. 'l').to_a
在示例中,我们使用两个范围运算符来创建数字和字符的范围。
p (1..3).to_a p (1...3).to_a
这两行使用两个范围运算符创建两个范围。范围对象转换为数组。第一个范围的值为 1、2 和 3,而第二个范围的值为 1 和 2。
p ('a' .. 'l').to_a
在这里,我们使用 ..
范围运算符创建从 'a' 到 'l' 的字母数组。
$ ./range_operator.rb [1, 2, 3] [1, 2] ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]
Ruby 三元运算符
三元运算符 ?:
是一个条件运算符。 对于我们想要根据条件表达式选择两个值之一的情况,它是一个方便的运算符。
cond-exp ? exp1 : exp2
如果 cond-exp 为 true,则评估 exp1 并返回结果。 如果 cond-exp 为 false,则评估 exp2 并返回其结果。
#!/usr/bin/ruby age = 32 adult = age >= 18 ? true : false if adult then puts "Adult" else puts "Not adult" end
在大多数国家/地区,成年是根据您的年龄来决定的。如果您超过一定年龄,您就是成年人。在这种情况下,我们可以使用三元运算符。
adult = age >= 18 ? true : false
首先,求值赋值运算符右侧的表达式。三元运算符的第一阶段是条件表达式求值。因此,如果年龄大于或等于 18,则返回 ?
字符之后的值。如果没有,则返回 :
字符之后的值。然后将返回的值分配给 adult 变量。
$ ./ternary.rb Adult
一个 32 岁的人是成年人。
计算质数
我们将计算素数。
#!/usr/bin/ruby nums = (4..50).to_a puts "Prime numbers:" print "2 3 " nums.each do |i| not_prime = false (2..Math.sqrt(i).ceil).each do |j| not_prime = true if i % j == 0 end print i, " " unless not_prime end puts
在上面的例子中,我们处理了几个运算符。素数(或素数)是一个自然数,它恰好有两个不同的自然数除数:1 和它本身。我们取一个数字,然后用数字除以它,从 2 到取出的数字。实际上,我们不必尝试所有较小的数字,我们可以除以小于所选数字的平方根的数字。该公式将起作用。该算法的核心是余数除法运算符,也称为模运算符。
nums = (4..50).to_a
我们从这些数字计算素数。
print "2 3 "
我们跳过 2 和 3 的计算。它们是素数。
not_prime = false
not_prime
是一个标志,用于指示所选数字不是素数。我们假设所选数字是素数,直到后来证明并非如此。
(2..Math.sqrt(i).ceil).each do |j| not_prime = true if i % j == 0 end
如果只用小于一个数平方根的数字进行模除,我们就可以。如果余数除法运算符对任何 i 值返回 0,则所讨论的数字不是素数。
print i, " " unless not_prime
如果未设置 not_prime
标志,则打印该数字。
上面的示例旨在演示几个运算符。实际上,有一种更容易的方法来计算素数。Ruby 有一个用于计算素数的模块。
#!/usr/bin/ruby require 'prime' Prime.each(50) do |i| print i, " " end puts
一个示例,使用 Ruby 素数模块计算最大为 50 的素数。
require 'prime'
我们包含素数模块。
Prime.each(50) do |i| print i, " " end
我们计算最大为上限 — 50 的素数。
$ ./primes2.rb 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
从这个输出中,我们可以看到 2 和 50 之间的素数。
在本 Ruby 教程中,我们介绍了表达式。