Ruby Language
Kontrola przepływu
Szukaj…
jeśli, elsif, else i koniec
Ruby oferuje oczekiwane wyrażenia if
i else
dla rozgałęzionej logiki, zakończone słowem kluczowym 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
W Ruby, if
instrukcje są wyrażeniami, które oceniają na wartość, a wynik można przypisać do zmiennej:
status = if age < 18
:minor
else
:adult
end
Ruby oferuje także trójskładnikowe operatory w stylu C ( szczegóły tutaj ), które można wyrazić jako:
some_statement ? if_true : if_false
Oznacza to, że powyższy przykład z użyciem if-else można również zapisać jako
status = age < 18 ? :minor : :adult
Dodatkowo Ruby oferuje słowo kluczowe elsif
które akceptuje wyrażenie, aby umożliwić dodatkową logikę rozgałęziania:
label = if shirt_size == :s
'small'
elsif shirt_size == :m
'medium'
elsif shirt_size == :l
'large'
else
'unknown size'
end
Jeśli żaden z warunków w łańcuchu if
/ elsif
jest spełniony i nie istnieje żadna else
klauzula, wówczas wyrażenie przyjmuje wartość zero. Może to być przydatne w interpolacji ciągów, ponieważ nil.to_s
jest pustym ciągiem:
"user#{'s' if @users.size != 1}"
Wartości prawdy i fałszu
W Ruby istnieją dokładnie dwie wartości, które są uważane za „falsy” i zwrócą wartość false, gdy zostaną przetestowane jako warunek dla wyrażenia if
. Oni są:
-
nil
- boolean
false
Wszystkie pozostałe wartości są uważane za „prawdziwe”, w tym:
-
0
- zero liczbowe (liczba całkowita lub inna) -
""
- Puste ciągi -
"\n"
- Ciągi zawierające tylko białe znaki -
[]
- Puste tablice -
{}
- Opróżnij skróty
Weźmy na przykład następujący 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", {})
Wyjdzie:
false is falsy
nil is falsy
0 is truthy
empty string is truthy
\n is truthy
empty array is truthy
empty hash is truthy
podczas, dopóki
while
pętla wykonuje blok podczas gdy dany warunek jest spełniony:
i = 0
while i < 5
puts "Iteration ##{i}"
i +=1
end
until
pętli wykonuje blok natomiast uwarunkowane jest fałszywe:
i = 0
until i == 5
puts "Iteration ##{i}"
i +=1
end
Wbudowany jeśli / o ile
Typowym wzorcem jest użycie wstawki lub końca, if
lub unless
:
puts "x is less than 5" if x < 5
Jest to znane jako modyfikator warunkowy i jest przydatnym sposobem dodania prostego kodu ochronnego i wczesnych zwrotów:
def save_to_file(data, filename)
raise "no filename given" if filename.empty?
return false unless data.valid?
File.write(filename, data)
end
Nie można dodać klauzuli else
do tych modyfikatorów. Ponadto generalnie nie zaleca się używania modyfikatorów warunkowych w głównej logice - w przypadku złożonego kodu należy użyć normalnego if
, elsif
, else
zamiast tego.
chyba że
Powszechnym stwierdzeniem jest if !(some condition)
. Ruby oferuje alternatywę dla instrukcji unless
.
Struktura jest dokładnie taka sama jak instrukcja if
, ale warunek jest ujemny. Ponadto, unless
oświadczenie nie obsługuje elsif
ale to nie obsługuje else
:
# Prints not inclusive
unless 'hellow'.include?('all')
puts 'not inclusive'
end
Oświadczenie o sprawie
Ruby używa słowa kluczowego case
do instrukcji switch.
Zgodnie z Ruby Docs :
Instrukcje case składają się z warunku opcjonalnego, który znajduje się w pozycji argumentu
case
, i zero lub więcej,when
klauzulami. Pierwsza klauzulawhen
pasująca do warunku (lub do oceny logicznej prawdy, jeśli warunek jest pusty) „wygrywa”, a sekcja kodu jest wykonywana. Wartość instrukcji case jest wartością klauzuli pomyślnegowhen
lubnil
jeśli takiej klauzuli nie ma.Instrukcja case może kończyć się klauzulą
else
. Za każdym razem,when
instrukcja może mieć wiele wartości kandydujących, oddzielonych przecinkami.
Przykład:
case x
when 1,2,3
puts "1, 2, or 3"
when 10
puts "10"
else
puts "Some other number"
end
Krótsza wersja:
case x
when 1,2,3 then puts "1, 2, or 3"
when 10 then puts "10"
else puts "Some other number"
end
Wartość klauzuli case
jest dopasowywana do każdej klauzuli when
przy użyciu metody ===
(nie ==
). Dlatego można go używać z różnymi rodzajami obiektów.
case
oświadczenie może być używany z zakresów :
case 17
when 13..19
puts "teenager"
end
case
oświadczenie może być używany z RegExp :
case "google"
when /oo/
puts "word contains oo"
end
Instrukcja case
może być używana z Proc lub lambda:
case 44
when -> (n) { n.even? or n < 0 }
puts "even or less than zero"
end
Instrukcja case
może być używana z klasami :
case x
when Integer
puts "It's an integer"
when String
puts "It's a string"
end
Wdrażając metodę ===
możesz stworzyć własne klasy dopasowania:
class Empty
def self.===(object)
!object or "" == object
end
end
case ""
when Empty
puts "name was empty"
else
puts "name is not empty"
end
Instrukcja case
może być używana bez wartości do dopasowania do:
case
when ENV['A'] == 'Y'
puts 'A'
when ENV['B'] == 'Y'
puts 'B'
else
puts 'Neither A nor B'
end
Instrukcja case
ma wartość, więc możesz użyć jej jako argumentu metody lub w przypisaniu:
description = case 16
when 13..19 then "teenager"
else ""
end
Kontrola pętli za pomocą break, next i redo
Przepływ wykonania bloku Ruby może być kontrolowany za pomocą instrukcji break
, next
i redo
.
break
Instrukcja break
natychmiast opuści blok. Wszelkie pozostałe instrukcje w bloku zostaną pominięte, a iteracja zakończy się:
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
instrukcja natychmiast powróci na górę bloku i przejdzie do następnej iteracji. Wszelkie pozostałe instrukcje w bloku zostaną pominięte:
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
Instrukcja redo
natychmiast powróci na górę bloku i powtórzy tę samą iterację. Wszelkie pozostałe instrukcje w bloku zostaną pominięte:
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
iteracja
Oprócz pętli, te instrukcje działają z metodami iteracji Enumerable, takimi jak each
i map
:
[1, 2, 3].each do |item|
next if item.even?
puts "Item: #{item}"
end
# Item: 1
# Item: 3
Blokuj wartości wyników
W obu instrukcjach break
i next
można podać wartość, która zostanie użyta jako wynik wyniku bloku:
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
rzucać, łapać
W przeciwieństwie do wielu innych języków programowania, słowa kluczowe „ throw
i catch
nie są powiązane z obsługą wyjątków w Rubim.
W Ruby throw
i catch
działają trochę jak etykiety w innych językach. Służą do zmiany przepływu sterowania, ale nie są związane z pojęciem „błędu”, jak w przypadku wyjątków.
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"
Kontroluj przepływ za pomocą instrukcji logicznych
Chociaż może się to wydawać sprzeczne z intuicją, można użyć operatorów logicznych do ustalenia, czy instrukcja jest uruchamiana. Na przykład:
File.exist?(filename) or STDERR.puts "#{filename} does not exist!"
Spowoduje to sprawdzenie, czy plik istnieje, i wydrukuje komunikat o błędzie, jeśli nie istnieje. Instrukcja or
jest leniwa, co oznacza, że przestanie działać, gdy będzie pewne, czy jej wartość jest prawdą czy fałszem. Gdy tylko pierwszy warunek okaże się prawdziwy, nie ma potrzeby sprawdzania wartości drugiego terminu. Ale jeśli pierwszy termin jest fałszywy, musi sprawdzić drugi termin.
Typowym zastosowaniem jest ustawienie wartości domyślnej:
glass = glass or 'full' # Optimist!
To ustawia wartość glass
na „pełny”, jeśli nie jest jeszcze ustawiona. Mówiąc dokładniej, możesz użyć symbolicznej wersji or
:
glass ||= 'empty' # Pessimist.
Możliwe jest również uruchomienie drugiej instrukcji tylko wtedy, gdy pierwsza jest fałszywa:
File.exist?(filename) and puts "#{filename} found!"
Ponownie and
jest leniwy, więc wykona drugie polecenie tylko w razie potrzeby, aby uzyskać wartość.
Operator or
ma niższy priorytet niż and
. Podobnie ||
ma niższy priorytet niż &&
. Formy symboli mają wyższy priorytet niż formy słów. Warto wiedzieć, kiedy chcesz połączyć tę technikę z zadaniem:
a = 1 and b = 2
#=> a==1
#=> b==2
a = 1 && b = 2; puts a, b
#=> a==2
#=> b==2
Zauważ, że Ruby Style Guide zaleca :
and
ior
słowa kluczowe są zakazane. Minimalna dodana czytelność nie jest po prostu warta wysokiego prawdopodobieństwa wprowadzenia subtelnych błędów. W przypadku wyrażeń boolowskich zawsze używaj&&
i||
zamiast. Do kontroli przepływu użyj,if
i ounless
; i&&
i||
są również do przyjęcia, ale mniej jasne.
początek, koniec
begin
blok jest strukturą sterowania, który grupuje wiele wypowiedzi.
begin
a = 7
b = 6
a * b
end
begin
blok zwróci wartość ostatniej instrukcji w bloku. Poniższy przykład zwróci 3
.
begin
1
2
3
end
begin
blok jest użyteczny do przypisania warunkowego pomocą ||=
operatora, gdzie może być wymagany wielu instrukcji zwraca wynik.
circumference ||=
begin
radius = 7
tau = Math::PI * 2
tau * radius
end
Można go również łączyć z innymi strukturami blokowymi, takimi jak rescue
, ensure
, while
, if
, unless
itd., Aby zapewnić lepszą kontrolę nad przebiegiem programu.
Bloki Begin
nie są blokami kodu, takimi jak { ... }
lub do ... end
; nie można ich przekazać do funkcji.
return vs. next: nielokalny zwrot w bloku
Rozważ ten uszkodzony fragment:
def foo
bar = [1, 2, 3, 4].map do |x|
return 0 if x.even?
x
end
puts 'baz'
bar
end
foo # => 0
Można oczekiwać, że return
wartość dla tablicy wyników bloków map
. Zatem zwracana wartość foo
wynosiłaby [1, 0, 3, 0]
. Zamiast tego return
zwraca wartość z metody foo
. Zauważ, że baz
nie jest drukowany, co oznacza, że wykonanie nigdy nie osiągnęło tej linii.
next
z wartością robi lewę. Działa jak return
poziomie bloku.
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]
W przypadku braku return
wartość zwracana przez blok jest wartością jego ostatniego wyrażenia.
Operator przypisania równego lub warunkowego (|| =)
Ruby ma operator or-equals, który pozwala na przypisanie wartości zmiennej tylko wtedy, gdy zmienna ma wartość nil
lub false
.
||= # this is the operator that achieves this.
ten operator z podwójnymi potokami reprezentującymi lub i znakiem równości reprezentującym przypisanie wartości. Możesz myśleć, że reprezentuje coś takiego:
x = x || y
ten powyższy przykład jest niepoprawny. Operator or-equals faktycznie reprezentuje to:
x || x = y
Jeśli x
wartość nil
lub false
wówczas x
ma przypisaną wartość y
, w przeciwnym razie pozostaje niezmienione.
Oto praktyczny przypadek użycia operatora or-equals. Wyobraź sobie, że masz część kodu, która ma wysłać wiadomość e-mail do użytkownika. Co robisz, jeśli z jakiegokolwiek powodu nie ma wiadomości e-mail dla tego użytkownika. Możesz napisać coś takiego:
if user_email.nil?
user_email = "[email protected]"
end
Za pomocą operatora or-equals możemy wyciąć cały fragment kodu, zapewniając czystą, jasną kontrolę i funkcjonalność.
user_email ||= "[email protected]"
W przypadkach, w których false
jest prawidłową wartością, należy zachować ostrożność, aby przypadkowo jej nie zastąpić:
has_been_run = false
has_been_run ||= true
#=> true
has_been_run = false
has_been_run = true if has_been_run.nil?
#=> false
Operator trójskładnikowy
Ruby ma operator trójskładnikowy ( ?:
:), Który zwraca jedną z dwóch wartości na podstawie tego, czy warunek zostanie oceniony jako prawdziwy:
conditional ? value_if_truthy : value_if_falsy
value = true
value ? "true" : "false"
#=> "true"
value = false
value ? "true" : "false"
#=> "false"
jest to to samo, co pisanie, if a then b else c end
, choć trójka jest preferowana
Przykłady:
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
Operator Flip-Flop
Operator flip-flop ..
jest używany między dwoma warunkami w instrukcji warunkowej:
(1..5).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4]
Warunek ma wartość false
dopóki pierwsza część nie zostanie true
. Następnie ocenia się na true
aż druga część stanie się true
. Następnie ponownie zmienia się na false
.
Ten przykład ilustruje, co jest wybierane:
[1, 2, 2, 3, 4, 4, 5].select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 2, 3, 4]
Operator flip-flop działa tylko wewnątrz ifs (włączając unless
) i operator trójskładnikowy. W przeciwnym razie jest uważany za operatora zasięgu.
(1..5).select do |e|
(e == 2) .. (e == 4)
end
# => ArgumentError: bad value for range
Może wielokrotnie przełączać się z false
na true
i wstecz:
((1..5).to_a * 2).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4, 2, 3, 4]