Ruby Language
Styrningsflöde
Sök…
om, elsif, annars och slut
Ruby erbjuder den förväntade if
och else
uttryck för förgrening logik, avslutas vid end
sökord:
# 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
I Ruby, if
uttalanden är uttryck som utvärderar till ett värde, och resultatet kan tilldelas en variabel:
status = if age < 18
:minor
else
:adult
end
Ruby erbjuder också C-stil ternära operatörer ( se här för detaljer ) som kan uttryckas som:
some_statement ? if_true : if_false
Detta betyder att exemplet ovan med if-else också kan skrivas som
status = age < 18 ? :minor : :adult
Dessutom erbjuder Ruby elsif
nyckelord som accepterar ett uttryck för att möjliggöra ytterligare grenlogik:
label = if shirt_size == :s
'small'
elsif shirt_size == :m
'medium'
elsif shirt_size == :l
'large'
else
'unknown size'
end
Om inget av villkoren i en if
/ elsif
kedja är sanna, och det inte finns någon else
klausul, utvärderas uttrycket till noll. Detta kan vara användbart i stränginterpolering, eftersom nil.to_s
är den tomma strängen:
"user#{'s' if @users.size != 1}"
Sannhets- och falskvärden
I Ruby finns det exakt två värden som betraktas som "förfalskade" och kommer att returnera falska när de testas som ett villkor för ett if
uttryck. Dom är:
-
nil
- boolesk
false
Alla andra värden betraktas som "sanningsenliga", inklusive:
-
0
- numerisk noll (heltal eller på annat sätt) -
""
- Tomma strängar -
"\n"
- Strängar som endast innehåller blanksteg -
[]
- Tomma matriser -
{}
- Tom hash
Ta till exempel följande kod:
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", {})
Kommer att matas ut:
false is falsy
nil is falsy
0 is truthy
empty string is truthy
\n is truthy
empty array is truthy
empty hash is truthy
medan, tills
En while
slinga exekverar blocket medan det givna villkor är uppfyllt:
i = 0
while i < 5
puts "Iteration ##{i}"
i +=1
end
En until
slinga kör blocket medan villkoret är felaktigt:
i = 0
until i == 5
puts "Iteration ##{i}"
i +=1
end
Inline om / om inte
Ett vanligt mönster är att använda en inline, eller släp, if
eller om unless
:
puts "x is less than 5" if x < 5
Detta är känt som en villkorlig modifierare och är ett praktiskt sätt att lägga till enkel skyddskod och tidiga returer:
def save_to_file(data, filename)
raise "no filename given" if filename.empty?
return false unless data.valid?
File.write(filename, data)
end
Det är inte möjligt att lägga till en else
klausul till dessa modifierare. Är det i allmänhet inte rekommenderas också att använda villkorliga modifierare inuti huvudlogik - För komplex kod bör man använda normal if
, elsif
, else
istället.
såvida inte
Ett vanligt uttalande är if !(some condition)
. Ruby erbjuder alternativet till om unless
uttalandet.
Strukturen är exakt densamma som ett if
uttalande, förutom att villkoret är negativt. Även unless
inte uttalandet inte stöd elsif
, men det stöd else
:
# Prints not inclusive
unless 'hellow'.include?('all')
puts 'not inclusive'
end
Ärende
Ruby använder case
sökord för switch uttalanden.
Enligt Ruby Docs :
Ärendeförklaringar består av ett valfritt villkor, som är i positionen för ett argument till
case
, och noll eller merwhen
klausuler. Den förstawhen
klausulen för att matcha villkoret (eller för att utvärdera till den booleska sanningen, om villkoret är noll) "vinner", och dess kodstans körs. Värdet på ärendeklarationen är värdet på den framgångsrikawhen
klausul, ellernil
om det inte finns någon sådan klausul.Ett ärende kan sluta med en
else
klausul. Var och enwhen
ett uttalande kan ha flera kandidatvärden, åtskilda med komma.
Exempel:
case x
when 1,2,3
puts "1, 2, or 3"
when 10
puts "10"
else
puts "Some other number"
end
Kortare version:
case x
when 1,2,3 then puts "1, 2, or 3"
when 10 then puts "10"
else puts "Some other number"
end
Värdet på case
klausulen matchas med varje when
klausul använder ===
metoden (ej ==
). Därför kan den användas med en mängd olika typer av objekt.
Ett case
kan användas med Ranges :
case 17
when 13..19
puts "teenager"
end
Ett case
kan användas med en Regexp :
case "google"
when /oo/
puts "word contains oo"
end
Ett case
kan användas med en Proc eller lambda:
case 44
when -> (n) { n.even? or n < 0 }
puts "even or less than zero"
end
Ett case
kan användas med klasser :
case x
when Integer
puts "It's an integer"
when String
puts "It's a string"
end
Genom att implementera metoden ===
du skapa dina egna matchningsklasser:
class Empty
def self.===(object)
!object or "" == object
end
end
case ""
when Empty
puts "name was empty"
else
puts "name is not empty"
end
Ett case
kan användas utan värde att matcha mot:
case
when ENV['A'] == 'Y'
puts 'A'
when ENV['B'] == 'Y'
puts 'B'
else
puts 'Neither A nor B'
end
Ett case
har ett värde, så du kan använda det som ett metodargument eller i en uppgift:
description = case 16
when 13..19 then "teenager"
else ""
end
Loop-kontroll med paus, nästa och gör om
Flödet av körning av ett Ruby-block kan kontrolleras med uttalanden om break
, next
och redo
om.
break
Den break
uttalande kommer att lämna blocket omedelbart. Alla återstående instruktioner i blocket kommer att hoppas över och iterationen slutar:
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
next
uttalande kommer omedelbart tillbaka till toppen av blocket och fortsätter med nästa iteration. Eventuella återstående instruktioner i blocket kommer att hoppas över:
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
redo
kommer omedelbart att återgå till toppen av blocket och försöka samma iteration igen. Eventuella återstående instruktioner i blocket kommer att hoppas över:
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
iteration
Förutom slingor, dessa uttalanden arbeta med uppräkningsbar iteration metoder, såsom each
och map
:
[1, 2, 3].each do |item|
next if item.even?
puts "Item: #{item}"
end
# Item: 1
# Item: 3
Blockera värden för resultat
I både break
och next
påstående kan ett värde anges och kommer att användas som ett blockresultatvärde:
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
kasta, fånga
Till skillnad från många andra programmeringsspråk, de throw
och catch
är nyckelord inte är relaterade till undantagshantering i Ruby.
I Ruby fungerar throw
och catch
lite som etiketter på andra språk. De används för att ändra kontrollflödet, men är inte relaterade till ett begrepp "fel" som undantag är.
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"
Kontrollera flödet med logiska uttalanden
Även om det kan verka motsatt, kan du använda logiska operatörer för att avgöra om ett uttal körs eller inte. Till exempel:
File.exist?(filename) or STDERR.puts "#{filename} does not exist!"
Detta kontrollerar om filen finns och skrivs bara ut felmeddelandet om den inte gör det. Uttalandet or
är uttömt, vilket innebär att det slutar köra när det är säkert vilket värde det är sant eller falskt. Så snart den första termen har visat sig vara sant, finns det inget behov av att kontrollera värdet på den andra termen. Men om den första termen är falsk, måste den kontrollera den andra termen.
En vanlig användning är att ställa in ett standardvärde:
glass = glass or 'full' # Optimist!
Det sätter värdet på glass
till "fullt" om det inte redan är inställt. Mer kortfattat kan du använda den symboliska versionen av or
:
glass ||= 'empty' # Pessimist.
Det är också möjligt att köra det andra uttalandet endast om det första är felaktigt:
File.exist?(filename) and puts "#{filename} found!"
Återigen, and
är lat så det kommer endast att utföra det andra uttalandet om det är nödvändigt för att komma fram till ett värde.
Operatören or
operatören har lägre prioritet än and
. På liknande sätt ||
har lägre prioritet än &&
. Symbolformerna har högre prioritet än ordformerna. Detta är praktiskt att veta när du vill blanda denna teknik med uppdrag:
a = 1 and b = 2
#=> a==1
#=> b==2
a = 1 && b = 2; puts a, b
#=> a==2
#=> b==2
Observera att Ruby Style Guide rekommenderar :
De
and
ochor
nyckelorden är förbjudna. Den minimala läsbarheten är bara inte värt den höga sannolikheten för att införa subtila buggar. Använd alltid&&
och||
för booleska uttryck istället. För flödeskontroll, användif
och omunless
;&&
och||
är också acceptabla men mindre tydliga.
börja, slut
begin
är en kontrollstruktur som grupperar flera uttalanden.
begin
a = 7
b = 6
a * b
end
En begin
blocket kommer att returnera värdet av den sista uttalandet i blocket. Följande exempel kommer att returnera 3
.
begin
1
2
3
end
begin
är användbart för villkorad tilldelning med hjälp av ||=
operatören där flera uttalanden kan krävas för att returnera ett resultat.
circumference ||=
begin
radius = 7
tau = Math::PI * 2
tau * radius
end
Det kan också kombineras med andra blockstrukturer såsom rescue
, ensure
, while
, if
, om unless
, etc för att ge större kontroll över programflödet.
Begin
block är inte kodblock, som { ... }
eller do ... end
; de kan inte överföras till funktioner.
retur kontra nästa: icke-lokal retur i ett block
Tänk på det här trasiga utdraget:
def foo
bar = [1, 2, 3, 4].map do |x|
return 0 if x.even?
x
end
puts 'baz'
bar
end
foo # => 0
Man kan förvänta sig att return
ger ett värde för map
array av blockresultat. Så returvärdet för foo
skulle vara [1, 0, 3, 0]
. I stället return
return ett värde från metoden foo
. Lägg märke till att baz
inte skrivs ut, vilket innebär att körningen aldrig nådde den raden.
next
med ett värde gör tricket. Det fungerar som en blocknivå return
.
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]
I avsaknad av en return
är värdet som returneras av blocket värdet på det sista uttrycket.
Or-Equals / Conditional allocation operator (|| =)
Ruby har en eller är lika med operatören som gör att ett värde kan tilldelas en variabel om och bara om den variabeln utvärderar antingen nil
eller false
.
||= # this is the operator that achieves this.
denna operatör med de dubbla rören som representerar eller och lika skylten representerar tilldelning av ett värde. Du kanske tror att det representerar något liknande:
x = x || y
detta exempel ovan är inte korrekt. Operatören or-equals representerar faktiskt detta:
x || x = y
Om x
utvärderar till nil
eller false
tilldelas x
värdet på y
och lämnas oförändrat på annat sätt.
Här är ett praktiskt användningsfall av operatören eller likadan. Föreställ dig att du har en del av din kod som förväntas skicka ett e-postmeddelande till en användare. Vad gör du om det av någon anledning inte finns någon e-post för den här användaren. Du kanske skriver något liknande:
if user_email.nil?
user_email = "[email protected]"
end
Med hjälp av operatören or-equals kan vi klippa ut hela koddelen och ge ren, tydlig kontroll och funktionalitet.
user_email ||= "[email protected]"
I fall där false
är ett giltigt värde måste man se till att inte åsidosätta det av misstag:
has_been_run = false
has_been_run ||= true
#=> true
has_been_run = false
has_been_run = true if has_been_run.nil?
#=> false
Ternary operatör
Ruby har en ternär operatör ( ?:
:), Som returnerar ett av två värden baserat på om ett villkor utvärderas som sanningen:
conditional ? value_if_truthy : value_if_falsy
value = true
value ? "true" : "false"
#=> "true"
value = false
value ? "true" : "false"
#=> "false"
det är detsamma som att skriva if a then b else c end
, även om ternary föredras
Exempel:
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-operatör
Den flip flop operatör ..
används mellan två tillstånd i ett villkorligt uttalande:
(1..5).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4]
Villkoret utvärderas till false
tills den första delen blir true
. Därefter utvärderas det till true
tills den andra delen blir true
. Efter det byter det till false
igen.
Detta exempel illustrerar vad som väljs:
[1, 2, 2, 3, 4, 4, 5].select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 2, 3, 4]
Flip-flop-operatören fungerar endast inuti ifs (inklusive om unless
) och ternary operator. Annars betraktas det som avståndsoperatören.
(1..5).select do |e|
(e == 2) .. (e == 4)
end
# => ArgumentError: bad value for range
Det kan växla från false
till true
och bakåt flera gånger:
((1..5).to_a * 2).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4, 2, 3, 4]