Как ruby ​​разрешает ключевое слово `self` в лямбде или блоке? - PullRequest
2 голосов
/ 09 октября 2019

Как и в JavaScript, в ruby ​​лямбда может передаваться через функции.

В JavaScript this будет разрешаться как объект вызывающего объекта.

Но как насчет ruby? Применяется ли тот же механизм к лямбде или блоку рубина? Как? Можете ли вы привести пример кода?

Кстати, я прочитал Язык программирования Ruby , но я не смог найти какую-либо полезную информацию из него ..

1 Ответ

5 голосов
/ 09 октября 2019

В Ruby self имеет лексическую область, то есть self внутри блока или лямбды - это то, что было бы в том же месте, не будучи в блоке или лямбде.

class << foo = Object.new
  def bar
    puts "`self` inside `foo#bar` is #{self.inspect}"
    yield self
  end
end

this = self

foo.bar do |that|
  puts "`self` inside the block is #{self.inspect}"
  case
  when this.equal?(self)
    puts "… which is the same as outside the block."
  when that.equal?(self)
    puts "… which is the same as inside the method."
  else
    puts "Ruby is really weird, `self` is something completely different!"
  end
end

# `self` inside `foo#bar` is #<Object:0xdeadbeef48151623>
# `self` inside the block is main
# … which is the same as outside the block.

Это такжеприменяется к лямбдам:

class << foo = Object.new
  def bar(lambda)
  #      ↑↑↑↑↑↑↑↑
    puts "`self` inside `foo#bar` is #{self.inspect}"
    lambda.(self)
    #↑↑↑↑↑↑↑↑↑↑↑↑
  end
end

this = self

foo.bar(-> that do
  #    ↑↑↑↑↑↑↑↑
  puts "`self` inside the lambda is #{self.inspect}"
  case
  when this.equal?(self)
    puts "… which is the same as outside the lambda."
  when that.equal?(self)
    puts "… which is the same as inside the method."
  else
    puts "Ruby is really weird, `self` is something completely different!"
  end
end)

# `self` inside `foo#bar` is #<Object:0xdeadbeef48151623>
# `self` inside the lambda is main
# … which is the same as outside the lambda.

Там есть , однако фиксированное количество очень специфических отражательных методов, которые делают изменяют self, это методы всемейство *_{exec|eval}:

Пример (изменение только одной соответствующей строкисверху):

class << foo = Object.new
  def bar(&blk)
  #      ↑↑↑↑↑↑
    puts "`self` inside `foo#bar` is #{self.inspect}"
    instance_exec(self, &blk)
    #↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
  end
end

this = self

foo.bar do |that|
  puts "`self` inside the block is #{self.inspect}"
  case
  when this.equal?(self)
    puts "… which is the same as outside the block."
  when that.equal?(self)
    puts "… which is the same as inside the method."
  else
    puts "Ruby is really weird, `self` is something completely different!"
  end
end

# `self` inside `foo#bar` is #<Object:0xdeadbeef48151623>
# `self` inside the block is #<Object:0xdeadbeef48151623>
# … which is the same as inside the method.
#                        ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

Это также относится к лямбдам (преобразованным в блоки):

foo.bar(&-> that do
  #    ↑↑↑↑↑↑↑↑
  puts "`self` inside the lambda is #{self.inspect}"
  case
  when this.equal?(self)
    puts "… which is the same as outside the lambda."
  when that.equal?(self)
    puts "… which is the same as inside the method."
  else
    puts "Ruby is really weird, `self` is something completely different!"
  end
end)

# `self` inside `foo#bar` is #<Object:0xdeadbeef48151623>
# `self` inside the lambda is #<Object:0xdeadbeef48151623>
# … which is the same as inside the method.
#                        ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

Наконец, Ruby позволяет вам рефлексивно изменять лексическую среду на определенном сайте вызова. в Binding объект. Затем вы, в свою очередь, можете оценить код в контексте этой конкретной привязки, используя метод привязки Binding#eval или передав объект привязки в Kernel#eval:

class << foo = Object.new
  def bar(str)
    puts "`self` inside `foo#bar`         is #{self.inspect}"
    binding.eval(str)
    #↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
  end
end

$this = self

foo.bar <<~'HERE'
  puts "`self` inside the eval'd string is #{self.inspect}"
  if $this.equal?(self)
    puts "… which is the same as outside the eval'd string."
  end
HERE

# `self` inside `foo#bar`         is #<Object:0x0070070070070070>
# `self` inside the eval'd string is #<Object:0x0070070070070070>

self - это один из трех неявных контекстов в Ruby:

  • self
  • определитель по умолчанию
  • постоянный контекст поиска

Есть хорошая статья в блоге yugui, в которой, в основном, говорится об определителе по умолчанию, но также кратко говорится о self: Три неявных контекста в Ruby . Есть также более старая статья на японском языке, в которой есть немного больше деталей: Ruby

...