[PL] Jak wypisać string w Ruby?

05 Oct 2016

Zadanie pochodzi z konkursu Cody autorstwa CodestHQ. Jeśli chcecie zagrać - nie psujcie sobie zabawy czytaniem ;)

Problem jest prosty - trzeba wypisać na następujący tekst:

Ruby was released in 1995!

Większość z nas zrobi to w najprostszy możliwy sposób:

p "Ruby was released in 1995!"

Ale teraz pojawia się utrudnienie - kod źródłowy musi spełniać następujące wyrażenie regularne: [a-zA-Z.\n ]. Czyli innymi słowy może składać się tylko z małych i wielkich liter, kropki, znaku nowej linii i spacji.

Pierwsze pytanie - jak możemy wypisać każdy znak na ekran bez jego używania? Odpowiedź jest dosyć oczywista - każda litera ma sobie odpowiadający kod ascii.

Zatem jeżeli dodamy do siebie odpowiednio wiele liczb, a następnie zamienimy to na znak (metodą .chr) - dostaniemy pożądaną literkę!

Zatem nasze rozwiązanie wyglądałoby następująco:

print 82.ord
print 117.ord
print 98.ord
print 121.ord
print 32.ord
print 119.ord
print 97.ord
print 115.ord
print 32.ord
print 114.ord
print 101.ord
print 108.ord
print 101.ord
print 97.ord
print 115.ord
print 101.ord
print 100.ord
print 32.ord
print 105.ord
print 110.ord
print 32.ord
print 49.ord
print 57.ord
print 57.ord
print 53.ord
print 33.ord

Teraz co prawda pozbyliśmy się cudzosłowów i znaku wykrzynika - jednak wciąż używamy liczb. Skąd zatem je wziąć, skoro nie wolno ich nam użyć?

Inspiracją i pewną podpowiedzią może być konstrukcja liczb naturalnych von Neumanna. Czyli:

\[0 = \emptyset\] \[n+1 = n \cup \{n \}\]

Dla pierwszych paru liczb:

\[1 = \{ \emptyset \}\] \[2 = \{ \emptyset, \{ \emptyset \} \}\] \[3 = \{ \emptyset, \{ \emptyset \}, \{ \emptyset, \{ \emptyset \} \} \}\]

Tłumacząc to na język programowania, potrzebować będziemy pustej tablicy (nasz “zbiór pusty”), a potem jakiejś metody na dodawanie jej do samej siebie (krok indukcyjny).

Użyjmy zatem konstrukcji liczb naturalnych Von Neumanna w Ruby, do uzyskania potrzebnych nam liczb naturalnych przy pomocy samych tablic (i trzech prostych metod). Zaczniemy od zdefiniowania dwóch metod, które są odpowiednikiem kroku bazowego i kroku indukcyjnego z definicji.

def b num
  num.push num.dup
end

def c
  Array.new
end

(metoda .dup w metodzie b ma znaczenie czysto kosmetyczne. Bez niej zamiast ładnych zbiorów pustych tablic, dostaniemy tablice rekurencyjne, które nie będą odpowiadać liczbom naturalnym, a do tego wyświetlają się mniej ładnie. Do tego utrudniają zauważenie analogii z konstrukcją Von Neumanna. Natomiast liczba elementów się zgadza, stąd zadanie byłoby poprawne także wtedy)

I wtedy, odpowiednio:

c #=> []
_.size #=> 0
b c #=> [[]]
_.size #=> 1
b b c #=> [[], [[]]]
_.size #=> 2
b b b c #=> [[], [[]], [[], [[]]]]
_.size #=> 3

Skonstruowaliśmy właśnie liczby naturalne, używając konstrukcji Von Neumanna w Ruby, przy pomocy tablicy i dwóch prostych metod. Całkiem nieźle, prawda?

Teraz brakuje nam tylko ostatniego klocka tej układanki - metody, która przyjmie całą daną tablicę, sprawdzi ile ona ma elementów, a następnie zwróci odpowiedni znak z tablicy ascii.

def a arg
  arg.size.chr
end

Żeby na przykład uzyskać ! musimy wykonać naszą konstrukcję 33 razy, a następnie dodać nasze zero na końcu, zamianę na znak i wypisanie na początku.

print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c #=> !

Intuicyjnie - c to zero, a każde kolejne b jest odpowiednikiem dodania do niego jedynki. Proste jak drut ;)

Finalne rozwiązanie wypisujące na ekran Ruby was released in 1995! wygląda następująco: (linie są bardzo długie, bo nie wolno przecież użyć nam symbolu \, które pozwoliłby złamać)

def a arg
  arg.size.chr
end

def b num
  num.push num.dup
end

def c
  Array.new
end

print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c 
print a b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c

A efekt:

 #=> "Ruby was released in 1995!"

(rozwiązanie wygrało nagrodę jako jedno z pięciu najciekawszych w konkursie Cody)

(tabelę ascii podwędziłem z wikipedii)