niedziela, 5 lutego 2012

Deklaracje i operatory

W tym artykule przyjrzę się niektórym podstawowym cechom języka Scala w odniesieniu do innych znanych mi języków. Będzie tu mowa o deklaracjach i operatorach.

Deklarowanie zmiennych i wartości

Deklaracja zmiennej wyrażana jest za pomocą słowa kluczowego var, nazwy zmiennej, opcjonalnego typu i obowiązkowej wartości początkowej.

var x = 1.0        // typ zmiennej 'Double' wywiedziony
                   // z wartości początkowej
var x1: Any = 1.0  // jawna deklaracja typu czasem jest niezbędna
Podobnie ma się sprawa z wartością (ang. value), jednak są dwie różnice: pierwsza to oczywiście inne słowo kluczowe, druga to brak możliwości zmiany wartości po zainicjowaniu. Jest to odpowiednik javowej zmiennej oznaczonej słowem kluczowym final lub const w C++.
val greet = "Hello"       // typ wartości 'String' wywiedziony
                          // z wartości początkowej
val greet: String = null  // to samo co powyżej, ale z jawną deklaracją typu,
                          // który nie wynika z przypisanej wartości

Podobnie jak Java, Scala posiada siedem podstawowych typów numerycznych: Byte, Char, Short, Int, Long, Float, Double i Boolean. Jednak odwrotnie niż w Javie (czy C++) typy te są klasami. Z tego powodu zupełnie legalnie można wywoływać metody powyższych klas, jak w przykładzie poniżej:

1.toString() // zwraca łańcuch "1"
1.to(10)     // zwraca zakres typu Range(1,2,3,4,5,6,7,8,9,10) 

Uwaga na ENTER

Średniki na końcu deklaracji nie są obowiązkowe, chyba że w tej samej linii miałoby się znaleźć ich więcej. Zalecane jest, aby kończyć deklarację znakiem końca linii. Znak ten (dalej będę używał skrótu nl) jest w Scali traktowany jako specjalny jeśli są spełnione następujące warunki:

  1. Ciąg znaków bezpośrednio poprzedzający nl może zakończyć deklarację, czyli dowolny literał, identyfikator lub jedno z następujących słów kluczowych lub znaków: this null true false return type <xml-start> _ ) ] }
  2. Ciąg znaków bezpośrednio następujący po nl może rozpocząć deklarację, czyli dowolny ciąg znaków poza następującymi: catch else extends finally forSome match with yield , . ; : = => <- <: <% >: # [ ) ] }
  3. Znak nl znajduje się w regionie gdzie jest to dozwolone, czyli wszędzie poza obszarami:
    • pomiędzy ( i ) oraz [ i ], chyba że wewnątrz zagnieżdżono obszar w którym nl jest dozwolony.
    • pomiędzy case i odpowiadającym mu symbolem =>, chyba że wewnątrz zagnieżdżono obszar dozwolony dla nl
    • analizowanymi w trybie XML

Nie zawsze jednak znak nowej linii oznacza koniec deklaracji. Ma to miejsce w następujących przykładach, zaczerpniętych z draftu specyfikacji języka Scala, w przypadku znaków nl pomiędzy liniami 1-2, 4-5, 7-8 oraz 10-11:

if (x > 0)
  x = x - 1

while (x > 0)
  x = x / 2

for (x <- 1 to 10)
  println(x)

type
  IntList = List[Int]

Natomiast należy bardzo uważać na nadmiarowe znaki nowego wiersza, ponieważ w niektórych przypadkach mogą one całkowicie zmienić sposób działania kodu. Poniższy przykład definiuje anonimową klasę:

new Iterator[Int]
{
  private var x = 0
  def hasNext = true
  def next = { x += 1; x }
}
Podczas, gdy dodanie dodatkowego wiersza:
new Iterator[Int]

{
  private var x = 0
  def hasNext = true
  def next = { x += 1; x }
}
powoduje zinterpretowanie powyższego kodu, jako utworzenie obiektu z następującym po nim lokalnym blokiem.

Kolejny przykład opisuje pojedyncze wyrażenie:

x < 0 ||
x > 10
Podczas, gdy dodatkowy wiersz tworzy z powyższego kodu dwa wyrażenia:
x < 0 ||

x > 10

Operatory

Większość operatorów znanych w Javie czy C++ występuje również w Scali. Jednak nie ma tu operatorów ++ oraz --. Zamiast tego należy używać x+=1 i x-=1.
Ponieważ istnieje możliwość przeciążania operatorów (nieobecna w Jave, ale możliwa w C++), można sobie uzupełnić braki. Twórcy języka uznali jednak, że nie ma potrzeby tworzenia dodatkowej reguły parsera po to tylko, by zaoszczędzić programistom jednego uderzenia klawisza.

Operatory w Scali są właściwie metodami, to znaczy poniższe wywołanie:

 1 + 2 
można zastąpić:
 1.+(2)
Scala pozwala na definiowanie nazw metod składających się nie tylko ze znaków alfanumerycznych. Dlatego np. klasa BigInt zawiera metodę (operator) /%, która zwraca parę liczb oznaczających wyniku dzielenia całkowitego oraz resztę z tego dzielenia.

Generalnie można zapisywać:

a metoda b
jako skrót zapisu:
a.metoda(b)
Dzięki temu każda metoda może pełnić rolę operatora, a wspomniany już wyżej przykład:
1.to(10)
można zapisać nieco ładniej:
1 to 10
uzyskując ten sam efekt.

W kolejnym artykule zajmę się deklaracjami funkcji i metod, ich wywoływaniem oraz magią metody apply.

Brak komentarzy: