【问题标题】:Why is this splat method call not working in Ruby?为什么这个 splat 方法调用在 Ruby 中不起作用?
【发布时间】:2013-12-27 13:16:50
【问题描述】:
def method(a, b='a', c, *d)
  [a,b,c, d]
end

p method(1,2,3,4)

不起作用,我不明白为什么,如果我们删除 b 参数一切正常。语法规则说你可以在 splat 参数之前放置具有默认值的参数。

【问题讨论】:

  • 你的 ruby​​ 版本是什么?
  • 我使用的是 Ruby 2.0 版本。
  • 不相关,splat在此处默认后...
  • 问题是默认在c之前,在splat之前。

标签: ruby


【解决方案1】:

具有默认值的变量和 splat 变量可以存在并共存,只要具有默认值的变量可以解释为(一个且唯一的)splat 的初始元素。

因此,这些都将起作用:

def good(a = :a, b);      [a,b];    end
def good(a = :a, *b);     [a,b];    end
def good(a, b = :b, *c);  [a,b,c];  end

但这些不会:

def bad(*a, b = :b);          [a,b];    end  # default after the splat
def bad(a = :a, b, c = :c);   [a,b,c];  end  # parsing would need two splats
def bad(a = :a, b, *c);       [a,b,c];  end  # parsing would need two splats
def bad(*a, b = :b, c);       [a,b,c];  end  # default after the splat

(Tbh,我不知道是什么实现细节阻止 Ruby 在没有歧义的情况下立即接受默认值。但我猜这是为了避免在 splat 上循环两次,而不是一次,即性能,并且可能还有其他原因可能与 Ruby 计算方法的数量有关。)

【讨论】:

  • 丹尼斯,你的括号里的评论,当一个默认值跟在一个 splat 后面时,不能有歧义吗?
  • 我很确定它在理论上可以正常工作,但实现方式不同。我认为,问题在于 Ruby 的内部处理具有默认值的变量的方式与处理 splat 开头的方式相同。这意味着如果它遇到*a, b = :b,它正在处理内部调用一些C函数,在遇到*a时基本上相当于scan_splat_args();再次遇到b = :b。这会引发错误,因为您不能有两个 splats。我怀疑,潜在的理性相当于性能原因,因为这样做会避免前瞻。
  • 丹尼斯,你能举一个例子,说明对method(*a, b = :b) 的调用是明确的吗?我以为原因就这么简单。
  • method(1)method(1,2)method(1,2,3)method(a = :a, *b, c = :c) 如果我们在 splat 之前填充所有默认值,并且从左到右填充,则将是明确的。但是解析起来会更加困难和缓慢。 (不过,如果 Ruby 支持它,我不会对此感到惊讶。)
  • 我终于明白你的意思了,但我可以看到这种行为如何导致混乱和弹性错误。无论如何,几分钟后,我会将我的 cmets 作为非建设性的杂物删除。
【解决方案2】:

默认参数(b)应该在位置参数(ac)之后:

def method(a, c, b='a', *d)
  [a,b,c, d]
end

【讨论】:

  • 那为什么这段代码有效:def method(a='a', b) [a, b] end p method(1,2)
  • @daremarkovic,这在 Ruby 1.8 中是不允许的。在 Ruby 1.9.2(?)+ 中,它等同于 def method(b, a='a') [a, b] end。您可以致电p method(1) 确认。
  • 我明白了,不确定是否等效,尽管完全有道理,但您是否有一些链接可以阅读更多相关信息,找不到任何人这样说的地方。
【解决方案3】:

幕后发生了一些事情:

def method(a=7, b, *c); end
SyntaxError: (irb):25: syntax error, unexpected *
 def method(a=7, b, *c); end
                     ^
def method(*b, *c); end
SyntaxError: (irb):26: syntax error, unexpected *
 def method(*b, *c); end
                 ^  

请注意,这是同样的错误。当您将具有默认值的参数放置在固定参数列表末尾以外的任何位置时,我希望内部 Ruby 使用相同的结构来表示它用于 splat 的结构(用于此的 Ruby C API 方法是 @987654322 @) 。因此,由于对多个 splat 施加相同的限制,您不能使用中间位置的默认值。

AFAICS,这不是语言的技术限制,而是实现细节 - 在 Ruby 中巧妙地处理非结束位置的默认值是重用用于 splats 的数据结构和逻辑。 splats 必须 以这种方式受到限制(因为两个 splats 不明确),但理论上可以有一个支持您问题中的默认值的方案。可能虽然这是一个不寻常的要求,但您在不久的将来不会看到它完成。我建议只需将您命名的可选参数移动到 splat 参数之前。

【讨论】:

  • 为了更清楚地表达你的观点,你不需要方法定义体,你也不需要调用方法。
  • @sawa:我不明白“是语法的”吗?支持method(a=7, b, *c)method(a, b=7, *c) 在逻辑上是一致的,可以将相同的参数扩展和分配给固定、可选和splat 运算符。但是,Ruby 只支持第二种语法。
  • 换句话说,“有效”。但是我忽略了你anywhere other than the end of the fixed parameter list的调理,所以我删除了之前的评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-26
  • 2019-01-25
  • 2014-05-05
  • 1970-01-01
  • 2015-10-05
  • 2015-07-08
  • 1970-01-01
相关资源
最近更新 更多