【问题标题】:Why does the Ruby splat not work for array coercion in conditional assignment?为什么 Ruby splat 不适用于条件赋值中的数组强制?
【发布时间】:2015-04-25 11:26:07
【问题描述】:

虽然 splat (*) 构造通常被称为 splat 运算符,但与其他一元运算符(如否定 (!) 相比,它显然是一个不同的野兽) 运算符。

在赋值 (=) 中使用时,splat 本身可以正常工作(即不包含在括号中),但在与条件赋值 (||=) 一起使用时会产生错误。示例:

a = *(1..3)
#=> [1, 2, 3]

b ||= *(1..3)
SyntaxError: (irb):65: syntax error, unexpected *

我不是在寻找做同样事情的替代方法,而是寻找对 Ruby 内部结构有更好理解的人来解释为什么 splat 构造的这种用法在第一种情况下有效,而在第二种情况下无效。

【问题讨论】:

  • 我猜这只是 ruby​​ 解析器的一个缺陷。
  • 我最好的猜测是a = *(1..3) 实际上被解析得更像a =* (1..3),所以=* 有点像一个运算符,而||=* 会被当作一个不同的不存在的运算符来处理。
  • @amoebe 确实!明确的a =* a || (1..3) 有效。
  • 关于 splat 的有趣之处 - *a = (1..3) 也会将 a 的值更改为 [1,2,3],即使它位于左侧。 *a ||= 1..3 仍然不起作用。
  • @PSkocik 但请记住,||= 在变量未定义和other funny stuff 时确实避免了名称错误。

标签: ruby


【解决方案1】:

这是我对 splat 实际目标的理解。这是用于 Ruby 2.2 MRI/KRI/YARV。

Ruby splat 在赋值期间将对象解构为数组。

a 为假时,这些示例都提供了相同的结果:

a = *(1..3)
a = * (1..3)
a =* (1..3)
a = *1..3
a = * 1..3
a = * a || (1..3)
a = * [1, 2, 3]
=> [1, 2, 3]

splat 在分配过程中进行解构,就好像你这样写:

a = [1, 2, 3]

(注意:splat 调用#to_a。这意味着当你 splat 一个数组时,没有任何变化。这也意味着你可以为你自己的任何类定义你自己的解构类型,如果你愿意的话。)

但是这些语句失败了:

*(1..3)
* 1..3
* [1,2,3]
false || *(1..3)
x = x ? x : *(1..3) 
=> SyntaxError

这些语句失败是因为在 splat 发生时没有发生赋值。

您的问题是这种特殊情况:

b ||= *(1..3)

Ruby 将其扩展为:

b = b || *(1..3)

此语句失败,因为在 splat 发生时没有发生赋值。

如果你需要在自己的代码中解决这个问题,你可以使用一个临时变量,例如:

b ||= (x=*(1..3))

值得一提:当 splat 位于表达式的左侧时,它的用法完全不同。这个 splat 是并行分配期间的低优先级贪心收集器。

例子:

*a, b = [1, 2, 3]  #=> a is [1, 2], b is 3
a, *b = [1, 2, 3]  #=> a is 1, b is [2, 3]

所以这确实解析:

*a = (1..3)  #=> a is (1..3)

它将a 设置为右侧的所有结果,即范围。

在 splat 可以理解为解构器或收集器的极少数情况下,解构器具有优先级。

这一行:

x = * y = (1..3)

评估为:

x = *(y = (1..3)) 

不是这个:

x = (*y = (1..3)) 

【讨论】:

  • 大量 +1。很棒的答案。
  • @fylooi 我为你添加了更多细节。
  • @joelparkerhenderson:那么,我是否正确理解,splat 的这种行为与赋值运算符密切相关(可能在语言的较低级别?)
  • 刚刚想起另一个不太可解释的 splat 用例 - [*input] - 如果 input 是一个数组,则返回 input,否则返回 [input]。有趣的部分是,这是在没有分配的情况下发生的。
  • @fylooi 到您在数组上没有分配的 splat 点:没有分配的 splat 仅调用 #to_a,Array 将其定义为返回自我。所以在没有赋值的情况下飞溅一个数组不会做任何事情。 (另请参阅 Evgney 关于 splat with 分配的评论,例如隐式分配)
【解决方案2】:

通过在你的表达式中添加*(1..3),你会得到1, 2, 3,当它被赋值时,它的行为就像质量赋值一样,但 ruby​​ 似乎不支持条件赋值。 IE。 a=1,2,3 只是 ruby​​ 的语法糖。只需显式使用数组:

a ||= [*1..3] #=> [1, 2, 3]

实际上你在这里只使用了部分 splat 功能 - 它是 autoconversion to array :) 所以你可以简单地做:

a ||= (1..3).to_a #=> [1, 2, 3]

【讨论】:

  • 我认为他们没有添加大量条件赋值来保持代码易于理解,如果你写 a,b ||= 1,2 不清楚 ruby​​ 是否应该只分配一个参数,如果另一个不是 nil 或者只是让它们保持不变...
猜你喜欢
  • 2017-06-11
  • 2013-04-10
  • 2016-06-16
  • 2013-09-22
  • 2013-05-14
  • 2014-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多