Можно ли ссылаться на лямбду изнутри, используя Ruby? - PullRequest
18 голосов
/ 01 марта 2012

Я хочу иметь возможность вызывать анонимную лямбду изнутри, используя 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?

Ответы [ 4 ]

8 голосов
/ 01 марта 2012

В следующем примере лямбда по-прежнему анонимна, но имеет ссылку.(Это считается анонимным?)

(l = lambda { l.call }).call

(Спасибо Никласу Б. за указание на ошибку в моем исходном ответе; я только проверял ее в IRB, и она работала там).1005 * Это, конечно, заканчивается ошибкой SystemStackError: stack level too deep, но это демонстрирует цель.

5 голосов
/ 01 марта 2012

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

lambda{ __callee__ }.call #=> nil

И без ссылки вы не можете вызвать эту функцию.Я могу предложить вам только немного более чистый вариант:

(fac = lambda{ |n| n==1 ? 1 : n*fac.call(n-1) }).call(5)
1 голос
/ 22 апреля 2019
fact = -> (x){ x < 2 ? 1 : x*fact.(x-1)}

минимальная функция

1 голос
/ 14 декабря 2015

В дополнение к комментарию KL-7 , есть решение Y комбинатора:

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
...