Scala — pierwsze kroki cz.2
Poniższy wpis jest kontynuacją części pierwszej kursu.
Krok trzeci - definiowanie funkcji
Do definiowania funkcji lub metody służy słowo kluczowe def. Napiszemy funkcję max zwracającą większą wartość z dwóch wartości przekazanych jako parametry:
def max(x:Int, y:Int):Int = { if(x > y) x else y }
max jest nazwą funkcji przyjmującej dwa parametry typu całkowitego - Int i zwracającą również typ Int. Jak widać pominięto znane z innych języków słowo kluczowe return. Jest ono opcjonalne w tym przypadku i moglibyśmy je dopisać przed x i y. Zwracana jest zawsze ostatnia wartość z miejsca do którego doszła funkcja.
Nie potrzeba też średników po każdej instrukcji. Kompilator sam znajduje gdzie się ona kończy. Charakterystyczne też jest odwrócenie kolejności nazwy argumentu z jego typem, jak również pojawienie się zwracanego typu po liście parametrów funkcji. (Może niektórzy już zorientowali się, że przypomina to sposób zapisu z języka UML.). Nawiasy klamrowe tak jak w większości języków pochodzących od C definiują nam zasięg. W tym przypadku ciało funkcji. Znak równości w pierwszej linii mówi, że funkcja zwraca wartość. Jest ona obowiązkowa dla funkcji. Jeśli jej nie napiszemy to oznacza to procedurę i brak zwracania wartości (oznacza to zwracanie Unit - coś podobnego do void w C).
Funkcję możemy wywołać np. w taki sposób:
max(3, 5)
Na co interpreter odpowie w znany nam już sposób:
res0: Int = 5
Dla małych funkcji i metod możemy skrócić zapis do jednej linijki. Nasza funkcja max może być zapisana w następujący sposób:
def max(x:Int, y:Int) = if(x > y) x else y
Podobnie jak przy definicji z inicjalizacją zmiennych funkcja potrafi sama odgadnąć zwracany typ. Gdy napiszemy procedurę kompilator przypisze jej zwracany typ jako Unit:
def powitanie() = println("Witaj świecie!") powitanie: ()Unit
Krok czwarty - piszemy skrypty
Mimo, że Scala powstała do pisania dużych programów i systemów to umożliwiono pisanie w niej skryptów. Stwórzmy plik o nazwie hello.scala i napiszmy w nim następującą treść:
println("Witaj świecie z wnętrza skryptu!")
Skrypt uruchamiamy poleceniem:
scala hello.scala
Uruchomienie skryptu trwa kilka sekund ze względu na czas kompilacji. Skrypty w Scali wydają się być miłym dodatkiem i mogą okazać się czasami użyteczne. Jednak nie zastąpią wielu przypadkach ani skryptów Pythona, ani też nie zastąpią większych programów pisanych w Scali. Pobieranie parametrów z konsoli po uruchomieniu skryptu odbywa się za pomocą listy argumentów args z numerem argumentu wpisanym w okrągłe nawiasy. Aby to sprawdzić podmieniamy zawartość skryptu na:
println("Witaj " + args(0) + "!")
Przy okazji widzimy, że napisy skleja się podobnie do C i Java. Teraz uruchamiamy poleceniem:
scala hello.scala Janie
Jeśli zapomnimy wpisać argumentu to pojawi się błąd i rzucony zostanie wyjątek, ponieważ próbujemy dostać się do pustej listy argumentów. Zamieńmy kod na poniższy:
if(!args.isEmpty) println("Witaj " + args(0) + "!") else println("Witaj Anonimie!")
Dzięki sprawdzeniu czy lista nie jest pusta unikamy problemów.
W systemach Uniksowych możemy uruchamiać skrypty podając tylko ich nazwę, ale dopisując w pierwszej linii za pomocą czego ma on być uruchomiony. W Ubuntu po instalacji Scali z pakietów będzie to:
#!/usr/bin/scala !#
Po nadaniu praw do uruchomienia plikowi możemy uruchomić skrypt poleceniem:
./hello.scala
Krok piąty - pętla while i zwrotnica if
Wygląd pętli while jest dość standardowy. Napiszmy funkcję liczącą silnie z wykorzystaniem pętli while:
def silnia(n:Int) = { var i = 1 var s = 1 while( i < n) { i += 1 s *= i } s } silnia(5)
Nie jest to kod pokazujący typowy styl programowania w Scali ponieważ, nie unikamy tutaj iterowania po indeksie i co jest zbędne. W kodzie znajdziemy też znane z języka C i Javy skróty operacji: i += 1 to i = i + 1 oraz s *= i to s = s * i.
Umieśćmy teraz w pliku drukujargumenty.scala następujący kod:
var i = 0 while (i < args.length) { if(i != 0) print(" ") print(args(i)) i += 1 } println()
i uruchamiamy poleceniem:
scala drukujargumenty.scala Scala jest fajna
Instrukcja if sprawdza warunek logiczny i podobnie jak w Javie nie przepuści innej operacji nie zwracającej prawdy lub fałszu. Instrukcje w bloku if muszą być ograniczone nawiasami klamrowymi gdy jest ich więcej. Funkcja print drukuje napis na konsolę bez dodawania znaku nowej linii, a metoda length listy args sprawdza ilość elementów w liście (w tym przypadku ilość argumentów w konsoli). Jeśli chcemy napisać kilka poleceń w jednej linii to możemy oddzielić je średnikami.
Krok szósty - pętla for i foreach
Realizacja funkcji silni za pomocą pętli for:
def silnia(n:Int) = { var s = 1 for(i <- 1 to n) { s *= i } s } silnia(5)
Jak widać nie jest to typowa dla języków C i Javy funkcja for. Bardziej podobna jest do pętli z Pythona. Generuje liczby od 1 do n ( 1 to n) i podstawia pod zmienną i. Drukowanie argumentów z konsoli w pliku drukujargumenty.scala można skrócić do:
for(arg <- args) print(arg + " ") println()
Foreach jest metodą którą posiadają kolekcje (sekwencje, listy, tablice itp.). Umożliwia ona pisanie kodu w zwarty sposób. Naszą funkcję silnia możemy napisać dzięki temu krócej:
def silnia(n:Int) = { var s = 1 (1 to n).foreach(i => s *= i) s } silnia(5)
We wnętrzu metody foreach użyta została funkcja anonimowa. Jest to skrócona forma, która mogłaby wyglądać tak:
(i:Int) => {s *= i}
Do pełnej funkcji brakuje słowa kluczowego def, nazwy funkcji i zamiast znaku równości mamy =>.
W pliku drukujargumenty.scala możemy podmienić kod na:
args.foreach(arg => print(arg + " ")) println()
Minusem jest fakt, że na końcu zostanie spacja. Można to rozwiązać jeszcze prościej używając metody mkString:
print(args.mkString(" "))
Użycie metody foreach pokazuje styl programowania funkcyjnego.
Do zobaczenia w następnym odcinku ;)