在 Ruby 中定义 [方括号] 方法是如何工作的?

Posted

技术标签:

【中文标题】在 Ruby 中定义 [方括号] 方法是如何工作的?【英文标题】:How does defining [square bracket] method in Ruby work? 【发布时间】:2012-04-18 14:53:18 【问题描述】:

我正在浏览Programming Ruby - a pragmatic programmers guide 并偶然发现了这段代码:

class SongList
  def [](key)
    if key.kind_of?(Integer)
      return @songs[key]
    else
      for i in 0...@songs.length
        return @songs[i] if key == @songs[i].name
      end
    end
    return nil
  end
end

我不明白定义 [ ] 方法的工作原理?

为什么key在[]外面,但是调用方法的时候却在[]里面?

key 可以不带括号吗?

我意识到有更好的方法来写这个,并且知道如何编写我自己的有效方法,但是这个 [ ] 方法让我感到困惑......非常感谢任何帮助,谢谢

【问题讨论】:

【参考方案1】:

ruby 中的方法,不像许多语言可以包含一些特殊字符。其中之一是数组查找语法。

如果您要实现自己的哈希类,在检索哈希中的项目时想要反转它,您可以执行以下操作:

class SillyHash < Hash

  def [](key)
    super.reverse
  end

end

您可以通过以下方式调用哈希来证明这一点:

a = :foo => "bar"
 => :foo=>"bar" 
a.[](:foo)
 => "bar" 
a.send(:[], :foo)
 => "bar" 

所以def[]定义了你做my_array["key"]时使用的方法其他你可能觉得奇怪的方法是:

class SillyHash < Hash

  def [](key)
    super.reverse
  end

  def []=(key, value)
    #do something
  end

  def some_value=(value)
    #do something
  end

  def is_valid?(value)
    #some boolean expression
  end

end

澄清一下,[] 方法的定义与数组或哈希无关。举以下(人为的)例子:

class B
  def []
    "foo"
  end
end

 B.new[]
 => "foo" 

【讨论】:

我认为 OP 是在问我们为什么不调用它:my_array.[]("key") 以及 my_array["key"] 可能如何工作...... 所以根据定义,每当我在 ruby​​ 中为某个类创建 [] 方法时,它都知道它正在某种数组上使用,并期望它稍后放入 [] 中的 (key) 参数? 您的代码中存在无限递归。我想你的意思是把电话转给super @Gazler 修复你的代码。方法中的 super[key] 不起作用。 super(key).reverse 会。 super[key] = blah 不起作用。 super(key, value) 会。您应该重新了解如何在 ruby​​ 方法中使用 super! @Ben 你说的很对。我已经用超级电话更新了答案。【参考方案2】:

方括号是方法名称,例如Array#size,您将Array#[] 作为方法,您甚至可以像使用任何其他方法一样使用它:

array = [ 'a', 'b', 'c']
array.[](0) #=> 'a'
array.[] 1  #=> 'b'
array[2]    #=> 'c'

最后一个类似于语法糖,与第一个完全相同。 Array#+ 工作类似:

array1 = [ 'a', 'b' ]
array2 = [ 'c', 'd' ]
array1.+(array2) #=> [ 'a', 'b', 'c', 'd' ]
array1.+ array2  #=> [ 'a', 'b', 'c', 'd' ]
array1 + array2  #=> [ 'a', 'b', 'c', 'd' ]

你甚至可以像这样添加数字:

1.+(1) #=> 2
1.+ 1  #=> 2
1 + 1  #=> 2

同样适用于/*- 等等。

【讨论】:

【参考方案3】:

这只是语法糖。有某些语法模式会被翻译成消息发送。特别是

a + b

一样
a.+(b)

同样适用于==!=&lt;&gt;&lt;=&gt;=&lt;=&gt;===&amp;|987654332 @、/-%**&gt;&gt;&lt;&lt;!===~!~

还有,

!a

一样
a.!

同样适用于~

那么,

+a

一样
a.+@

同样适用于-

另外,

a.(b)

一样
a.call(b)

setter 也有特殊的语法:

a.foo = b

一样
a.foo=(b)

最后但并非最不重要的一点是,索引有特殊的语法:

a[b]

一样
a.[](b)

a[b] = c

一样
a.[]=(b, c)

【讨论】:

你的清单让我想到了 Ruby 的糖。有趣的是,字符串插值器#(例如"say hi, #my_name")不是方法调用的糖。镐书索引的第一页有一个很好的列表。 @SooDesuNe:它确实但是调用to_s【参考方案4】:

它是一个运算符重载器,它覆盖或补充您定义的类中的方法的行为,或者您正在修改其行为的类。您可以对不同于 [] 的其他运算符执行此操作。在这种情况下,您正在修改 [] 在类 SongList 的任何实例上调用它时的行为。

如果你有歌曲列表 = SongList.new 然后你做 songlist["foobar"] 然后您的自定义 def 将开始运行,并假定“foobar”将作为参数(键)传递,并且它将对“foobar”执行任何方法所说的对键执行的操作。

试试

class Overruler
    def [] (input)
          if input.instance_of?(String)
            puts "string"
          else
            puts "not string"
          end
     end
end
foo = Overruler.new
foo["bar"].inspect
foo[1].inspect

【讨论】:

以上是关于在 Ruby 中定义 [方括号] 方法是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

ruby 内省:如何问ruby 方法定义在哪里? [复制]

如何在Ruby中定义一个参数没有类型的方法[关闭]

Ruby 学习笔记1

ruby简单的基础 4

如何在 Ruby 的模块中定义类级宏?

如何像在 Javascript 中一样在 Ruby 中动态创建对象及其方法?