gpt4 book ai didi

Ruby 的精妙之处

转载 作者:数据小太阳 更新时间:2023-10-29 07:00:25 25 4
gpt4 key购买 nike

这里有一个很好的文档,介绍了当前在 ruby​​ 中的改进实现: http://ruby-doc.org//core-2.2.0/doc/syntax/refinements_rdoc.html ,但也有一些奇怪的极端情况。

首先,include moduleusing module正交(一个包含模块的实例方法,另一个激活细化)。但是有一个技巧可以包含一个细化模块本身,请参阅 Better way to turn a ruby class into a module than using refinements? .

def to_module(klass)
Module.new do
#note that we return the refinement module itself here
return refine(klass) {
yield if block_given?
}
end
end

class Base
def foo
"foo"
end
end
class Receiver
include to_module(Base) {
def foo
"refined " + super
end
}
end
Receiver.new.foo #=> "refined foo"

奇怪的是,这个优化模块不能与 using 一起使用!

m=to_module(Base) {}
m.class #=> Module
using m
#=>TypeError: wrong argument type Class (expected Module)

因此,仅在细化模块的封闭模块上使用才有效。其次,我想使用上面的 yield 技巧来传递一个 Proc 来优化(即使它只接受一个 block ),而不像在中那样求助于将 Proc 转换回源 https://www.new-bamboo.co.uk/blog/2014/02/05/refinements-under-the-knife/ .但是在包含示例中使用 yield 不起作用:

def ref_module1(klass)
Module.new do
refine(klass) {
yield
}
end
end

class Receiver1
using ref_module1(Base) {
def foo
"refined " + super
end
}
def bar
Base.new.foo
end
end
Receiver1.new.bar #=> NoMethodError: super: no superclass method `foo'

我们看到 Receiver1 仍然使用 Bar#foo 而不是改进的方法。但是我们可以使用 module_eval 代替:

def ref_module2(klass,&b)
Module.new do
refine(klass) {
module_eval(&b)
}
end
end

class Receiver2
using ref_module2(Base) {
def foo
"refined " + super
end
}
def bar
Base.new.foo
end
end
Receiver2.new.bar #=> "refined foo"

我不太明白为什么 module_eval 在这里工作而不是 yield 方法。在细化 block 内部,'default_definee' 是细化模块,因此 module_eval 将 'default_definee' 设置为 self='the refinement module' 应该不会影响它。事实上,在开头的“include”示例中,当我使用 module_eval 或直接 yield 时,我得到了相同的结果。

谁能解释这种行为?

最佳答案

上下文(或绑定(bind))是 module_eval 起作用而 yield 在您最后一组示例中不起作用的原因。它实际上与改进无关,如下所示。

module_eval 开头:

class Foo
def run(&block)
self.class.module_eval(&block)
end
end

foo = Foo.new
foo.run {
def hello
"hello"
end
}

puts foo.hello # => "hello"
puts hello => # '<main>': undefined method 'hello' for main:Object (NameError)

Foo#run我们调用module_evalFoo .这会将上下文 ( self) 切换为 Foo .结果很像我们简单定义的 hello class Foo 内部原来。

现在让我们来看看yield :

class Foo
def run
yield
end
end

foo = Foo.new
foo.run {
def hello
"hello"
end
}

puts hello # => "hello"
puts foo.hello # => '<main>': private method 'hello' called for ...

yield简单地在其原始上下文中调用该 block ,在本例中为 <main> .调用该 block 时,最终结果与通常在顶层定义该方法时的结果完全相同:

class Foo
def run
yield
end
end

foo = Foo.new

def hello
"hello"
end

puts hello # => "hello"
puts foo.hello # => '<main>': private method 'hello' called for ...

您可能会注意到 foo似乎有 hello yield 中的方法例子。这是定义 hello 的副作用作为顶层的方法。结果是 <main>只是 Object 的一个实例,并且定义顶级方法实际上只是在 Object 上定义私有(private)方法几乎所有其他东西最终都会继承。您可以通过打开 irb 并运行以下命令来查看:

self       # => main
self.class # => Object

def some_method
end

"string".method(:some_method) # => #<Method: String(Object)#some_method>

现在回到您的示例。

这是 yield 中发生的事情示例:

def ref_module1(klass)
Module.new do
refine(klass) {
yield
}
end
end

class Receiver1
# like my yield example, this block is going to
# end up being invoked in its original context
using ref_module1(Base) {
def foo
"I'm defined on Receiver1"
end
}

def bar
# calling foo here will simply call the original
# Base#foo method
Base.new.foo
end
end

# as expected, if we call Receiver1#bar
# we get the original Base#foo method
Receiver1.new.bar # => "foo"

# since the block is executed in its original context
# the method gets defined in Receiver1 -- its original context
Receiver1.new.foo # => "I'm defined on Receiver1"

至于module_eval ,它在您的示例中有效,因为它会导致 block 在新模块的上下文中运行,而不是在 Receiver1 上运行类。

关于Ruby 的精妙之处,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28649472/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com