Ruby 的 && 运算符
Ruby's && operator
在 PHP 中,计算结果为 true
:
$a = 1
$b = 2
var_dump($a && $b); // true
在 ruby 中,计算结果为 2
:
a = 1
b = 2
p a && b # 2
为什么 ruby return 最后一个语句的值(当第一个为真且第二个也为真时)而不是 return 布尔值?
我有两个数组,我用外部迭代器对它们进行迭代:
a = [1,2,3].to_enum
b = [5,6,7].to_enum
c = []
begin
while a_next = a.next && b_next = b.next
result = a_next + b_next
p "a[x] + b[x] = c[x] = #{a_next} + #{b_next} = #{result}"
c << result
end
rescue
p c
end
条件:while a_next = a.next && b_next = b.next
设置a_next = b_next
(我认为第一个问题似乎与此行为有关)。
但是当我包装两个 &&
操作数时,它按预期工作:(a_next = a.next) && (b_next = b.next)
.
这里有几个方面。
Why does ruby returns the value of the last statement (when the first is true and the second one is also true) and does not return a boolean?
因为这样更有用。这种语义实际上非常有用,以至于 PHP 7 添加了它(但作为新的运算符,??
)。在 Ruby 中,就像在 PHP 中一样,所有值都是真值或假值。与 PHP 不同,Ruby 对此有更严格的想法:只有 false
和 nil
是假的,其他都是真值。这使您可以轻松地设置默认值:
name = options[:name] || "John Doe"
如果options[:name]
没有找到并且returnsnil
,那么那部分是假的,||
的右边将被返回;否则,将返回 options[:name]
。
在大多数情况下,您不需要 布尔值,因为真或假就足够了。如果你真的真的想要一个布尔值,例如为了不从 class 中泄露私人信息,惯用语 !!value
很常见:
def has_name?
!!(self.name || self.nickname)
end
!
(否定)的结果总是布尔值;如果你否定两次,你会将真值转换为 true
,将假值转换为 false
.
最后,
The thing that bugs me is the condition in the while
- while a_next = a.next && b_next = b.next
- written like this, it always sets a_next = b_next
(the first question seems related to this behavior, I think). But when i wrap the two &&
operands - it works as expected - (a_next = a.next) && (b_next = b.next) # works ok
.
然后你需要把它们包起来。这是由于运算符的优先级,并且设计成这样,因为写
更正常
blue_green = colour == :blue || colour == :green
比
blue_green = (colour == :blue || colour == :green)
还有一组布尔运算符实际上设计为像您建议的那样工作,唯一的区别是优先级,因此您可以编写并让它工作:
while a_next = a.next and b_next = b.next
与
相同
while (a_next = a.next) && (b_next = b.next)
警告:不正确地使用 and
和 or
运算符代替 &&
和 ||
是一个常见的错误,许多风格指南完全禁止它们(他们在这种情况下 only 有用 - 循环条件内的赋值 - 它可以用括号解决)。 E.g.:
The and
and or
keywords are banned. It's just not worth it. Always use &&
and ||
instead.
Truthy 和 Falsy 与 true
和 false
考虑
3 && 4 #=> 4
3 && false #=> false
nil && 4 #=> nil
false && nil #=> false
3 || 4 #=> 3
3 || false #=> 3
nil || 4 #=> 4
false || nil #=> nil
回想一下,在 Ruby、false
和 nil
中按逻辑求值 false
("falsy"),其他所有值按逻辑求值 true
("truthy")。因此,通常不需要将虚假值转换为 false
或将 truthy
值转换为 true
。例如,我们可以写
3 ? "happy" : "sad"
#=> "happy"
nil ? "happy" : "sad"
#=> "sad"
将 Truthy 和 Falsy 转换为 true
和 false
如果您坚持,可以使用双感叹号技巧将 truthy
和 falsey
值转换为 true
和 false
:
!!3 => !(!3) => !(false) => true
!!nil => !(!nil) => !(true) => false
&&
和 ||
的交易技巧
让 &&
和 ||
定义它们的方式通常非常方便。例如,如果 h
是一个散列,假设我们写
h[k] = (h[k] || []) << 3
如果h
没有键k
、h[k] #=> nil
,则表达式简化为
h[k] = [] << 3
#=> [3]
另一个例子是对整数数组 arr
或 nil
:
的元素求和
arr.reduce(0) { |t,n| t + (n || 0) }
还有许多其他创造性的方法可以利用 Ruby 中的这两个运算符,如果它们只是返回 true
或 false
.[=53=,则这些方法是不可能的]
代码片段
现在让我们转到您提到的代码片段。
首先,你的救援条款有点让人分心,while
的论点总是正确的,因此有点人为,所以让我们写下你的内容。
a = [1,2,3].to_enum
#=> #<Enumerator: [1, 2, 3]:each>
b = [5,6,7].to_enum
#=> #<Enumerator: [5, 6, 7]:each>
c = []
loop do
a_next = a.next
b_next = b.next
result = a_next + b_next
p "a[x] + b[x] = c[x] = #{a_next} + #{b_next} = #{result}"
c << result
end
# "a[x] + b[x] = c[x] = 1 + 5 = 6"
# "a[x] + b[x] = c[x] = 2 + 6 = 8"
# "a[x] + b[x] = c[x] = 3 + 7 = 10"
p c
#=> [1, 2, 3]
当 a.next
在枚举 a
的所有元素后执行时,它将引发 StopIteration
异常(参见 Enumerator#next). The reason I chose Kernel#loop 而不是 while
前者通过跳出循环来处理 StopIteration
异常。因此,不需要救援。(顺便说一句,如果你用 while
救援,你会想要 rescue StopIteration
,而不是救援所有例外。)
在 PHP 中,计算结果为 true
:
$a = 1
$b = 2
var_dump($a && $b); // true
在 ruby 中,计算结果为 2
:
a = 1
b = 2
p a && b # 2
为什么 ruby return 最后一个语句的值(当第一个为真且第二个也为真时)而不是 return 布尔值?
我有两个数组,我用外部迭代器对它们进行迭代:
a = [1,2,3].to_enum
b = [5,6,7].to_enum
c = []
begin
while a_next = a.next && b_next = b.next
result = a_next + b_next
p "a[x] + b[x] = c[x] = #{a_next} + #{b_next} = #{result}"
c << result
end
rescue
p c
end
条件:while a_next = a.next && b_next = b.next
设置a_next = b_next
(我认为第一个问题似乎与此行为有关)。
但是当我包装两个 &&
操作数时,它按预期工作:(a_next = a.next) && (b_next = b.next)
.
这里有几个方面。
Why does ruby returns the value of the last statement (when the first is true and the second one is also true) and does not return a boolean?
因为这样更有用。这种语义实际上非常有用,以至于 PHP 7 添加了它(但作为新的运算符,??
)。在 Ruby 中,就像在 PHP 中一样,所有值都是真值或假值。与 PHP 不同,Ruby 对此有更严格的想法:只有 false
和 nil
是假的,其他都是真值。这使您可以轻松地设置默认值:
name = options[:name] || "John Doe"
如果options[:name]
没有找到并且returnsnil
,那么那部分是假的,||
的右边将被返回;否则,将返回 options[:name]
。
在大多数情况下,您不需要 布尔值,因为真或假就足够了。如果你真的真的想要一个布尔值,例如为了不从 class 中泄露私人信息,惯用语 !!value
很常见:
def has_name?
!!(self.name || self.nickname)
end
!
(否定)的结果总是布尔值;如果你否定两次,你会将真值转换为 true
,将假值转换为 false
.
最后,
The thing that bugs me is the condition in the
while
-while a_next = a.next && b_next = b.next
- written like this, it always setsa_next = b_next
(the first question seems related to this behavior, I think). But when i wrap the two&&
operands - it works as expected -(a_next = a.next) && (b_next = b.next) # works ok
.
然后你需要把它们包起来。这是由于运算符的优先级,并且设计成这样,因为写
更正常blue_green = colour == :blue || colour == :green
比
blue_green = (colour == :blue || colour == :green)
还有一组布尔运算符实际上设计为像您建议的那样工作,唯一的区别是优先级,因此您可以编写并让它工作:
while a_next = a.next and b_next = b.next
与
相同while (a_next = a.next) && (b_next = b.next)
警告:不正确地使用 and
和 or
运算符代替 &&
和 ||
是一个常见的错误,许多风格指南完全禁止它们(他们在这种情况下 only 有用 - 循环条件内的赋值 - 它可以用括号解决)。 E.g.:
The
and
andor
keywords are banned. It's just not worth it. Always use&&
and||
instead.
Truthy 和 Falsy 与 true
和 false
考虑
3 && 4 #=> 4
3 && false #=> false
nil && 4 #=> nil
false && nil #=> false
3 || 4 #=> 3
3 || false #=> 3
nil || 4 #=> 4
false || nil #=> nil
回想一下,在 Ruby、false
和 nil
中按逻辑求值 false
("falsy"),其他所有值按逻辑求值 true
("truthy")。因此,通常不需要将虚假值转换为 false
或将 truthy
值转换为 true
。例如,我们可以写
3 ? "happy" : "sad"
#=> "happy"
nil ? "happy" : "sad"
#=> "sad"
将 Truthy 和 Falsy 转换为 true
和 false
如果您坚持,可以使用双感叹号技巧将 truthy
和 falsey
值转换为 true
和 false
:
!!3 => !(!3) => !(false) => true
!!nil => !(!nil) => !(true) => false
&&
和 ||
让 &&
和 ||
定义它们的方式通常非常方便。例如,如果 h
是一个散列,假设我们写
h[k] = (h[k] || []) << 3
如果h
没有键k
、h[k] #=> nil
,则表达式简化为
h[k] = [] << 3
#=> [3]
另一个例子是对整数数组 arr
或 nil
:
arr.reduce(0) { |t,n| t + (n || 0) }
还有许多其他创造性的方法可以利用 Ruby 中的这两个运算符,如果它们只是返回 true
或 false
.[=53=,则这些方法是不可能的]
代码片段
现在让我们转到您提到的代码片段。
首先,你的救援条款有点让人分心,while
的论点总是正确的,因此有点人为,所以让我们写下你的内容。
a = [1,2,3].to_enum
#=> #<Enumerator: [1, 2, 3]:each>
b = [5,6,7].to_enum
#=> #<Enumerator: [5, 6, 7]:each>
c = []
loop do
a_next = a.next
b_next = b.next
result = a_next + b_next
p "a[x] + b[x] = c[x] = #{a_next} + #{b_next} = #{result}"
c << result
end
# "a[x] + b[x] = c[x] = 1 + 5 = 6"
# "a[x] + b[x] = c[x] = 2 + 6 = 8"
# "a[x] + b[x] = c[x] = 3 + 7 = 10"
p c
#=> [1, 2, 3]
当 a.next
在枚举 a
的所有元素后执行时,它将引发 StopIteration
异常(参见 Enumerator#next). The reason I chose Kernel#loop 而不是 while
前者通过跳出循环来处理 StopIteration
异常。因此,不需要救援。(顺便说一句,如果你用 while
救援,你会想要 rescue StopIteration
,而不是救援所有例外。)