Blog (30)
Komentarze (5.6k)
Recenzje (0)

Scala — pierwsze kroki cz.4

@mikolaj_sScala — pierwsze kroki cz.422.09.2014 00:15

Dzięki opanowaniu materiału z poprzednich części kursu. Jesteśmy już blisko, aby móc wykorzystać zdobyte już umiejętności do konkretnego zastosowania w skryptach. Do tego potrzebujemy jeszcze kilku elementów całej układanki. W tym w szczególności umiejętności czytania i zapisywania do plików. Po przeczytaniu tej części powinniśmy być już w stanie to zrobić. Pisanie programów w IDE kompilowanych do bytecodu wymagać będzie znajomości klas i obiektów, co przedstawię w następnej ostatniej już części.

Krok ósmy - operatory

Język C++ pozwala na przeładowywanie operatorów, umożliwiając pisanie klas, w których operacje na nich mogą być wykonywane naturalnie za pomocą tychże operatorów. W Javie natomiast nie można zdefiniować operatorów dlatego zastępuje się operatory metodami o nazwach takich jak add, mutliple itp. W Scali natomiast wszystko zostało zupełnie przeprojektowane. Nie istnieje podział na metody i operatory. Polega to na tym, że metody mogą mieć prawie dowolne nazwy, w tym mogą to być różne znaki uznawane za operatory. Natomiast metody, które mają tylko jeden argument można wywoływać bez użycia kropki i nawiasów:

[code=Scala]1 + 2[/code]

Możemy równie dobrze napisać jako:

[code=Scala]1.+(2)[/code]

Aby zrozumieć przykład prześledźmy w jaki sposób można wywoływać metody.

Definiujemy liczbę typu Long (litera L na końcu)

[code=Scala]val liczba = 0L liczba: Long = 0[/code]

Teraz możemy zamienić ją na String, wywołując wbudowaną metodę toString

[code=Scala]liczba.toString() res3: java.lang.String = 0[/code]

Równie dobrze możemy tę samą metodę wywołać bez nawiasów. Dotyczy to wszystkich metod bez parametrów:

[code=Scala]liczba.toString[/code]

Więcej, możemy wywołać tę samą metodę na literałach. Kompilator gdy zorientuje się jakiego są typu wywoła metodę z odpowiedniego obiektu:

[code=Scala]12345.toString res4: java.lang.String = 12345 "34564".toInt res5: Int = 3456[/code]

W przypadku obiektów i metod możemy również zrezygnować z użycia nawiasów i kropki, oddzielając nazwę metody od parametru spacją.

Więc 1 + 2 to wywołanie metody + na obiekcie Int reprezentowanej przez jej obiekt o wartości 1. czyli 1.+(2)

Tego efektu nie można stosować w stosunku do funkcji, musimy użyć nawiasów. Funkcje natomiast mogą zawierać wiele różnych znaków w nazwie w tym cyfry np:

[code=Scala]def !#(a:Int) = a*10 $bang$hash: (a: Int)Int !#(30) res8: Int = 300[/code]

Zdefiniowano tutaj i użyto funkcji o nazwie !#.

Nazwy funkcji i metod mają jednak pewne ograniczenia. Nie mogą zaczynać się od cyfry i niektórych znaków jak np. #, $, &. Oczywiście nie mogą być nazwami kluczowymi, w tym pojedynczymi znakami używanymi przez składnie. Przykładowo funkcja nie może się nazywać : ani :nazwa ponieważ dwukropek wykorzystuje się przy określaniu typu zmiennej. Ale już użycie nazwy :# lub :: jest dopuszczalne ponieważ te znaki nie występują nigdy razem w składni. Często dodaje się na końcu metod znaku zapytania ? jednak musimy poprzedzić go znakiem podkreślnika:

[code=Scala]def parzysta_?(i:Int) = i % 2 == 0 parzysta_$qmark: (i: Int)Boolean[/code]

[code=Scala]parzysta_?(567) res13: Boolean = false[/code]

W przypadku metod umożliwiają one tworzenie bardziej intuicyjnych bibliotek i DSLi. Więcej na temat nazw metod dowiemy się przy okazji omawiania definicji klas i obiektów.

Krok dziewiąty - rozpoznawanie stylu funkcyjnego

Jak wiemy Scala nie jest językiem wymuszającym pisanie w stylu funkcyjnym. Ułatwia to przenoszenie kodu z innych języków jak np. Javy. Pomaga też początkującym programistom odnaleźć się szybko w składni Scali i nie tracić zbytnio produktywności rozgryzając nieustannie jak napisać dany kod funkcyjnie. Ma to również swoje minusy i może nie podobać się purystom językowym. Jednak Scala powstała z czysto praktycznych powodów i nie stara się utrudnić życia programiście w imię jakiś wyższych ideałów.

Jednym z elementów stylu funkcyjnego jest unikanie iterowania po dodatkowej zmiennej np.:

[code=Scala]def drukujArg(args: Array[String]):Unit = { var i = 0 while (i < args.length) { println(args(i)) i += 1 } }[/code]

Możemy zastąpić:

[code=Scala]def drukujArg(args: Array[String]):Unit = { for (arg <- args.length) println(args(i)) } [/code]

Lub jeszcze zwięźlej:

[code=Scala]def drukujArg(args: Array[String]):Unit = { args.foreach(println) } [/code]

W tym ostatnim używamy wbudowanej w kolekcje metody foreach i uproszczonej składni anonimowej funkcji. (możemy ją rozpisać jako; (arg) => println(arg) )

Ostatnia wersja jest najkrótsza i możemy ją przetłumaczyć w prosty sposób jako: każdy argument zmiennej args drukuj w nowej linii. Taki sposób pisania na dłuższą metę jest opłacalny, ponieważ skraca długość kodu i pomaga wyłapywać błędy jakie powstają przy bardziej rozwlekłym kodzie. Nie wymaga również stosowania pośrednich zmiennych.

Powyższe funkcje mają jednak pewien minus, generują uboczny efekt w postaci wydruku danych na konsolę. Nie jest to jeszcze idealnie funkcyjny styl. Lepiej byłoby zrobić następująco:

[code=Scala]def formatujArg(args: Array[String]) = args.mkString("\n")

println(formatujArg(args)) [/code]

Metoda formatujArg jest w pełni funkcyjna tzn.: nie używa zmiennych mutowalnych i nie ma efektów ubocznych. Tworzy nowy obiekt w formie Stringu, który można użyć np. do wydrukowania na ekran. Oryginalna tablica nie ulega zmianie i jest bezpieczna. (Domyślnie każdy parametr metody lub funkcji jest typu val, jeśli nie chcemy aby tak było dopisujemy na początku var.)

Powyższa metoda ułatwia również testowanie kodu, ponieważ można łatwo sprawdzić, czy logika działania jest prawidłowa:

[code=Scala]val doDruku = formatujArg(Array("jeden", "dwa", "trzy")) assert(doDruku == "jeden\ndwa\ntrzy"[/code]

Przykład oczywiście jest banalny, ale ukazuje ideę, że kod pisany w stylu funkcyjnym jest łatwiejszy do testowania.

Warto pamiętać, że sam wygląd kodu polegający na łańcuchowym przetwarzaniu danych przez wbudowane funkcje nie jest jeszcze prawdziwym programowaniem funkcyjnym. Przykładowo podobny kod do funkcyjnego pisze się używając jQuery. Jednak mimo podobieństwa trudno nazwać go stylem funkcyjnym, ponieważ operuje się na zmiennych mutowalnych. Głównym wyróżnikiem stylu funkcyjnego jest właśnie używanie niemutowalnych danych. Gdy chcemy zmienić te dane to tworzymy i zwracamy nowy obiekt nie zmieniając starego. Obiekty powinny być zabezpieczone przez zmianą. Dlatego w stylu funkcyjnym używamy typu zmiennych val.

Krok dziesiąty - czytanie danych z pliku

Aby pisać proste skrypty najczęściej niezbędna jest praca na plikach oraz możliwość odczytu i zapisu danych. Do czytania danych z pliku używamy obiektu scala.io.Source

[code=Scala]import scala.io.Source

if(args.length > 0) { for(line <- Source.fromFile(args(0)).getLines) print(line.length + " " + line) } else Console.err.println("Błędna nazwa pliku")[/code]

Po zapisaniu kodu w pliku i uruchomieniu z argumentem będącym ścieżką do pliku:

scala nazwa_programu.scala sciezka_do_pliku

uzyskujemy wydruk zawartości pliku z ilością danych w każdej linii.

Source potrafi również czytać z sieci (po podaniu URL), strumieni oraz tablic z danymi (Array[Byte])

np:.

[code=Scala]import scala.io.Source

if(args.length > 0) { for(line <- Source.fromURL(args(0)).getLines) print(line.length + " " + line) } else Console.err.println("Brak URL")[/code]

Podajemy w tym przypadku pełny URL (z http).

Metoda fromFile zwraca Iterator[String] dający dostęp do danych. Aby zamienić go na kolekcję wykonujemy metodę:

[code=Scala]val linie = Source.fromFile("plik").getLines.toList[/code]

Czytanie z konsoli

Do czytania używamy metody readLine("Pytanie"):

[code=Scala]import scala.io.Source val http = readLine("Podaj HTTP: ") for(line <- Source.fromURL(http).getLines) print(line.length + " " + line)[/code]

Zapis do pliku

W standardowych bibliotekach Scali nie ma osobnego sposobu zapisu danych do pliku. Korzysta się z klas Javy. W przypadku pliku tekstowego możemy wykonać zapis następująco:

[code=Scala]val wyjscie= new java.io.FileWriter("plik.txt") wyjscie.write("Witaj pliczku!") wyjscie.close[/code]

Jak już wspomniałem jest to przedostatnia już część kursu. Z góry zapraszam na ostatnią, w której przedstawię aspekty programowania obiektowego w Scali.

Szanowna Użytkowniczko! Szanowny Użytkowniku!
×
Aby dalej móc dostarczać coraz lepsze materiały redakcyjne i udostępniać coraz lepsze usługi, potrzebujemy zgody na dopasowanie treści marketingowych do Twojego zachowania. Twoje dane są u nas bezpieczne, a zgodę możesz wycofać w każdej chwili na podstronie polityka prywatności.

Kliknij "PRZECHODZĘ DO SERWISU" lub na symbol "X" w górnym rogu tej planszy, jeżeli zgadzasz się na przetwarzanie przez Wirtualną Polskę i naszych Zaufanych Partnerów Twoich danych osobowych, zbieranych w ramach korzystania przez Ciebie z usług, portali i serwisów internetowych Wirtualnej Polski (w tym danych zapisywanych w plikach cookies) w celach marketingowych realizowanych na zlecenie naszych Zaufanych Partnerów. Jeśli nie zgadzasz się na przetwarzanie Twoich danych osobowych skorzystaj z ustawień w polityce prywatności. Zgoda jest dobrowolna i możesz ją w dowolnym momencie wycofać zmieniając ustawienia w polityce prywatności (w której znajdziesz odpowiedzi na wszystkie pytania związane z przetwarzaniem Twoich danych osobowych).

Od 25 maja 2018 roku obowiązuje Rozporządzenie Parlamentu Europejskiego i Rady (UE) 2016/679 (określane jako "RODO"). W związku z tym chcielibyśmy poinformować o przetwarzaniu Twoich danych oraz zasadach, na jakich odbywa się to po dniu 25 maja 2018 roku.

Kto będzie administratorem Twoich danych?

Administratorami Twoich danych będzie Wirtualna Polska Media Spółka Akcyjna z siedzibą w Warszawie, oraz pozostałe spółki z grupy Wirtualna Polska, jak również nasi Zaufani Partnerzy, z którymi stale współpracujemy. Szczegółowe informacje dotyczące administratorów znajdują się w polityce prywatności.

O jakich danych mówimy?

Chodzi o dane osobowe, które są zbierane w ramach korzystania przez Ciebie z naszych usług, portali i serwisów internetowych udostępnianych przez Wirtualną Polskę, w tym zapisywanych w plikach cookies, które są instalowane na naszych stronach przez Wirtualną Polskę oraz naszych Zaufanych Partnerów.

Dlaczego chcemy przetwarzać Twoje dane?

Przetwarzamy je dostarczać coraz lepsze materiały redakcyjne, dopasować ich tematykę do Twoich zainteresowań, tworzyć portale i serwisy internetowe, z których będziesz korzystać z przyjemnością, zapewniać większe bezpieczeństwo usług, udoskonalać nasze usługi i maksymalnie dopasować je do Twoich zainteresowań, pokazywać reklamy dopasowane do Twoich potrzeb. Szczegółowe informacje dotyczące celów przetwarzania Twoich danych znajdują się w polityce prywatności.

Komu możemy przekazać dane?

Twoje dane możemy przekazywać podmiotom przetwarzającym je na nasze zlecenie oraz podmiotom uprawnionym do uzyskania danych na podstawie obowiązującego prawa – oczywiście tylko, gdy wystąpią z żądaniem w oparciu o stosowną podstawę prawną.

Jakie masz prawa w stosunku do Twoich danych?

Masz prawo żądania dostępu, sprostowania, usunięcia lub ograniczenia przetwarzania danych. Możesz wycofać zgodę na przetwarzanie, zgłosić sprzeciw oraz skorzystać z innych praw wymienionych szczegółowo w polityce prywatności.

Jakie są podstawy prawne przetwarzania Twoich danych?

Podstawą prawną przetwarzania Twoich danych w celu świadczenia usług jest niezbędność do wykonania umów o ich świadczenie (tymi umowami są zazwyczaj regulaminy). Podstawą prawną przetwarzania danych w celu pomiarów statystycznych i marketingu własnego administratorów jest tzw. uzasadniony interes administratora. Przetwarzanie Twoich danych w celach marketingowych realizowanych przez Wirtualną Polskę na zlecenie Zaufanych Partnerów i bezpośrednio przez Zaufanych Partnerów będzie odbywać się na podstawie Twojej dobrowolnej zgody.