为什么带赋值和尾随条件的Ruby return与赋值的表现不同?

hi3rlvi2  于 2023-02-03  发布在  Ruby
关注(0)|答案(2)|浏览(126)

更新:我的思维模式在这里出错了。方法f定义了一个参数a。如果我将这个参数命名为b,那么f确实会返回'oh'而不是“2”。

感谢下面的@Holgur帮助我重新分析并看到我的错误。
考虑方法f

def f(a)
  return a = 2 if !a.nil?
  return 'oh'
end

f(42) # 2
f(nil) # 'oh'

考虑方法g

def g(b)
  return a = b if !a.nil?
  return 'oh'
end

g(42) # 'oh'
g(nil) # 'oh'

考虑方法h

def h(b)
  a = b
  return a if !a.nil?
  return 'oh'
end

h(42) # 42
h(nil) # 'oh'

我期望g(42)返回42?为什么g(42)不返回42
此处fg之间以及gh之间的差值的计算顺序是什么?

prdp8dxp

prdp8dxp1#

return a = b if !a.nil?
return 'oh'

大致相当于

if !a.nil?
  return a = b
end
return 'oh'

因此,Ruby首先测试a是否不为nil(这是假的,因为a实际上是nil,因为它还没有被赋值),因此,if的主体不执行,沿着执行return 'oh'
然而,这里更重要的问题是:为什么这样做的工作在所有和没有导致一个错误,如
NameError:未定义局部变量或方法'a'
尝试访问if中的a变量时,即使它之前未初始化。
这是因为Ruby会用nil初始化变量,如果它们出现在代码赋值语句的左边,即使变量实际上可能没有被赋值。这个行为在https://stackoverflow.com/a/12928261/421705中有进一步的解释。
按照这个逻辑,你的代码只能使用你原来的inline-if,但是如果使用块形式if,就会失败,因为使用这个更长的形式,a只能在if块中初始化,这里,你会得到NoMethodError

8yoxcaq7

8yoxcaq72#

正如@HolgerJust指出的,这是一个词法分析问题。
使用 * 修饰符-[if/unless]* 还有其他一些类似的有趣副作用

def a; 1; end; 
(a if a = true) == a 
#=> false

下面是解析器对它的简单理解:
1.定义方法a()
1.然后解析器会遇到a作为 then 主体的一部分,所以它将这个a标记为方法调用(a()),因为a在这里不是局部变量,并且ruby语法允许在方法调用中省略括号。
1.解析器然后遇到 test expression,在这里它将a标记为局部变量,因为赋值(=
1.执行 * 测试表达式 *,并在过程中将a赋值为true,测试通过
1.现在执行调用a()方法的 then 主体,因为这是#2中标识引用a的方式,这导致表达式(a if a = true)返回1
1.然而,如#4中所指出的,对a的赋值也发生了,因此该比较变为(1) == true

**注意:**如果您删除方法定义,这将由于#2而引发NameError,但仍将进行局部变量赋值。

begin 
  c if c = 1 
rescue NameError 
  puts 'Oh' 
  c
end == c and c == 1 
# 'Oh'
#=> true

相关问题