2009-11-23 28 views

Respuesta

4

Depende de los detalles de su código real, pero dado su ejemplo, si nombra el bloque de antemano, puede evitar dar el valor y el bloqueo en su función. Por ejemplo:

def f(x, &b) 
    yield x 
end 

foo = lambda do |i| 
    p i 
    f(i-1,&foo) if i > 0 
end 
f(4,&foo) 

Sin embargo, me gustaría encontrar una solución más elegante a este problema. Sospecho que esta sería una buena aplicación del combinador Y. Tan pronto como tenga algo mejor para ti, actualizaré este mensaje.

+0

Eso está bien, aunque tenía dudas acerca de lambda en mi caso porque se trata de la misma cantidad de bytes que def f..end. Esto se volvió un poco abstracto de manera abstracta, pero todo comenzó con esto: http://stackoverflow.com/questions/1766675/code-golf-running-water/1770264#1770264 – DigitalRoss

+0

Sí, desafortunadamente, si es el menor número de caracteres usados ​​que lo que buscas, usar lambda probablemente no sea muy útil. Incluso la abreviada -> sintaxis encontrada en Ruby 1.9 aún probablemente no ahorraría mucho, una vez que incluye el código y-combinator. Si no está familiarizado con este, o si hay algún otro lector, puede encontrar ejemplos en Ruby aquí: http://nex-3.com/posts/43-fun-with-the-y-combinator-in-ruby y aquí: http://www.eecs.harvard.edu/~cduan/technical/ruby/ycombinator.shtml –

+0

Aún así, un problema muy interesante. Voy a masticarlo un poco más antes de aceptar que el lambda con nombre es el camino más corto para ir. –

1
def f(x, &b) 
    b.call x 
    f(x-1,&b) if x>0 
end 

f(4) do |x| 
p x 
end 
2

Un bloque puede llamarse de forma recursiva siempre que esté almacenado en una variable a la que pueda acceder el bloque. Por ejemplo:

def f(x) 
    block = lambda do |y| 
    # some calculation on value, or simply yield to the block passed to f() 
    yield y 
    block.call(y - 1) if y > 0 
    end 
    block.call(x) 
end 

f(4) do |x| 
    puts "Yielded block: #{x}" 
end 

Alternativamente, puede volver al bloque recursiva, con destino a las personas que llaman bloquear y luego llamar a ese bloque. Por ejemplo:

def g 
    block = lambda do |y| 
    # some calculation on value, or simply yield to block passed to g() 
    yield y 
    block.call(y - 1) if y > 0 
    end 
end 

printing_descender = g do |x| 
    puts "Encapsulated block: #{x}" 
end 
printing_descender.call(4) 

Salidas:

Yielded block: 4 
Yielded block: 3 
Yielded block: 2 
Yielded block: 1 
Yielded block: 0 
Encapsulated block: 4 
Encapsulated block: 3 
Encapsulated block: 2 
Encapsulated block: 1 
Encapsulated block: 0 
0

respuesta de Matt es buena. También es la única forma de implementar un retorno inmediato de una búsqueda recursiva profunda. Tenga en cuenta que al regresar del bloque en realidad regresa de la función de invocación. desenrollando todas las llamadas de bloque recursivas de una sola vez.

+0

Esto realmente depende si usa 'lambda' o' proc'. Las funciones de lambda son anónimas que se pueden devolver, las de proc se ejecutan en el ámbito de la función primaria y la devolución hará lo que diga. –

0

hay muchas formas de hacerlo usando callcc o catch/throw (que siempre puede regresar de llamadas recursivas profundas). aquí está la versión no golfística que utiliza los vars locales del hilo

def f x, &b 
    t = Thread.current 
    t[:b] ||= b 
    b ||= t[:b] 
    b.call(x) 
ensure 
    t[:b] = nil 
end 

f 4 do |i| 
    p i 
    f i - 1 if i > 0 
end 
Cuestiones relacionadas