Почему foo больше не ноль или функция внутри функции

Почему в приведенном ниже фрагменте кода foo заменяет его определение?

def foo
  def foo
    1
  end
end

в первый раз foo равен nil

foo
=> nil

foo.foo
=> 1

Теперь, если я снова назову foo:

foo
=> 1

Как видите, foo больше не нуль. Может кто-то объяснить это мне? спасибо.

Ответ 1

def foo
  p "about to redef foo"
  def foo
    1
  end
end
foo
"about to redef foo"
=> nil
foo
=> 1

Кроме того, когда вы вызываете foo.foo, кажется, что вы пытаетесь получить доступ к внутреннему методу foo, но он не работает таким образом. Ваш метод foo фактически определен на Object, поэтому вы действительно вызываете 1.foo.

Ответ 2

Если вы хотите получить этот эффект, попробуйте

def foo
  foo = proc {
    1
  }
end

Так как методы def не создают новый self. Каждый метод связан до self, который в этом случае равен main, Object.new, который созданный для каждого файла, загруженного интерпретатором ruby. Внутри class, self - это класс, и вы получаете методы экземпляра.

Ответ 3

Определения методов анализируются при чтении, но не выполняются до вызова. Когда вы выполняете первый foo, выполняется внешний foo, который определяет Object#foo как

def foo
  1
end

и возвращает nil как возвращаемое значение операции, которая определила метод. С этого момента, когда вы вызываете foo, выполняется только что созданный foo, возвращая

1

Ответ 4

Когда ваш первый вызов foo возвращает метод foo, а при повторном вызове foo он возвращает 1. Прочитайте closure

Ответ 5

Вложенные определения методов в рубине запутываются.

Они действительно переопределяются!

Что происходит, так это то, что оба определения относятся к самому внешнему контексту. То есть оба определения определяют один и тот же метод (!) Foo. Хотя внешнее определение интерпретируется при чтении файла, тогда как внутреннее определение интерпретируется только при первом вызове внешнего метода. Затем он заменит начальное определение.

Учитывая этот код

def foo
  def foo
    1
  end
end

Пропустим через это:

  • При загрузке файла глобальный метод foo определяется телом def foo; 1; end.
  • когда вы вызываете foo(), этот глобальный метод выполняется, а переопределяет глобальный метод foo с телом 1 и возвращает nil, поскольку определение метода не имеет возвращаемое значение.

  • когда вы вызываете foo().foo(), выполняется глобальный метод и возвращает 1, на котором выполняется глобальный метод, снова возвращающий 1.

Смятение здесь состоит в двух вещах: а) определение вложенных методов применяется к одной и той же внешней области и б) что глобальный метод может быть вызван для любого объекта.

Вот еще один пример, демонстрирующий, как вложенные определения фактически переопределяются.

class A
  def m(arg=nil)
    def m; 42; end if arg
    23
  end
end

вот что происходит

a = A.new
a.m # => 23
a.m # => 23
a.m(:magic) # => 23
a.m # => 42
a.m # => 42

как вы можете видеть, вложенное определение на самом деле является переопределением.

Ответ 6

Лично мне всегда было очень странно, что он определяет внутренний def для класса. Я бы счел более разумным определить его на синглетоне. например эквивалентно def self.foo, так как он вызывается на уровне экземпляра, а не на уровне класса.

Либо это, либо он может быть вызван только из метода, который он определен в: хотя это может быть не так полезно, поскольку у нас есть лямбда.

Одно можно сказать наверняка, но вы почти никогда не увидите этого на практике.