Wyrażenia warunkowe

W poniższym rozdziale nauczysz się używać jednych z dwu (obok pętli) najważniejszych konstrukcji w programowaniu - wyrażeń warunkowych. Najpierw jednak poznamy pewne metody, dzięki którym nasze programy staną się interaktywne - dzięki temu łatwiej będzie nam się uczyć kolejnych zagadnień.

Interaktywność

Praktycznie każdy zaawansowany program oferuje użytkownikowi jakiś stopień interaktywności. Inaczej mówiąc, program komunikuje się z użytkownikiem i vice versa. Wiemy już jak w Rubym program może “powiedzieć” coś użytkownikowi - np. za pomocą metod puts i print. Pora dowiedzieć się jak użytkownik programu może coś mu “powiedzieć”. Służy do tego metoda gets:

#File: Floor.rb
puts "Type a number:"
n = gets
puts "Floor:"
puts n.to_i
jan@Stacja-Linux ~/Pulpit $ ruby Integer.rb
Type a number:

Przeanalizujmy powyższy program. Najpierw wyświetlany jest napis “Type a number:”. W następnej linijce, na zmienną n przypisywany jest wynik metody gets. Ale co jest jej wynikiem? To co wpisze użytkownik ;) Wpiszmy więc coś (np. 15.6) i wciśnijmy enter:

jan@Stacja-Linux ~/Pulpit $ ruby Integer.rb
Type a number:
15.6
Floor:
15

Otrzymaliśmy podłogę podanej liczby, ale jak to się stało? Po przypisaniu napisu "15.6" na zmienną n, wyświetlony został napis "Floor:". Następnie wyświetlona została zmienna n, przekształcona na typ liczby całkowitej (Przekształcenie to (zwane rzutowaniem lub konwersją) jest tu niezbędne, gdyż wynikiem metody gets jest zawsze napis). Zauważ, że powyższy program można napisać krócej i bez użycia zmiennych (ale chyba kosztem czytelności ;)):

#File: Floor2.rb
puts “Type a number:”
puts “Floor\n #{gets.to_i}”

Wyrażenie warunkowe if

Do tej pory pisaliśmy programy, które robiły to samo, niezależnie od otrzymanych danych (jedynymi możliwymi urozmaiceniami były błędy, gdy przekazane dane były niewłaściwe ;)). Pora to zmienić!

Przeanalizujmy wyrażenie if. Składnia jest następująca:

if (WARUNEK)
  INSTRUKCJE
elsif (INNY_WARUNEK)
  INNE_INSTRUKCJE
else
  JESZCZE_INNE_INSTRUKCJE
end

To co dzieje się po wykonaniu kodu zgodnego z powyższym schematem jest bardzo intuicyjne. Jeśli spełniony (tj. o wartości logicznej true) jest WARUNEK przy słowie kluczowym if, wykonają się INSTRUKCJE poniżej, aż do kolejnego słowa kluczowego (w tym przypadku elsif). Jeśli zaś WARUNEK ma wartość false, INSTRUKCJE te nie będą wykonane i nastąpi ewaluacja WARUNKU przy słowie elsif. Jeśli INNY_WARUNEK jest spełniony wykonają się INNE_INSTRUKCJE poniżej, aż do kolejnego słowa kluczowego (w tym przypadku else), w przeciwnym wypadku wykona się kod między słowami else i end.

Ilość warunków elsif jest dowolna, więc poprawne będą również wyrażenia o schemacie jak poniżej...:

if (WARUNEK_1)
  INSTRUKCJE_1
elsif (WARUNEK_2)
  INSTRUKCJE_2
elsif (WARUNEK_3)
  INSTRUKCJE_3
elsif (WARUNEK_4)
  INSTRUKCJE_4
else
  INSTRUKCJE_5
end

... i poniżej:
if (WARUNEK_1)
  INSTRUKCJE_1
else
  INSTRUKCJE_5
end

Co więcej, słowo kluczowe else, które “zbiera pozostałe przypadki” również nie jest obowiązkowe. Warunki nie muszą zaś być objęte w nawiasy (ale czasem poprawia to czytelność kodu). Poprawny będzie więc zapis:

if WARUNEK
  INSTRUKCJE
end

Spróbujmy teraz napisać i przeanalizować program, który sprawdzi, czy podana liczba jest podzielna przez 13:

 

#File: Divisibility.rb
puts "Type a number:"
n = gets.to_f
if n%13==0
  puts "#{n} is divisible by 13"
end 

Powyższy program prosi użytkownika o liczbę. Jeśli podana liczba jest podzielna przez 13, program wypisuje stosowny komunikat. W przeciwnym przypadku… nie robi nic. Spróbujmy przerobić ten program tak, aby był bardziej user-friendly:

#File: Divisibility2.rb
puts "Type a number:"
n = gets.to_f
if n%13==0
  puts "#{n} is divisible by 13"
else
  puts "#{n} is NOT divisible by 13"
end 

Teraz jeśli liczba nie będzie podzielna przez 13, użytkownik również otrzyma stosowną wiadomość.

Poniżej przykład programu (liczącego ilość miejsc zerowych w funkcji kwadratowej) z większą ilością warunków. Zauważ, że wyrażania warunkowe mogą być zagnieżdżone (tj. w instrukcjach pod jednym z warunków może znaleźć się kolejny if, a w nim kolejny, a w nim kolejny...):

#File: Roots.rb
puts "Define a in f(x) = ax**2+bx+c:"
a = gets.to_f
puts "Define b in f(x) = ax**2+bx+c:"
b = gets.to_f
puts "Define c in f(x) = ax**2+bx+c:"
c = gets.to_f
if a == 0
  if b == 0
    if c == 0
      puts "f(x) = 0 has an infinite number of roots ;)"
    else
      puts "f(x) = #{c} has no roots"
    end
  else
    puts "f(x) = #{b}x+#{c} has one root"
  end
else
  d = b**2 - 4*a*c
  if d > 0
    puts "f(x) = #{a}x**2+#{b}x+#{c} has two roots"
  elsif d == 0
    puts "f(x) = #{a}x**2+#{b}x+#{c} has one root"
  else
    puts "f(x) = #{a}x**2+#{b}x+#{c} has no roots"
  end
end 

Na koniec tego rozdziału mała ciekawostka. Skrócony zapis trójargumentowego wyrażenia warunkowego:

WARUNEK ? INSTRUKCJE_1 : INSTRUKCJE_2

Powyższy schemat kodu jest równoważny poniższemu:

if (WARUNEK)
  INSTRUKCJE_1
else
  INSTRUKCJE_1
end

Poniżej wykorzystanie operatora trójargumentowego na przykładzie naszego programu badającego podzielność przez 13 (operator trójargumentowy jest stosowany, gdy kod instrukcji do wykonania jest krótki - w przeciwnym wypadku, bardziej czytelny jest standardowy if):

#File: Divisibility3.rb
puts "Type a number:"
n = gets.to_f
puts "#{n} is#{ n%13==0 ? "" : " NOT" } divisible by 13"

 

Dla ciekawych świata:
Więcej informacji na temat wyrażeń warunkowych
 (między innymi wyrażenia unless oraz case, dla których zabrakło miejsca w samouczku ;))