【问题标题】:Use of underscores with splats in Ruby在 Ruby 中使用带有 splats 的下划线
【发布时间】:2016-01-05 12:59:06
【问题描述】:

我已经关注Why's (Poignant) Guide to Ruby,通过其他几个指南,到Ruby style guide,了解 Ruby 主义者的想法。 但这是我第一次看到尾随下划线。这些是什么?它们有用吗?如果有用,我们何时使用它们以及如何将它们与 splat 运算符一起使用?

(Ruby 风格指南链接锚定到实际示例)

【问题讨论】:

    标签: ruby-on-rails ruby splat


    【解决方案1】:

    合法的变量名是_。像*_ 这样的东西类似于*x。术语结尾的下划线变量实际上是指赋值语句左侧以逗号分隔的一系列变量中的最后一个变量名,例如:

    a, b, _ = [1, 2, 3, 4]
    

    splat 运算符有两种用途:

    1. 将数组分解为各个项目。
    2. 将项目收集到一个数组中。

    发生哪些情况取决于使用 splat 运算符的 上下文

    以下是 Ruby 风格指南所说的不好的示例:

    a, b, _ = *foo
    

    该示例中的尾随下划线变量 是不必要的,因为您可以通过以下方式将foo 的前两个元素分配给变量ab

    a, b = *foo
    

    下划线变量用于表示,我不关心这个变量,因此在该示例中,如果您只想分配给ab

    该示例也可能被认为是 不好的 风格,因为也不需要 * 运算符(来源:Cary Swoveland):

    a, b = [1, 2, 3]
    p a, b
    
    --output:--
    1
    2
    

    *可以用在右手边,效果如下:

    x, y, z = 10, [20, 30]
    p x, y, z
    
    --output:--
    10
    [20, 30]
    nil
    
    x, y, z = 10, *[20, 30]
    p x, y, z
    
    --output:--
    10
    20
    30
    

    因此,请记住,在样式指南的其余示例中,右侧的 * 是多余的。

    下一个不好的例子是:

    a, _, _ = *foo
    

    这是一个更具体的例子:

    a, _, _ = *[1, 2, 3, 4]
    p a, _
    
    puts "-" * 10
    
    a, _ = *[1, 2, 3, 4]
    p a, _
    
    --output:--
    1
    3
    ----------
    1
    2
    

    下面显示了示例第一部分中分配的工作方式:

     a, _, _
     ^  ^  ^
     |  |  |
    [1, 2, 3, 4]
    

    无论如何,如果你去掉左边的第二个下划线变量,那么a 将被分配相同的东西。去掉两个下划线变量怎么样?

    a = *[1, 2, 3, 4]
    p a
    
    --output:--
    [1, 2, 3, 4]
    

    不。所以左边的第一个下划线变量似乎是必要的。但是,还有另一种语法可以在不使用尾随下划线变量的情况下获得相同的结果:

    a, = *[1, 2, 3, 4]
    p a
    
    --output:--
    1
    

    因此,第三个不好的例子:

    a, *_ = *foo
    

    也可以写成:

    a, = *foo 
    

    从而避免尾随下划线变量。

    最后,风格指南提供了这个神秘的建议:

    当有 splat 时,尾随下划线变量是必要的 在赋值左侧定义的变量和 splat 变量不是下划线。

    我认为这可能指的是这样的事情:

    *a = *[1, 2, 3, 4]
    p a
    
    --output:--
    [1, 2, 3, 4]
    

    如果你想让a 被分配到数组的前三个元素,那么你必须这样写:

    *a, _ = *[1, 2, 3, 4]
    p a
    
    --output:--
    [1, 2, 3]
    

    无论什么原因,解析器都无法处理:

    *a, = *[1, 2, 3, 4]
    
    --output:--
    *a, = *[1, 2, 3, 4]
         ^
    1.rb:6: syntax error, unexpected '\n', expecting :: or '[' or '.'
    

    这是一个很好的例子:

    *a, b, _ = *foo
    

    如果您想将 foo 的最后一个元素的第二个元素分配给 b,则后面的下划线变量是必需的。

    下面的好例子有点令人困惑:

    a, _b = *[1, 2, 3, 4]
    a, _b, = *[1, 2, 3, 4]
    

    让我们尝试一下:

    a, _b = *[1, 2, 3, 4]
    p a, _b
    
    puts "-" * 10
    
    a, _b, = *[1, 2, 3, 4]
    p a, _b
    
    --output:--
    1
    2
    ----------
    1
    2
    

    在 ruby​​ 中,诸如 _b 之类的变量名与名为 _b 的变量没有什么不同。在像 Erlang 这样的函数式语言中,变量 __BB 具有不同的效果——但在 Ruby 中则不然。

    顺便说一句,我不会花五分钟来学习那种风格——它太深奥了。

    【讨论】:

    • 它不能处理*a, = ... 的原因与它不能处理*a, *b = ... 的原因相同:它如何决定在a 中放入多少项目,在b 中放入多少?或者在*a, = ... 的情况下,它如何决定在a 中放入多少项以及忽略多少项?它不能,所以解析器甚至不允许它。
    • @Jordan:是的,但请查看错误消息。 或者在 *a, = ... 的情况下,它如何决定在 a 中放入多少项以及忽略多少项?-- 解析器可以贪婪地分配给*a,,用结果是a 等于除最后一个元素之外的整个数组。
    • @JamesPond,对。 x_x_ 之间没有区别。当你看到*_ 时会有点困惑,但这相当于*x。下划线只是对读者的一个提示,取自函数式语言,该变量不会被使用。
    • @7stud 解析器可以做到这一点,但它是任意的。如果它是贪婪的,为什么它会捕获所有除了最后一个元素?为什么它不能捕获每一个元素?尾随的逗号本身本质上是贪婪的,因此类似于*a, *b = ... 它只是意味着“把其他所有东西都扔掉”。当它跟随一个 splat 变量时,将其含义更改为“扔掉最后一个元素”没有任何意义。
    • 哇!这真是一场绝妙的表演。我猜你在开始写它的时候没想到你的答案会这么长而且影响深远。您注意到如果a, = *[1, 2, 3, 4] 导致a #=> 1。我可能在您的回答中错过了它,但是当您移除 splat 时会获得相同的结果。 a,_ = [1,2,3,4] 很常见,但实际上只需要逗号。
    【解决方案2】:

    在变量前面加上下划线_ 没有任何语法意义,它只是一个约定(即,它不是一个严格的规则,并且总是可以在不破坏代码的情况下被破坏)。

    它在 Ruby 中并不经常使用,但它是 Ruby 中的对应物,用于在 LaTeX 编程中为函数添加 @ 标记。据我了解,它是用来表达诸如“劣质”、“子程序”、“核心”或“元素”之类的概念。可以认为是“多元化”的对立面。

    示例 1. 数组的变量名通常使用复数形式,其元素将使用单数名称引用:

    items = [1, 2, 3]
    items.each{|item| ...}
    

    这可能有点奇怪,但这种做法偶尔会出现,即使元素使用非单词变量名来引用:

    is = [1, 2, 3]
    is.each{|i| ...}
    

    现在,假设数组被预先命名为单数名称,然后您想将元素名称与它区分开来。在这种情况下,您可以在元素名称前添加下划线:

    item = [1, 2, 3]
    item.each{|_item| ...}
    
    x = [1, 2, 3]
    x.each{|_x| ...}
    

    示例 2. 假设您有一个调用另一个方法的方法,而后者仅用作子例程,并且完成了前者的大部分工作。在这种情况下,您可以在后者前面加上下划线:

    def foo a, b
      ... # Do something just a bit here
      _foo(a, b)
    end
    def _foo a, b
      ... # Do still a bit here
      __foo(a, b)
    end
    def __foo a, b
      ... # Most of the job is done here
    end
    

    示例 3。这有点不同,因为它是硬编码 Ruby 的一部分,它使用两个下划线(而不是一个),并且围绕变量(而不是前置)。有方法send,它调用__send__。这样一来,即使 send 被重新定义为其他内容,__send__ 也可以使用。

    【讨论】:

    • 谢谢您,先生,前面的答案只缺少前导下划线解释,我不是在问这个问题,而是从您那里收到的。你是一个真正的学者,一个绅士。
    • 顺便说一句,第二种方法中的 _foo(a, b) 和 __foo(a, b) 除了通知“我将在更大的方法 _this_method_name 中使用此方法“?还是我错过了重点?
    • @JamesPond 通知这是唯一的意图。这意味着它不是单独使用的。
    • 啊。如果我错了,请纠正我。它的名称相同,带有前导下划线,以表明只能在没有下划线的方法中调用它。我们不能在 foo 的内部调用 _foo(在程序的主体部分),就像我们不能在 _foo 之外调用 __foo 一样(我们可以在 foo 中调用它吗?)
    • 我认为没有那么严格。如果__foo 将同时用于foo_foo,则可以。人们在相当宽松的解释下使用它,所以你不需要太认真地对待它。
    猜你喜欢
    • 1970-01-01
    • 2014-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-06
    • 1970-01-01
    • 2021-09-02
    • 1970-01-01
    相关资源
    最近更新 更多