【问题标题】:How does defining [square bracket] method in Ruby work?在 Ruby 中定义 [方括号] 方法是如何工作的?
【发布时间】: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 可以不带括号吗?

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

【问题讨论】:

    标签: ruby methods


    【解决方案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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-04-09
        • 1970-01-01
        • 2015-07-07
        • 2021-12-03
        • 2023-03-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多