2.2. Znaki końca wiersza

Znak średnika służy do oznaczania końca instrukcji. W niektórych sytuacjach, zależnie od kontekstu, koniec instrukcji może być też oznaczany przez znak końca wiersza. W wielu przypadkach pozwala to na ominięcie znaków średnika w kodzie programu. Jednak z drugiej strony może to prowadzić do sytuacji, kiedy Scala uznaje instrukcję za zakończoną w sytuacji, kiedy nie było to zamiarem programisty. Poniższe przykłady ilustrują różne zagadnienia związane z traktowaniem znaków nowego wiersza.

W pliku PlusMinus1.scala znajduje się definicja metody next, która zwraca wartość swojego argumentu powiększoną lub pomniejszoną o 1 w zależności od tego, czy argument jest, czy nie jest liczbą ujemną.

Plik PlusMinus1.scala:
def next(a: Int) = {
  if (a<0) a - 1 else a + 1
}

Użyte jest w nim wyrażenie warunkowe rozpoczynające się od słowa kluczowego if, po którym w nawiasach okrągłych znajduje się wyrażenie logiczne. Jeśli wartość tego wyrażenia jest prawdziwa, to wynikiem całego wyrażenia warunkowego jest wartość wyrażenia a - 1, znajdującego się po zamykającym nawiasie okrągłym, kończącym wyrażenie logiczne. Jeśli wartość wyrażenia logicznego nie jest prawdziwa, to wynikiem całego wyrażenia warunkowego jest wartość wyrażenia a + 1, znajdującego się po słowie kluczowym else.

scala> :load PlusMinus1.scala
Loading PlusMinus1.scala...
next: (a: Int)Int

scala> next(-2)
res0: Int = -3

scala> next(2)
res1: Int = 3

Wyrażenie warunkowe jest opisane w punkcie 4.1.

W pliku PlusMinus2.scala, w definicji metody next, wprowadzony jest dodatkowy znak nowego wiersza.

Plik PlusMinus2.scala:
def next(a: Int) = {
  if (a<0) a - 1 else a 
                      + 1 
}

Spójrzmy jakie skutki wywołuje wprowadzona zmiana.

scala> :load PlusMinus2.scala
Loading PlusMinus2.scala...
next: (a: Int)Int

scala> next(-2)
res2: Int = 1

scala> next(2)
res3: Int = 1

Kompilator uznał zawartość wierszy i za poprawne składniowo, ale oddzielne wyrażenia, a nie za jedno wyrażenie warunkowe, zapisane w dwóch oddzielnych wierszach. Ponieważ wyrażenie + 1 jest ostatnim wyrażeniem w bloku kodu definiującym treść metody, jego wartość staje się wynikiem wywołania metody.

W definicji metody next z pliku PlusMinus3.scala, znak nowego wiersza znajduje się przed słowem kluczowym else.

Plik PlusMinus3.scala:
def next(a: Int) = {
  if (a<0) a - 1
  else a + 1
}

Tym razem wyniki wywołania metody są takie, jak w przypadku metody next z pliku PlusMinus1.scala.

scala> :load PlusMinus3.scala
Loading PlusMinus3.scala...
next: (a: Int)Int

scala> next(-2)
res4: Int = -3

scala> next(2)
res5: Int = 3

Wiersz, który zaczyna się od słowa kluczowego else, nie może zostać uznany za poprawnie rozpoczynającą się instrukcję języka Scala. Wobec tego kompilator uznaje ten wiersz za ciąg dalszy wyrażenia warunkowego z poprzedniego wiersza.

W pliku PlusMinus4.scala znak nowego wiersza jest umieszczony po znaku +.

Plik PlusMinus4.scala:
def next(a: Int) = {
  if (a<0) a - 1 else a +
                      1
}

Wyniki znowu odpowiadają rezultatom wywołania metody z pliku PlusMinus1.scala.

scala> :load PlusMinus4.scala
Loading PlusMinus4.scala...
next: (a: Int)Int

scala> next(-2)
res6: Int = -3

scala> next(2)
res7: Int = 3

Pozostawienie znaku + na końcu wiersza powoduje, że kompilator nie uznaje wyrażenia za prawidłowo zakończone. Początek kolejnego wiersza uznany zostaje zatem za kontynuację wyrażenia.

W pliku PlusMinus5.scala wyrażenie a + 1 jest ujęte w nawiasy okrągłe.

Plik PlusMinus5.scala:
def next(a: Int) = {
  if (a<0) a - 1 else (a
                       + 1)
}

Wyniki ponownie są takie, jak wcześniej.

scala> :load PlusMinus5.scala
Loading PlusMinus5.scala...
next: (a: Int)Int

scala> next(-2)
res8: Int = -3

scala> next(2)
res9: Int = 3

Wyrażenie a + 1, mimo że rozbite na dwa wiersze, jest traktowane jako jedno, gdyż znajduje się w nawiasach okrągłych. Znak nowego wiersza wewnątrz wyrażenia w nawiasach okrągłych nie jest traktowany jako separator instrukcji.

W pliku PlusMinus6.scala zamiast nawiasów okrągłych użyte zostały nawiasy klamrowe.

Plik PlusMinus6.scala:
def next(a: Int) = {
  if (a<0) a - 1 else {a
                       + 1}
}

Tym razem wyniki wywołania metody różnią się od wcześniejszych, a przy okazji kompilator pokazuje ostrzeżenie.

scala> :load PlusMinus6.scala
Loading PlusMinus6.scala...
<console>:11: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
         if (a<0) a - 1 else {a
                              ^
next: (a: Int)Int

scala> next(-2)
res10: Int = -3

scala> next(2)
res11: Int = 1

Po słowie kluczowym else znajduje się blok kodu zawierający w nawiasach klamrowych dwa wyrażenia. Wyrażenia a oraz + 1 są traktowane jako dwa oddzielne, prawidłowo zbudowane wyrażenia języka Scala. Wobec tego, że wyrażenie + 1 jest ostatnim wyrażeniem bloku kodu z klauzuli else, jego wynik jest zwracany jako wynik całej metody, w przypadku gdy argumentem metody jest liczba nieujemna. Wartość pierwszego wyrażenia z bloku kodu (czyli wyrażenia a) jest w tym przypadku ignorowana (i tego zasadniczo dotyczy ostrzeżenie kompilatora).

Powyższe przykłady zawierają sytuacje, kiedy kompilator nie generuje komunikatu o błędzie, mimo że intencje programisty są być może interpretowane opacznie. Od popełnienia podobnych błędów mogą nas ustrzec odpowiednia czujność lub testy.

Choć w wielu sytuacjach możemy opuścić znaki średnika, to jeśli chcemy umieścić kilka instrukcji w tym samym wierszu, należy je tymi znakami oddzielić, jak w poniższym przykładzie.

scala> val a = 1; val b = 2; val c = 3
a: Int = 1
b: Int = 2
c: Int = 3

Specyfikacja języka Scala opisuje reguły dotyczące interpretacji znaków końca wiersza w punkcie 1.2.

Język programowania Scala Wydanie 2. Copyright © Grzegorz Balcerek 2016

Licencja Creative Commons

Ten utwór jest dostępny na licencji Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowe.

Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.