Ruby Language
Controle stroom
Zoeken…
als, elsif, anders en einde
Ruby biedt de verwachte if
en else
uitdrukkingen voor vertakkende logica, beëindigd door het end
trefwoord:
# 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
In Ruby, if
statements uitdrukkingen zijn die evalueren naar een waarde en het resultaat kan worden toegewezen aan een variabele:
status = if age < 18
:minor
else
:adult
end
Ruby biedt ook ternaire operatoren in C-stijl ( zie hier voor details ) die kunnen worden uitgedrukt als:
some_statement ? if_true : if_false
Dit betekent dat het bovenstaande voorbeeld met if-else ook kan worden geschreven als
status = age < 18 ? :minor : :adult
Bovendien biedt Ruby het sleutelwoord elsif
dat een expressie accepteert om aanvullende vertakkingslogica mogelijk te maken:
label = if shirt_size == :s
'small'
elsif shirt_size == :m
'medium'
elsif shirt_size == :l
'large'
else
'unknown size'
end
Als geen van de omstandigheden in een if
/ elsif
keten waar zijn, en er is geen else
artikel, dan is de expressie de waarde nihil. Dit kan handig zijn binnen stringinterpolatie, omdat nil.to_s
de lege string is:
"user#{'s' if @users.size != 1}"
Waarheid en valse waarden
In Ruby zijn er precies twee waarden die als "vals" worden beschouwd en die onwaar zullen retourneren wanneer ze worden getest als voorwaarde voor een if
expressie. Zij zijn:
-
nil
- boolean
false
Alle andere waarden worden als "waarheidsgetrouw" beschouwd, waaronder:
-
0
- numerieke nul (geheel of anders) -
""
- Lege tekenreeksen -
"\n"
- Tekenreeksen die alleen witruimte bevatten -
[]
- Lege arrays -
{}
- Lege hashes
Neem bijvoorbeeld de volgende code:
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", {})
Zal uitvoeren:
false is falsy
nil is falsy
0 is truthy
empty string is truthy
\n is truthy
empty array is truthy
empty hash is truthy
terwijl, tot
Een while
lus voert het blok uit terwijl aan de gegeven voorwaarde is voldaan:
i = 0
while i < 5
puts "Iteration ##{i}"
i +=1
end
Een until
lus voert het blok terwijl de voorwaardelijke vals:
i = 0
until i == 5
puts "Iteration ##{i}"
i +=1
end
Inline indien / tenzij
Een gebruikelijk patroon is het gebruik van een inline, of een volgnummer, if
of unless
:
puts "x is less than 5" if x < 5
Dit staat bekend als een voorwaardelijke modificator en is een handige manier om eenvoudige bewakingscode en vroegtijdig rendement toe te voegen:
def save_to_file(data, filename)
raise "no filename given" if filename.empty?
return false unless data.valid?
File.write(filename, data)
end
Het is niet mogelijk om een else
component aan deze modificaties toe te voegen. Ook is het in het algemeen niet aan te raden om voorwaardelijke modifiers gebruiken in het hoofdgebouw logica - Voor complexe code moet men gebruik maken van de normale if
, elsif
, else
plaats.
tenzij
Een veel voorkomende verklaring is if !(some condition)
. Ruby biedt het alternatief van de unless
verklaring.
De structuur is exact hetzelfde als een if
instructie, behalve dat de voorwaarde negatief is. Ook ondersteunt de unless
instructie geen elsif
, maar wel else
:
# Prints not inclusive
unless 'hellow'.include?('all')
puts 'not inclusive'
end
Case statement
Ruby gebruikt het sleutelwoord case
voor schakelinstructies.
Volgens de Ruby Docs :
Case-uitspraken bestaan uit een optionele voorwaarde, die in de positie van argument tot
case
, en nul of meerwhen
clausules. De eerstewhen
clausule om de conditie te passen (of te evalueren in Boolean waarheid, als de voorwaarde null) “wint”, en de code strofe wordt uitgevoerd. De waarde van de zaak uitspraak is de waarde van de succesvollewhen
clausule, ofnil
als er geen dergelijke clausule.Een case-statement kan eindigen met een
else
clausule. Elke keerwhen
een statement meerdere kandidaatwaarden kan hebben, gescheiden door komma's.
Voorbeeld:
case x
when 1,2,3
puts "1, 2, or 3"
when 10
puts "10"
else
puts "Some other number"
end
Kortere versie:
case x
when 1,2,3 then puts "1, 2, or 3"
when 10 then puts "10"
else puts "Some other number"
end
De waarde van de case
clausule komt overeen met elke clausule when
clausule wordt gebruikt met de methode ===
(niet ==
). Daarom kan het worden gebruikt met verschillende soorten objecten.
Een case
statement kan worden gebruikt met Ranges :
case 17
when 13..19
puts "teenager"
end
Een case
statement kan worden gebruikt met een Regexp :
case "google"
when /oo/
puts "word contains oo"
end
Een case
statement kan worden gebruikt met een Proc of lambda:
case 44
when -> (n) { n.even? or n < 0 }
puts "even or less than zero"
end
Een case
-instructie kan worden gebruikt met Classes :
case x
when Integer
puts "It's an integer"
when String
puts "It's a string"
end
Door de ===
methode te implementeren, kunt u uw eigen wedstrijdklassen maken:
class Empty
def self.===(object)
!object or "" == object
end
end
case ""
when Empty
puts "name was empty"
else
puts "name is not empty"
end
Een case
statement kan worden gebruikt zonder een waarde die overeenkomt met:
case
when ENV['A'] == 'Y'
puts 'A'
when ENV['B'] == 'Y'
puts 'B'
else
puts 'Neither A nor B'
end
Een case
instructie heeft een waarde, dus u kunt deze gebruiken als methode-argument of in een opdracht:
description = case 16
when 13..19 then "teenager"
else ""
end
Lusbesturing met pauze, volgende en opnieuw uitvoeren
De stroom van uitvoering van een Ruby-blok kan worden geregeld met de instructies break
, next
en redo
.
break
De break
verlaat het blok onmiddellijk. Alle resterende instructies in het blok worden overgeslagen en de iteratie wordt beëindigd:
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
De next
instructie keert onmiddellijk terug naar de bovenkant van het blok en gaat door met de volgende iteratie. Alle resterende instructies in het blok worden overgeslagen:
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
De instructie redo
keert onmiddellijk terug naar de bovenkant van het blok en probeert dezelfde iteratie opnieuw. Alle resterende instructies in het blok worden overgeslagen:
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
iteratie
Naast lussen werken deze instructies met Enumerable iteratiemethoden, zoals each
en map
:
[1, 2, 3].each do |item|
next if item.even?
puts "Item: #{item}"
end
# Item: 1
# Item: 3
Resultaatwaarden blokkeren
In zowel de break
als de next
instructies kan een waarde worden opgegeven en deze wordt gebruikt als een blokresultaatwaarde:
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
gooien vangen
In tegenstelling tot veel andere programmeertalen, zijn de trefwoorden throw
en catch
niet gerelateerd aan het afhandelen van uitzonderingen in Ruby.
In Ruby gedragen throw
en catch
een beetje zoals labels in andere talen. Ze worden gebruikt om de besturingsstroom te wijzigen, maar zijn niet gerelateerd aan een concept van "fout" zoals uitzonderingen.
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"
Besturingsstroom met logische instructies
Hoewel het contra-intuïtief lijkt, kunt u logische operatoren gebruiken om te bepalen of een instructie al dan niet wordt uitgevoerd. Bijvoorbeeld:
File.exist?(filename) or STDERR.puts "#{filename} does not exist!"
Hiermee wordt gecontroleerd of het bestand bestaat en wordt alleen het foutbericht afgedrukt als dit niet het geval is. De instructie or
is lui, wat betekent dat deze stopt met uitvoeren zodra het zeker is of de waarde waar of onwaar is. Zodra de eerste term waar blijkt te zijn, hoeft de waarde van de andere term niet te worden gecontroleerd. Maar als de eerste term vals is, moet deze de tweede term controleren.
Een algemeen gebruik is om een standaardwaarde in te stellen:
glass = glass or 'full' # Optimist!
Dat stelt de waarde van glass
op 'vol' als het nog niet is ingesteld. Korter, u kunt de symbolische versie gebruiken van or
:
glass ||= 'empty' # Pessimist.
Het is ook mogelijk om de tweede instructie alleen uit te voeren als de eerste onwaar is:
File.exist?(filename) and puts "#{filename} found!"
Nogmaals, and
is lui, dus het zal alleen de tweede instructie uitvoeren als dat nodig is om tot een waarde te komen.
De operator or
heeft een lagere prioriteit dan and
. Evenzo ||
heeft een lagere prioriteit dan &&
. De symboolvormen hebben een hogere prioriteit dan de woordvormen. Dit is handig om te weten wanneer je deze techniek met opdracht wilt combineren:
a = 1 and b = 2
#=> a==1
#=> b==2
a = 1 && b = 2; puts a, b
#=> a==2
#=> b==2
Merk op dat de Ruby Style Guide aanbeveelt :
De
and
enor
sleutelwoorden zijn verboden. De minimale toegevoegde leesbaarheid is de grote kans op het introduceren van subtiele bugs gewoon niet waard. Gebruik voor booleaanse uitdrukkingen altijd&&
en||
in plaats daarvan. Gebruikif
enunless
voor stroomregeling;&&
en||
zijn ook acceptabel, maar minder duidelijk.
begin, einde
Het begin
is een besturingsstructuur die meerdere instructies groepeert.
begin
a = 7
b = 6
a * b
end
Een begin
retourneert de waarde van de laatste instructie in het blok. Het volgende voorbeeld retourneert 3
.
begin
1
2
3
end
Het begin
is handig voor voorwaardelijke toewijzing met de operator ||=
waarbij meerdere instructies nodig kunnen zijn om een resultaat te retourneren.
circumference ||=
begin
radius = 7
tau = Math::PI * 2
tau * radius
end
Het kan ook worden gecombineerd met andere blokstructuren zoals rescue
, ensure
, while
, if
, unless
, enz. Om een grotere controle over de programmastroom te bieden.
Begin
zijn geen codeblokken, zoals { ... }
of do ... end
; ze kunnen niet worden doorgegeven aan functies.
terugkeer versus volgende: niet-lokale terugkeer in een blok
Beschouw dit gebroken fragment:
def foo
bar = [1, 2, 3, 4].map do |x|
return 0 if x.even?
x
end
puts 'baz'
bar
end
foo # => 0
Je zou verwachten dat return
een waarde oplevert voor de reeks blokresultaten van de map
. Dus de retourwaarde van foo
zou [1, 0, 3, 0]
. In plaats daarvan return
return een waarde van de methode foo
. Merk op dat baz
niet wordt afgedrukt, wat betekent dat de uitvoering die lijn nooit heeft bereikt.
next
met een waarde volstaat. Het fungeert als een return
blokniveau.
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]
Bij afwezigheid van een return
, is de waarde die door het blok wordt geretourneerd de waarde van zijn laatste uitdrukking.
Or-is gelijk aan / Voorwaardelijke toewijzingsoperator (|| =)
Ruby heeft een operator of is gelijk aan waarmee een waarde aan een variabele kan worden toegewezen als en alleen als die variabele nil
of false
.
||= # this is the operator that achieves this.
deze operator met de dubbele pijpen die of vertegenwoordigen en het isgelijkteken dat een waarde toekent. Je denkt misschien dat het zoiets als dit vertegenwoordigt:
x = x || y
dit bovenstaande voorbeeld is niet correct. De operator or-is gelijk aan dit:
x || x = y
Als x
aan nil
of false
, krijgt x
de waarde van y
en wordt anders ongewijzigd gelaten.
Hier is een praktische use-case van de operator of is gelijk aan. Stel je voor dat je een deel van je code hebt dat naar verwachting een e-mail naar een gebruiker zal verzenden. Wat doe je als er om welke reden dan ook geen e-mail is voor deze gebruiker. Je zou zoiets kunnen schrijven:
if user_email.nil?
user_email = "[email protected]"
end
Met de operator of-is gelijk aan kunnen we dit hele stuk code snijden, voor een schone, duidelijke controle en functionaliteit.
user_email ||= "[email protected]"
In gevallen waarin false
een geldige waarde is, moet ervoor worden gezorgd dat deze niet per ongeluk wordt opgeheven:
has_been_run = false
has_been_run ||= true
#=> true
has_been_run = false
has_been_run = true if has_been_run.nil?
#=> false
Ternary operator
Ruby heeft een ternaire operator ( ?:
:), Die een waarde van twee retourneert op basis van of een voorwaarde als waarheidsgetrouw wordt geëvalueerd:
conditional ? value_if_truthy : value_if_falsy
value = true
value ? "true" : "false"
#=> "true"
value = false
value ? "true" : "false"
#=> "false"
het is hetzelfde als schrijven if a then b else c end
, hoewel de ternaire de voorkeur heeft
Voorbeelden:
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
Flip-Flop-operator
De flip flop-operator ..
wordt gebruikt tussen twee voorwaarden in een voorwaardelijke verklaring:
(1..5).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4]
De voorwaarde wordt als false
geëvalueerd totdat het eerste deel true
. Vervolgens wordt het geëvalueerd tot true
totdat het tweede deel true
. Daarna schakelt het weer naar false
.
Dit voorbeeld illustreert wat er wordt geselecteerd:
[1, 2, 2, 3, 4, 4, 5].select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 2, 3, 4]
De flip-flop-operator werkt alleen binnen ifs (inclusief unless
) en ternaire operator. Anders wordt het beschouwd als de bereikoperator.
(1..5).select do |e|
(e == 2) .. (e == 4)
end
# => ArgumentError: bad value for range
Het kan meerdere keren van false
naar true
en omgekeerd schakelen:
((1..5).to_a * 2).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4, 2, 3, 4]