Могу ли я ссылаться на лямбду изнутри с помощью Ruby?
Я хочу иметь возможность вызывать анонимную лямбду изнутри с помощью Ruby. Рассмотрим следующий рекурсивный блок (возвращает факториал). Я знаю, что могу назначить его переменной, и эта переменная входит в область лямбда:
fac = lambda { |n| n == 1 ? 1 : n * fac.call(n - 1) }
fac.call(5)
Но, я хочу, чтобы иметь возможность сделать следующее (по какой-либо практической причине пока еще, я просто заинтересован в изучении языка еще):
(lambda { |n| n == 1 ? 1 : n * self.call(n - 1) }).call(5)
Я знаю, что это не сработает, потому что self
- это объект main
. Я делаю это неправильно? Я пытаюсь сделать что-то невозможное - а если нет, то это из-за какого-то теоретического ограничения или просто не реализовано в Ruby?
Ответы
Ответ 1
В следующем примере лямбда по-прежнему анонимна, но имеет ссылку. (Это для анонимного?)
(l = lambda { l.call }).call
(Спасибо Niklas B. за указание ошибки в моем первоначальном ответе, я только тестировал ее в IRB и там работал).
Это, конечно, заканчивается ошибкой SystemStackError: stack level too deep
, но это демонстрирует цель.
Ответ 2
Кажется, что анонимная функция действительно не имеет никакой ссылки. Вы можете проверить его вызываемый
lambda{ __callee__ }.call #=> nil
И без справки вы не можете вызвать эту функцию.
Я могу предложить вам только немного более чистый вариант:
(fac = lambda{ |n| n==1 ? 1 : n*fac.call(n-1) }).call(5)
Ответ 3
В дополнение к комментарий KL-7, здесь Y combinator решение:
lambda { |f|
lambda { |x| x.call(x) }.call(
lambda { |x| f.call( lambda { |v| x.call(x).call(v) } ) } )
}.call(
lambda { |f|
lambda { |n| n == 0 ? 1 : n * f.call(n - 1) }
}
).call(5) #=> 120
Обычно вы разделяете их:
y = lambda { |f|
lambda { |x| x.call(x) }.call(
lambda { |x| f.call( lambda { |v| x.call(x).call(v) } ) } )
}
fac = y.call(
lambda { |f| lambda { |n| n == 0 ? 1 : n * f.call(n - 1) } }
)
fac.call(5) #=> 120
Обратите внимание, что хотя fac
назначается, он не используется в лямбда.
Я бы использовал синтаксис Ruby ->
и .()
вместо .call()
:
y = ->(f) {
->(x) { x.(x) }.(
->(x) { f.(->(v) { x.(x).(v) }) } )
}
fac = y.(->(f) {
->(n) { n == 0 ? 1 : n * f.(n - 1) }
})
fac.(5) #=> 120
Вызов y
можно немного упростить, используя curry
:
y = ->(f) {
->(x) { x.(x) }.(
->(x) { f.curry.(->(v) { x.(x).(v) }) } )
}
fac = y.(
->(f, n) { n == 0 ? 1 : n * f.(n - 1) }
)
fac.(5) #=> 120
Ответ 4
fact = -> (x){ x < 2 ? 1 : x*fact.(x-1)}
минимальная функция