【问题标题】:Methods in Ruby: objects or not?Ruby 中的方法:对象与否?
【发布时间】:2011-02-05 20:05:42
【问题描述】:

this discussion 的启发,经过一番谷歌搜索后,我无法找到一个关于 Ruby 中方法的非常简单问题的答案:方法是对象还是不是?

herethere 有不同的意见,我真的很想听听,比如说,一个深入的解释。

我知道Object#method 方法,它接受一个方法名称并返回一个Method 实例,但是另一方面,您可以对块执行类似的操作,将它们变成Proc 实例,而块不是对象,那么是什么让方法有什么不同呢?

【问题讨论】:

  • 函数是 Ruby 中的一等公民,可以转换为对象,何必担心呢?我认为答案太低了,没有多大意义......
  • 是吗?恕我直言,您需要将它们转换为对象的事实使它们不是一流的。这也是我想从答案中学到的。

标签: ruby object methods language-features


【解决方案1】:

方法是 Ruby 的语法,但它们不是值 Ruby 程序可以在其上运行。 也就是说,Ruby 的方法不是 对象以字符串的方式, 数字和数组是。它是 然而,有可能获得一个方法 表示给定方法的对象, 我们可以间接调用方法 通过方法对象。

来自The Ruby Programming Language:

【讨论】:

  • 嗨,如果方法不是对象,那怎么可能? irb(main):015:0> def hi irb(main):016:1> "hello" irb(main):017:1> end => nil irb(main):018:0> hi.object_id => 22452528 irb(main):019:0> hi.object_id.send hi => "hi" irb(main):020:0> hi.object_id.send(hi) => "hi"
  • hi.object_id首先调用方法hi,然后返回其结果的object_id(即字符串"hello")。
【解决方案2】:

你真的说不出来。

访问方法的唯一方法是将#method 消息发送到某个对象,然后该对象将返回一个Method 对象。但是 Method 对象是方法本身吗?或者它是该方法的包装器?还是原始方法的转换版本?

你不知道:如果你想查看一个方法,你必须调用#method,此时你肯定得到一个对象。 之前你叫#method是什么你看不到,所以你说不出来。

几个数据点:在 Ruby 中,一切都返回一个值。 def 返回什么?它总是返回nil,而不是Method 对象。还有define_method?它返回Proc,但不是Method(也不是UnboundMethod)。 [注意:在 Rubinius 中,def 返回方法的编译字节码,但仍然不是 Method 对象。]

如果您查看 Ruby 语言规范第 6.1 节的第 4 和第 5 段(第 5 页和第 6 页的第 29-34 和 1-5 行),您可以清楚地看到方法和对象之间存在区别.如果您查看内置类的规范,您会发现MethodUnboundMethod 都不在其中,Object#method 也不在其中。 IOW:您可以构建一个完全符合标准的 Ruby 解释器,其中方法不是对象。

现在,OTOH 块绝对不是对象。有许多方法可以从块中构造 Proc 对象,然后它们具有与原始块相同的行为(lambdaprocProc.new& 印记),但块本身不是对象。

这样想:您可以将字符串传递给File.new 以构造文件对象,但这不会使字符串成为文件。您可以将一个块传递给Proc.new 以构造一个 proc 对象,但这不会使块成为一个 proc。

【讨论】:

  • 不想听起来讽刺,但什么是块?它只是 .rb 文件中的一段文本吗?
  • Jörg 在后来的 StackOverflow 问题 @MladenJablanović 链接中澄清说 Ruby 方法不是对象。这是引用:“但是,请注意,Method 和 UnboundMethod 都是方法的包装器,而不是方法本身。方法不是 Ruby 中的对象。(与我在其他答案中写的相反,顺便说一句。我真的需要回去修复这些。)“
  • @AndrewGrimm 块是块。它们是一个独特的实体,只能以特定方式对其进行操作。
【解决方案3】:

在 Ruby 中,方法和块本身并不是原生或一流的对象。但是,它们可以很容易地被包裹在对象中,因此通常没有区别。

但是尝试一下,并记住结果,

a = Object.method(:new).object_id
b = Object.method(:new).object_id
a == b
=> false

在 Haskell 中,所有值(包括数字以及 lambda 和函数)都是一等值。在语言的各个方面,它们都被同等对待。在 Ruby 中不是这样,但可以近似。

【讨论】:

  • 正义,你的论点和我的一样。此外,我不知道返回方法的任何其他方式,因此 #method(method_name) 似乎是引用方法的唯一方式,除了在其原始对象上按名称调用它。
  • 您的 object_id 唯一证明的是它们不是立即数。 a = 1.0.object_id; b = 1.0.object_id; a == b # => false
【解决方案4】:

即使方法的返回值是对象而不是 nil,对象和方法也不相同。 对象位于堆上,除非在方法、lambda 或 proc 范围内,并且方法本身位于堆栈上,并在解释后分配地址,而静态和类对象则在堆上分配。 Ruby 仍然使用 C 来解释它并将其传递给 VALUE 结构。

【讨论】:

  • 从 ruby​​ 的角度来看,方法就是对象。实现细节并不重要。方法可以像任何其他对象一样是地址和传递。 Procs 也是对象(块只是创建 Proc/lambda 的一种语法方式)。
【解决方案5】:

在 Ruby 中,方法不是对象。这很令人困惑,因为有一个 Method 类,您可以获取 Method 的实例。这些实例只是方法本身的代理。这些实例提供了一些有用的功能。他们有一些内在的魔力,可以将它们与实际方法挂钩(因此您可以执行Method#call 之类的操作),但您实际上无法访问这些内容(AFAIK)。

1.method(:to_s).object_id == 1.method(:to_s).object_id #=> false

这意味着1 有两个#to_s 方法(它没有),或者#method 方法返回的实际上不是方法本身,而是该方法的一些代理。如果方法实际上是对象,那么您将遇到可以两次获得相同实例的情况。如果方法是对象,那么您将能够在它们上设置一个实例变量,然后在第二次检索方法对象后获取该实例变量的值。你不能那样做。因此,虽然一般可能没有什么不同,但在某些情况下,我发现我无法做我想做的事情。

1.method(:to_s).instance_variable_set(:@foo, 'foo') #=> "foo" 
1.method(:to_s).instance_variable_get(:@foo)        #=> nil 
# And just in case you question it...
1.object_id == 1.object_id                          #=> true 

【讨论】:

    【解决方案6】:

    因为括号在 ruby​​ 中是可选的,所以在您需要通过 method 方法显式获取方法对象的意义上,方法对象通常是“隐藏的”。但是,如果您努力捕获一个方法对象,就会很清楚它就像一个对象。由于 Ruby >= 2.1,这比以往任何时候都更容易利用。

    例如,您可以让您的方法表现得更像它们在 Javascript 中的行为(其中没有括号是方法对象,而括号用于调用方法),如下所示:

    foo = method def foo
      def a(num)
        3 * num.to_i
      end
    
      n = yield if block_given?
      a(n || 3)
    rescue
      "oops!"
    end
    
    def foo.bar(num)
      a(num)
    end
    
    foo.class #=> Method
    foo() #=> 9
    foo.call #=> 9
    foo.call{2} #=> 6
    foo(){2} #=> 6
    foo.call{ raise "blam!" } #=> "oops!"
    foo.bar(5) #=> 15
    

    See this gist 用于将这些示例编写为测试的版本。

    JRL's answer 引用 Matz 书中的话说,方法不是对象像字符串等,但方法对象是真实的,除了 parens/no-parens 之外,它们的行为几乎与任何其他红宝石对象。这是一个duck-typed language,所以我会说它在我的书中将方法限定为对象。

    【讨论】:

      猜你喜欢
      • 2022-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-15
      • 2011-11-28
      • 1970-01-01
      • 1970-01-01
      • 2011-05-12
      相关资源
      最近更新 更多