Zadania z podstaw języka ruby i programowania OO

Poniższe zadania mają na celu samodzielne przećwiczenie różnych zagadnień omawianych na zajęciach.

pokaż / ukryj wszystkie odpowiedzi

  1. Proszę napisać program, który będzie prosił użytkownika o podanie dwóch liczb, po czym zwróci w wyniku ich sumę, np:
    >Proszę podać pierwszą liczbę:
    >5
    >Proszę podać drugą liczbę:
    >3
    >Wynik: 8
    

    pokaż / ukryj odpowiedź

    To proste zadanie ma na celu przypomnienie, w jaki sposób wczytujemy dane ze standardowego wejścia oraz jak konwertujemy napisy do liczb. Przykładowe rozwiązanie pokazuje, jak korzystamy z interpolowanych stringów do wypisania wyniku:
    puts "Podaj pierwsza liczbe:"
    num1=gets.to_i
    puts "Podaj druga liczbe:"
    num2=gets.to_i
    puts "Wynik: #{num1+num2}"
    
  2. Proszę zmodyfikować powyższy program w taki sposób, aby po zwróceniu wyniku ponownie prosił użytkownika o podanie kolejnych liczb.

    pokaż / ukryj odpowiedź

    W tym rozwiązaniu korzystamy z pętli while do ciągłego czytania ze standardowego wyjścia. Można rozwiązać to zadanie również stosując rekurencyjne wywołania funkcji.
    num1=num2=nil
    puts "Podaj pierwsza liczbe:"
    while num=gets.to_i
      if num1.nil?
        num1=num
        num2=nil
        puts "Podaj druga liczbe:"
      elsif num2.nil?
        num2=num
        puts "Wynik: #{num1+num2}"
        num1=nil
        puts "Podaj pierwsza liczbe:"
      end
    end
    
  3. Proszę napisać program obliczający wartość ułamka i wypisujący ją z dokładnością do n liczb po przecinku. Program pobiera od użytkownika licznik, mianownik i liczbę n.
    • Wskazówka: printf

    pokaż / ukryj odpowiedź

    Formatujemy wynik korzystając z funkcji printf, która działa podobnie jak w języku C. Przy okazji warto zwrócić uwagę na to, że któryś z argumentów dzielenia musi być liczba zmiennoprzecinkową.
    puts "Podaj licznik:"
    licznik=gets.to_f
    puts "Podaj mianownik:"
    mianownik=gets.to_f
    puts "Podaj ilosc miejsc po przecinku:"
    n=gets.to_i
    wynik = licznik/mianownik
    printf("Wynik: %.#{n}f",wynik)
    
  4. Proszę napisać program wczytujący wprowadzoną z klawiatury serię liczb zakończoną -1 i wypisujący n największych.
    • Wskazówka: metoda sort tablicy. Dodatkowo proszę rozważyć wariant, w którym przewidujemy wczytywanie wielu liczb i nie chcemy ich wszystkich przechowywać w pamięci, jedynie w każdym momencie n największych

    pokaż / ukryj odpowiedź

    Pierwsza wersja zakłada, że zapamiętujemy wszystkie liczby aż do zakończenia wpisywania i dopiero wtedy je sortujemy i prezentujemy n największych. Do przechowywania liczb używamy tablicy, a do jej sortowania używamy metody sort.
    nums = []
    n=5
    min=0
    while (num=gets.to_i) != -1
      nums<< num 
    end
    nums.sort!{|a,b| b <=> a}
    puts nums[0..n-1]
    
    W nieco trudniejszym wariancie, gdy nie chcemy przechowywać wszystkich liczb a jedynie w każdym momencie n największych, korzystamy z funkcji min dla zwrócenia najmniejszej wartości przechowywanej w tablicy, funkcji index do odnalezienia pozycji danej wartości oraz ponownie funkcji sort. Działanie programu jest obrazowane przez wypisywanie aktualnej zawartości tablicy, co można wygodnie zrobić stosując funkcję inspect (jest to funkcja, która umożliwia czytelne wypisanie zawartości dowolnego obiektu, stosuje się ją na ogół przy debugowaniu kodu). Dodatkowo w tym wariancie nie dopuszczamy umieszczania duplikatów w tablicy.
    nums = []
    n=5
    min=0
    while (num=gets.to_i) != -1
      next if num < min || nums.index(num)
      if nums.size > n
        puts "replacing #{min} with #{num}"
        nums[nums.index(min)]=num
      else
        nums<< num
      end
      min = nums.min
      puts nums.inspect
    end
    nums.sort!{|a,b| b <=> a}
    puts nums
    
  5. Proszę napisać program wczytujący z pliku serię liczb i wypisujący średnią arytmetyczną z tych liczb.

    pokaż / ukryj odpowiedź

    sum=0.0
    count=0
    while (num=gets.to_i) != -1
      sum+=num
      count+=1
    end
    puts "Srednia: #{sum/count}"
    
  6. Proszę napisać program, który wczyta plik tekstowy (jego nazwa może być podana jako parametr wywołania lub zapisana w pliku) po czym wypisze na standardowe wyjście listę wyrazów i częstości ich wystąpień w podanym tekście, posortowaną malejąco pod względem częstości, np:
    "Ho! Ho! Ho! Merry Christmas!"
    
    Ho : 3
    Christmas : 1
    Merry : 1
    
    (dla ułatwienia w pierwszej wersji można pominąć specjalne traktowanie interpunkcji)

    pokaż / ukryj odpowiedź

    W tym zadaniu skorzystamy z tablicy haszującej do przechowywania statystyk wystąpień słów. Dzięki podaniu wartości 0 w konstruktorze każdy nowy wpis w haszu ma wartość inicjalizowaną na 0, co jest wygodne przy inkrementowaniu ilości wystapień słowa. Korzystamy z iteratora each wczytując kolejne linie pliku. Za pomocą predefiniowaniej klasy znaków \W rozdzielamy linie tekstu po wszystkich znakach, które nie występują w słowach. Na końcu dokonujemy sortowania tablicy słów, uzyskanej w wyniku pobrania wszystkich kluczy tablicy haszującej, gdzie kryterium sortowania stanowi wartość tablicy haszującej dla danego słowa.
    stats = Hash.new(0)
    File.open("martin_fowler_ruby.txt").each do |line|
      words = line.split(/\W+/)
      words.each do |word|
        stats[word]+=1    
      end
    end
    
    words = stats.keys
    ordered_words = words.sort{|w1,w2| stats[w2] <=> stats[w1]}
    ordered_words.each{|w| puts "#{w}: #{stats[w]}"}
    
  7. Proszę napisać klasę Square, o jednym atrybucie o nazwie "a" podawanym w parametrze konstruktora i oznaczającym długość boku kwadratu. Niech klasa implementuje metodę "area" zwracającą pole kwadratu. Np:
    s1 = Square.new(5)
    s1.area > 25
    

    pokaż / ukryj odpowiedź

    class Square
      def initialize(a)
        @a= a
      end
      
      def area
        @a**2
      end
    end
    
    s = Square.new(5)
    puts s.area
    
  8. Proszę napisać klasę Guitar o jednej metodzie o nazwie "play", zwracającej napis "plonk plonk", oraz klasę Drum, która również ma metodę "play", zwracającą napis "bang bang".
    g = Guitar.new
    g.play > "plonk plonk"
    d = Drum.new
    d.play > "bang bang"
    

    pokaż / ukryj odpowiedź

    Mozna zaimplementować te klasy na różne sposoby, przykładowo;
    class Guitar
      def play
        "plonk plonk" 
      end
    end
    
    class Drum
      def play
        "bang bang"
      end
    end
    
    g = Guitar.new
    puts "guitar: "+g.play
    d = Drum.new
    puts "drums: "+d.play
    
  9. Proszę do powyższych dwóch klas dodać nadklasę o nazwie Instrument, która implementuje metodę "play" w taki sposób, że możliwe jest usunięcie z Guitar i Drum definicji tej metody przy utrzymaniu dawnego zachowania.
    • Wskazówka: proszę postarać się, aby rozbudowa orkiestry o kolejne instrumenty nie wymagała zmiany w klasie Instrument.

    pokaż / ukryj odpowiedź

    Ponieważ nie chcemy obciążać klasy nadrzędnej wiedzą o dźwiękach wydawanych przez poszczególne instrumenty, musimy zaproponować sposób przechowywania informacji o dźwięku w klasach dziedziczących. W tym rozwiązaniu skorzystamy ze stałych definiowanych w poszczególnych klasach od osiągnięcia tego celu (warto sprawdzić, dlaczego nie nadaje sie do tego celu zmienna klasowa).
    class Instrument
      SOUND="undefined sound"
      def play
        self.class::SOUND 
      end
    end
    
    class Guitar < Instrument
      SOUND="plonk plonk" 
    end
    
    class Drum < Instrument
      SOUND="bang bang"
    end
    
    g = Guitar.new
    puts "guitar: "+g.play
    d = Drum.new
    puts "drums: "+d.play
    

    Alternatywne rozwiązanie autorstwa Olka Pohla

    class Instrument
      def self.get_sound
        @sound
      end
      def play
        self.class.get_sound
      end
    end
    
    class Guitar < Instrument
      @sound = "plonk plonk"
    end
    
    class Drum < Instrument
      @sound = "bang bang"
    end
    
  10. Proszę napisać klasy Bicycle, Car i Vehicle w taki sposób, aby unikając duplikacji kodu można było uzyskać taki efekt:
    b = Bicycle.new(6)
    b.info > "I am a Bicycle, I have 2 wheels and 6 gears"
    c = Car.new(5,4)
    c.info > "I am a Car, I have 4 wheels and 5 gears and room for 4 passengers"
    
    • Wskazówka: w pierwszej wersji pojawiła się pomyłka w treści zadania, większość samochodów wciąż ma 5 biegów i tyle również ma mieć obiekt c, zgodnie z parametrem podanym w konstruktorze.

    pokaż / ukryj odpowiedź

    Podobnie jak w poprzednim przypadku, nie chcemy aby klasa Vehicle była obciążona szczegółową wiedzą o poszczególnych podklasach. Chcemy do niej wyciągnąć tylko części wspólne roweru i samochodu. W realiach tego zadania można uznać, że to co łączy rowery i samochody to posiadanie określonej ilości kół i biegów. Niech więc klasa Vehicle przyjmuje te parametry w konstruktorze. Klasy dziedziczące będą mogły skorzystać wtedy z konstruktora nadklasy, za pomocą metody super. Również za pomocą metody super samochód będzie mógł wykorzystać część wspólną komunikatu wyświetlanego po wywołaniu info i dokleić do niego specyficzna dla siebie informację o ilości pasażerów.
    class Vehicle
      def initialize(gears,wheels)
        @gears=gears
        @wheels=wheels    
      end
      def info
        "I am a #{self.class}, I have #{@wheels} wheels and #{@gears} gears"
      end
    end
    
    
    class Bicycle < Vehicle
      def initialize(gears)
        super(gears,2)
      end
    end
    
    class Car < Vehicle
      def initialize(gears,passengers)
        super(gears,4)
        @passengers=passengers
      end
      
      def info
        super+" and room for #{@passengers} people"
      end
    end
    
      
    b = Bicycle.new(6)
    puts b.info
    c = Car.new(5,4)
    puts c.info