Ruby Language
Flujo de control
Buscar..
si, elsif, else y end
Ruby ofrece las expresiones if
y else
esperadas para la lógica de bifurcación, terminadas por la palabra clave end
:
# Simulate flipping a coin
result = [:heads, :tails].sample
if result == :heads
puts 'The coin-toss came up "heads"'
else
puts 'The coin-toss came up "tails"'
end
En Ruby, if
las declaraciones son expresiones que se evalúan como un valor, y el resultado se puede asignar a una variable:
status = if age < 18
:minor
else
:adult
end
Ruby también ofrece operadores ternarios de estilo C ( consulte aquí para obtener detalles ) que se pueden expresar como:
some_statement ? if_true : if_false
Esto significa que el ejemplo anterior usando if-else también puede escribirse como
status = age < 18 ? :minor : :adult
Además, Ruby ofrece la palabra clave elsif
que acepta una expresión para habilitar lógica de bifurcación adicional:
label = if shirt_size == :s
'small'
elsif shirt_size == :m
'medium'
elsif shirt_size == :l
'large'
else
'unknown size'
end
Si ninguna de las condiciones en una cadena if
/ elsif
es verdadera, y no hay else
cláusula, entonces la expresión se evalúa como nula. Esto puede ser útil dentro de la interpolación de cadenas, ya que nil.to_s
es la cadena vacía:
"user#{'s' if @users.size != 1}"
Valores de verdad y falsedad.
En Ruby, hay exactamente dos valores que se consideran "falsos" y devolverán falso cuando se analicen como una condición para una expresión if
. Son:
-
nil
- booleano
false
Todos los demás valores se consideran "veraces", incluidos:
-
0
- cero numérico (entero o no) -
""
- Cuerdas vacías -
"\n"
- Cadenas que contienen solo espacios en blanco -
[]
- Arreglos vacíos -
{}
- Hashes vacíos
Tomemos, por ejemplo, el siguiente código:
def check_truthy(var_name, var)
is_truthy = var ? "truthy" : "falsy"
puts "#{var_name} is #{is_truthy}"
end
check_truthy("false", false)
check_truthy("nil", nil)
check_truthy("0", 0)
check_truthy("empty string", "")
check_truthy("\\n", "\n")
check_truthy("empty array", [])
check_truthy("empty hash", {})
Saldrá:
false is falsy
nil is falsy
0 is truthy
empty string is truthy
\n is truthy
empty array is truthy
empty hash is truthy
mientras, hasta
A while
bucle se ejecuta el bloque, mientras que se cumple la condición dada:
i = 0
while i < 5
puts "Iteration ##{i}"
i +=1
end
Un bucle until
ejecuta el bloque mientras que el condicional es falso:
i = 0
until i == 5
puts "Iteration ##{i}"
i +=1
end
En línea si / a menos
Un patrón común es usar una línea o cola, if
o a unless
:
puts "x is less than 5" if x < 5
Esto se conoce como un modificador condicional y es una forma útil de agregar código de guarda simple y devoluciones tempranas:
def save_to_file(data, filename)
raise "no filename given" if filename.empty?
return false unless data.valid?
File.write(filename, data)
end
No es posible agregar una cláusula else
a estos modificadores. Además, generalmente no se recomienda usar modificadores condicionales dentro de la lógica principal. Para código complejo, uno debería usar normal if
, elsif
, else
lugar.
a no ser que
Una declaración común es if !(some condition)
. Ruby ofrece la alternativa de la declaración unless
.
La estructura es exactamente la misma que una instrucción if
, excepto que la condición es negativa. Además, la declaración unless
no es compatible con elsif
, pero sí es compatible con else
:
# Prints not inclusive
unless 'hellow'.include?('all')
puts 'not inclusive'
end
Declaración del caso
Ruby usa la palabra clave del case
para las declaraciones de cambio.
Según los documentos de Ruby :
Las declaraciones de casos consisten en una condición opcional, que está en la posición de un argumento a
case
, y cero o máswhen
cláusulas. La primera cláusulawhen
para que coincida con la condición (o para evaluar la verdad booleana, si la condición es nula) "gana" y se ejecuta su stanza de código. El valor de la declaración de caso es el valor de la cláusulawhen
éxito, onil
si no existe tal cláusula.Una declaración de caso puede terminar con una cláusula
else
. Cada unawhen
una declaración puede tener múltiples valores candidatos, separados por comas.
Ejemplo:
case x
when 1,2,3
puts "1, 2, or 3"
when 10
puts "10"
else
puts "Some other number"
end
Versión más corta:
case x
when 1,2,3 then puts "1, 2, or 3"
when 10 then puts "10"
else puts "Some other number"
end
El valor del case
cláusula se empareja con cada when
cláusula usando el ===
método (no ==
). Por lo tanto, se puede utilizar con una variedad de diferentes tipos de objetos.
Una declaración de case
se puede utilizar con rangos :
case 17
when 13..19
puts "teenager"
end
Se puede usar una declaración de case
con un Regexp :
case "google"
when /oo/
puts "word contains oo"
end
Se puede usar una declaración de case
con un Proc o lambda:
case 44
when -> (n) { n.even? or n < 0 }
puts "even or less than zero"
end
Se puede usar una declaración de case
con las Clases :
case x
when Integer
puts "It's an integer"
when String
puts "It's a string"
end
Al implementar el método ===
puede crear sus propias clases de coincidencia:
class Empty
def self.===(object)
!object or "" == object
end
end
case ""
when Empty
puts "name was empty"
else
puts "name is not empty"
end
Se puede usar una declaración de case
sin que coincida un valor con:
case
when ENV['A'] == 'Y'
puts 'A'
when ENV['B'] == 'Y'
puts 'B'
else
puts 'Neither A nor B'
end
Una declaración de case
tiene un valor, por lo que puede usarla como un argumento de método o en una asignación:
description = case 16
when 13..19 then "teenager"
else ""
end
Control de bucle con ruptura, siguiente y rehacer
El flujo de ejecución de un bloque de Ruby se puede controlar con las declaraciones break
, next
y redo
.
break
La sentencia break
saldrá del bloque inmediatamente. Cualquier instrucción restante en el bloque se omitirá, y la iteración terminará:
actions = %w(run jump swim exit macarena)
index = 0
while index < actions.length
action = actions[index]
break if action == "exit"
index += 1
puts "Currently doing this action: #{action}"
end
# Currently doing this action: run
# Currently doing this action: jump
# Currently doing this action: swim
next
La next
declaración volverá a la parte superior del bloque inmediatamente y continuará con la siguiente iteración. Cualquier instrucción restante en el bloque será omitida:
actions = %w(run jump swim rest macarena)
index = 0
while index < actions.length
action = actions[index]
index += 1
next if action == "rest"
puts "Currently doing this action: #{action}"
end
# Currently doing this action: run
# Currently doing this action: jump
# Currently doing this action: swim
# Currently doing this action: macarena
redo
La instrucción de redo
volverá a la parte superior del bloque inmediatamente y volverá a intentar la misma iteración. Cualquier instrucción restante en el bloque será omitida:
actions = %w(run jump swim sleep macarena)
index = 0
repeat_count = 0
while index < actions.length
action = actions[index]
puts "Currently doing this action: #{action}"
if action == "sleep"
repeat_count += 1
redo if repeat_count < 3
end
index += 1
end
# Currently doing this action: run
# Currently doing this action: jump
# Currently doing this action: swim
# Currently doing this action: sleep
# Currently doing this action: sleep
# Currently doing this action: sleep
# Currently doing this action: macarena
Enumerable
enumerable
Además de los bucles, estas declaraciones funcionan con los métodos de iteración Enumerable, como each
y el map
:
[1, 2, 3].each do |item|
next if item.even?
puts "Item: #{item}"
end
# Item: 1
# Item: 3
Valores de bloque de resultados
Tanto en la declaración de break
como en la next
, se puede proporcionar un valor, y se usará como un valor de resultado de bloque:
even_value = for value in [1, 2, 3]
break value if value.even?
end
puts "The first even value is: #{even_value}"
# The first even value is: 2
lanzar
A diferencia de muchos otros lenguajes de programación, las palabras clave de throw
y catch
no están relacionadas con el manejo de excepciones en Ruby.
En Ruby, throw
y catch
actúan un poco como etiquetas en otros idiomas. Se utilizan para cambiar el flujo de control, pero no están relacionados con un concepto de "error", como lo son las excepciones.
catch(:out) do
catch(:nested) do
puts "nested"
end
puts "before"
throw :out
puts "will not be executed"
end
puts "after"
# prints "nested", "before", "after"
Flujo de control con sentencias lógicas.
Si bien puede parecer contrario a la intuición, puede usar operadores lógicos para determinar si se ejecuta una declaración. Por ejemplo:
File.exist?(filename) or STDERR.puts "#{filename} does not exist!"
Esto verificará si el archivo existe y solo imprimirá el mensaje de error si no existe. La declaración or
es perezosa, lo que significa que dejará de ejecutarse una vez que esté seguro de si el valor es verdadero o falso. Tan pronto como se determina que el primer término es verdadero, no hay necesidad de verificar el valor del otro término. Pero si el primer término es falso, debe verificar el segundo término.
Un uso común es establecer un valor predeterminado:
glass = glass or 'full' # Optimist!
Eso establece el valor del glass
en 'lleno' si aún no está configurado. Más concisamente, puede utilizar la versión simbólica de or
:
glass ||= 'empty' # Pessimist.
También es posible ejecutar la segunda instrucción solo si la primera es falsa:
File.exist?(filename) and puts "#{filename} found!"
De nuevo, and
es perezoso, por lo que solo ejecutará la segunda instrucción si es necesario para llegar a un valor.
El operador or
tiene menor prioridad que and
. Del mismo modo, ||
tiene menor precedencia que &&
. Las formas de los símbolos tienen mayor prioridad que las formas de las palabras. Esto es útil para saber cuándo desea mezclar esta técnica con la asignación:
a = 1 and b = 2
#=> a==1
#=> b==2
a = 1 && b = 2; puts a, b
#=> a==2
#=> b==2
Tenga en cuenta que la Guía de estilo Ruby recomienda :
El
and
yor
palabras clave están prohibidos. La legibilidad añadida mínima no vale la pena por la alta probabilidad de introducir errores sutiles. Para expresiones booleanas, use siempre&&
y||
en lugar. Para control de flujo, useif
y aunless
;&&
y||
También son aceptables pero menos claras.
comenzar
El bloque de begin
es una estructura de control que agrupa varias declaraciones.
begin
a = 7
b = 6
a * b
end
Un bloque de begin
devolverá el valor de la última instrucción en el bloque. El siguiente ejemplo devolverá 3
.
begin
1
2
3
end
El bloque de begin
es útil para la asignación condicional utilizando el operador ||=
donde se pueden requerir varias declaraciones para devolver un resultado.
circumference ||=
begin
radius = 7
tau = Math::PI * 2
tau * radius
end
También se puede combinar con otras estructuras de bloque, como rescue
, ensure
, while
if
, a unless
, etc. para proporcionar un mayor control del flujo del programa.
Begin
bloques Begin
no son bloques de código, como { ... }
o do ... end
; no pueden ser pasados a funciones.
retorno vs. siguiente: retorno no local en un bloque
Considere este fragmento roto :
def foo
bar = [1, 2, 3, 4].map do |x|
return 0 if x.even?
x
end
puts 'baz'
bar
end
foo # => 0
Se podría esperar que el return
produzca un valor para la matriz de resultados de bloque del map
. Entonces, el valor de retorno de foo
sería [1, 0, 3, 0]
. En su lugar, return
devuelve un valor del método foo
. Tenga en cuenta que baz
no se imprime, lo que significa que la ejecución nunca llegó a esa línea.
next
con un valor hace el truco. Actúa como un return
nivel de bloque.
def foo
bar = [1, 2, 3, 4].map do |x|
next 0 if x.even?
x
end
puts 'baz'
bar
end
foo # baz
# => [1, 0, 3, 0]
En ausencia de una return
, el valor devuelto por el bloque es el valor de su última expresión.
Or-Equals / Operador de asignación condicional (|| =)
Ruby tiene un operador o-igual que permite que un valor se asigne a una variable si y solo si esa variable se evalúa como nil
o false
.
||= # this is the operator that achieves this.
este operador con los tubos dobles que representan o y el signo igual que representa la asignación de un valor. Puedes pensar que representa algo como esto:
x = x || y
Este ejemplo anterior no es correcto. El operador or-equals en realidad representa esto:
x || x = y
Si x
evalúa como nil
o false
, a x
se le asigna el valor de y
, de lo contrario, no se modifica.
Aquí hay un caso práctico de uso del operador or-igual. Imagina que tienes una parte de tu código que se espera que envíe un correo electrónico a un usuario. ¿Qué debe hacer si por cualquier motivo no hay un correo electrónico para este usuario? Podrías escribir algo como esto:
if user_email.nil?
user_email = "[email protected]"
end
Usando el operador or-equals podemos cortar todo este fragmento de código, brindando un control y funcionalidad claros y claros.
user_email ||= "[email protected]"
En los casos donde false
es un valor válido, se debe tener cuidado de no anularlo accidentalmente:
has_been_run = false
has_been_run ||= true
#=> true
has_been_run = false
has_been_run = true if has_been_run.nil?
#=> false
Operador ternario
Ruby tiene un operador ternario ( ?:
:), Que devuelve uno de los dos valores en función de si una condición se evalúa como verdadera:
conditional ? value_if_truthy : value_if_falsy
value = true
value ? "true" : "false"
#=> "true"
value = false
value ? "true" : "false"
#=> "false"
es lo mismo que escribir if a then b else c end
, aunque se prefiere el ternario
Ejemplos:
puts (if 1 then 2 else 3 end) # => 2
puts 1 ? 2 : 3 # => 2
x = if 1 then 2 else 3 end
puts x # => 2
Operador de flip-flop
El operador flip flop ..
se usa entre dos condiciones en una declaración condicional:
(1..5).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4]
La condición se evalúa como false
hasta que la primera parte se vuelva true
. Luego se evalúa como true
hasta que la segunda parte se vuelva true
. Después de eso cambia a false
otra vez.
Este ejemplo ilustra lo que se está seleccionando:
[1, 2, 2, 3, 4, 4, 5].select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 2, 3, 4]
El operador de flip-flop solo trabaja dentro de ifs (incluyendo a unless
) y el operador ternario. De lo contrario, se está considerando como el operador de rango.
(1..5).select do |e|
(e == 2) .. (e == 4)
end
# => ArgumentError: bad value for range
Puede cambiar de false
a true
y hacia atrás varias veces:
((1..5).to_a * 2).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4, 2, 3, 4]