Почему 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
, так как он вызывается на уровне экземпляра, а не на уровне класса.
Либо это, либо он может быть вызван только из метода, который он определен в: хотя это может быть не так полезно, поскольку у нас есть лямбда.
Одно можно сказать наверняка, но вы почти никогда не увидите этого на практике.