2011-07-31 5 views
13

me robaron mi título de este post: Executes a function until it returns a nil, collecting its values into a listidiomático Ruby - Ejecución de las funciones hasta que se devuelve un nulo, la recopilación de sus valores en una lista

Esa pregunta se refiere a Lisp y es, francamente, por encima de mi cabeza. Sin embargo, creo que su pregunta - traducido en Rubí - es exactamente mi propia:

Cuál es la mejor manera de crear un bucle condicional en [rubíes] que ejecuta una función hasta que vuelve NIL momento en el cual recoge los valores devueltos en una lista?

Mi enfoque torpe actual es la siguiente:

def foo 
    ret = Array.new 
    x = func() # parenthesis for clarity (I'm not a native Ruby coder...) 
    until x.nil? 
    ret << x 
    x = func() 
    end 
    ret 
end 

Este fragmento de código hará lo que yo quiero ... pero sé que es un limpiador, más idiomáticamente enfoque Rubí ... ¿verdad?

+0

estaría interesado si se pudiera proporcionar un caso en el mundo real, donde se utilizaría un procedimiento de este tipo. – Phrogz

+0

@Phrogz Esto suena como un generador iterable para mí, y es un patrón de diseño común, especialmente en programas funcionales. –

Respuesta

10

Es curioso cómo nadie sugirió Enumerator y su método take_while, me parece que acaba de encajar:

# example function that sometimes returns nil 
def func 
    r = rand(5) 
    r == 0 ? nil : r 
end 

# wrap function call into lazy enumerator 
enum = Enumerator.new{|y| 
    loop { 
    y << func() 
    } 
} 

# take from it until we bump into a nil 
arr = enum.take_while{|elem| 
    !elem.nil? 
} 

p arr 
#=>[3, 3, 2, 4, 1, 1] 
+0

+1, solución muy agradable, 'Enumerator' está infrautilizado para cosas como esta. –

+0

Pensé en un enfoque similar, pero no me gustó el resultado final, porque ya me estaba confundiendo 1 minuto después de escribirlo. –

+0

¿Por qué el voto a favor? –

3

Esto debería hacer el truco:

def foo 
    arr = [] 
    while true 
    x = yield 
    break if x.nil? 
    arr << x 
    end 
    arr 
end 

Uso:

foo { doStuff } 
foo &bar 
+3

Creo que es más idiomático en Ruby usar 'loop {...}' en lugar de 'while true ...'. – Phrogz

8

supongo que esto se parece más a Ruby:

def foo 
    r = nil; [].tap { |a| a << r until (r = yield).nil? } 
end 
+0

Me gusta este, más limpio que el mío. – emboss

+0

Si se declara x = nula en los argumentos del método, que será una sola línea :) – emboss

+0

ya ha cambiado, pero no declaradas en los argumentos. ;) –

1

personalmente me gustaría escribirlo así, tan que puede pasar un bloque que llama a una función o hace lo que quiera:

def gather 
    [].tap do |collection| 
    result=true; i=0 
    until result.nil? 
     unless (result=yield(i)).nil? 
     collection << result 
     end 
     i += 1 
     end 
    end 
end 


# Silly test 
$letters = *('a'..'z') 
$current = -1 
def next_char(max) 
    $letters[$current+=1] if $current<max 
end 
some = gather{ next_char(10) } 
p some 
#=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"] 

some = gather{ |i| $letters[i] if i<=10 } 
p some 
#=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"] 

Tenga en cuenta que usted podría escribir el método de la siguiente manera:

def gather 
    [].tap do |result| 
    x = true 
    result << x unless (x=yield).nil? until x.nil? 
    end 
end 

... pero yo personalmente no parece que sea muy fácil de leer.

0

Creo que eras bastante cerca para empezar.

def gather 
    ret = [] 
    while x = yield 
    ret << x 
    end 
    ret 
end 

El único truco aquí es la comprensión de que la asignación de "x = rendimiento" devuelve el valor de x, por lo que "mientras que x = rendimiento" bucles mientras que x no es nulo/falso. No debe confundirse con "mientras que el rendimiento x ==" ...

Cuestiones relacionadas