Literały
Słowo literał w specyfikacjach Go jest odmieniane przez wszystkie przypadki, dlatego warto się z nim zaprzyjaźnić.
Literał to nic innego jak "wartość zapisana w kodzie programu".
Niektórych nawet doświadczonych programistów może dziwić czemu właśnie w tych dokumentach jest tak często używane. Dzieje się tak, ponieważ w Go nie ma klas, konstruktorów i innych cudów OOP, a wpisywanie literałów zamiast tworzenia wartości przez wbudowaną funkcję new() (lub make()) jest często wygodniejsze i dużo czytelniejsze.
Ci którzy mieli do czynienia z JavaScriptem (lub Pythonem) powinni być
przyzwyczajeni do inicjalizacji tablic przez [], lub tworzenia obiektów (a w
Pythonie dictów) za pomocą {}.
W Go praktyka tworzenia wartości za pomocą literałów odnosi się do wszystkich typów, prócz kanałów (chan).
Poniżej przegląd literałów z podziałem na "rodziny"
Literały numeryczne
Mamy trzy rodzajów literałów numerycznych
Liczby całkowite, możemy zapisać w podstawie dziesiętnej, ósemkowej i szesnastkowej
42 //42 zapis o podstawie dziesiętnej 052 //42 w podstawie ósemkowej (hex) 0x2A //42 w podstawie szesnastkowej
Liczby zmiennoprzecinkowe (float32 lub float64) zapisujemy w jednym z
poniższych formatów
10. //obowiązkowa kropka po części dziesiętnej, // bez kropki to literał liczby całkowitej 0.1 //z lewej strony kropki część całkowita po prawej część dziesiętna .1 //to co wyżej można pisać bez części całkowitej 1e-1 //możemy też używać postaci z wykładnikiem (1e-1 == 0.1 == .1)
Liczby zespolone, różnią się od zmiennoprzecinkowych tym że dodana jest do nich część urojona. Część urojona składa się z cyfr oraz symbolu i. Przykłady:
1i //tylko część urojona 1 10+10i //liczba zespolona z częścią rzeczywistą 10 i częścią urojoną 10 .1+.1i //liczba zespolona z częścią rzeczywistą 0.1 i częścią urojoną 0.1 1e-1-1i
Literały znakowe
Warto uważnie przeczytać jak deklarujemy literały znakowe, bo kilka razy
mocno się naciąłem na różnice między grawisem (`) a pojedynczym (')
i podwójnym (") cudzysłowem :-)
W pojedynczym cudzysłowie zapisujemy literały znakowe.
'a'
Taki literał jest reprezentowany przez liczbę całkowitą typu int, a nie przez
byte (jakby mogło się czasem zdawać). Dzieje się tak ponieważ kod źródłowy Go
jest domyślnie przetwarzany jakby był w kodowaniu UTF-8. Jak wiemy, w UTF-8
możemy zapisywać znaki wielobajtowe np takie jak: 'ą','ć','ę','ł' etc.
Między apostrofami możemy definiować tylko jeden znak, choć może być reprezentowany przez wiele bajtów.
Gdyby jednak standardowy UTF-8 to było za mało, lub nie możemy polegać na kodowaniu pliku źródłowego możemy uciec się do alternatywnych sposobów zapisu takich jak:
'\141' //'a' w ósemkowo (maksymalny octal to 377, czyli 255 dziesiętnie) '\x61' //'a' w szesnastkowo (tylko dwa hexy, więc 'ą' nie zapiszemy w ten sposób) '\u0105' //'ą' jako mały unicode (cztery hexy) '\U00000105' //'ą' jako duży unicode (osiem hexów)
Mamy do dyspozycji także pulę znaków specjalnych takich jak:
\a U+0007 alarm lub dzwonek \b U+0008 backspace \f U+000C form feed \n U+000A line feed or newline \r U+000D carriage return \t U+0009 horizontal tab \v U+000b vertical tab \\ U+005c backslash \' U+0027 single quote (możliwy tylko jako literał znaku) \" U+0022 double quote (możliwy tylko jako część łańcucha znaków)
Podwójny cudzysłów posłuży nam do zapisywania łańcuchów znaków. Reguły
dotyczące poszczególnych form zapisów pojedynczych znaków nadal są w mocy,
(czyli formy \u0123 \u00001234 i \x22 z powodzeniem mogą być używane w
łańcuchach) nielegalne jest użycie nieescapowanych ", ani znaku nowej linii
(których escapowanie pokazano w zestawieniu powyżej).
"ala ma kota" /// to poniżej się nie kompiluje "ala ma kota" //zwróci syntax error
Za to legalnym jest umieszczanie znaku nowej linii w literałach łańcuchów znakowych zawartych między gravisami `, jednak nie zapiszemy żadnego znaku z użyciem backslasha. ` sygnalizuje rozpoczęcie surowego (nie poddawanego obróbce) ciągu znaków
`ala ma kotki dwa!\n`
Powyższy string reprezentuje każdy wyraz w osobnej linii jednak nie kończy się pustym wierszem!
Literały złożone
Nazywamy tak wszystkie wszystkie literały, które reprezentują w programie
wartość złożoną to jest: wszelkiego rodzaju mapy, structy, arraye,
slicesy (tylko chan i interface nie mają reprezentacji literału - zresztą
nie miało by to sensu).
Co do zasady literały typów złożonych mają postać
typ {"klucz1": wartość, "klucz2": wartość2}
przy czym klucze są opcjonalne dla wszystkich typów poza mapami. W zależności od typu klucze mają inne znaczenie: dla struktur to pola, dla tablic indeksy, dla map klucze. Należy pamiętać, że albo decydujemy się pominąć klucze, albo stosować je w całej definicji literału.
Literały mapowań
Literały map są wyjątkowo mało ciekawe, mają tylko jedną prawidłową postać:
type strToInt map[string]int stival := strToInt{"jeden": 1, "dwa": 2,"trzy":3} stival2 := map[string]int{"a": 0, "b": 1}//z sygnaturą typu
Nota bene warto zauważyć, że typ literału złożonego może być zadeklarowany, a można go też wstawić "z palca".
Literały tablicowe
Przykładowe literały tablicowe wyglądają tak:
type intArr [2]int arr1 := intArr {0: 1, 1: 2} arr2 := [2]int{0: 1, 1: 2}
Warto pamiętać o tym, że Go domyślnie inicjuje wartości na zero typu, dzięki temu możemy zrobić tak:
arr3 := [100]int{50:1}
czyli zadeklarować tylko jedną wartość w 100 elementowej tablicy, reszta będzie zerami. Jak pisałem wyżej nie potrzebujemy kluczy w literałach tablicowych, możemy je pominąć i wpisywać tylko wartości, które będą indeksowane od 0
arr4 := [5]int{1,2,3,4,5}
Czasami nużące jest wpisywanie za każdym razem ilości elementów w deklaracji
typu, twórcy zadbali także o to - literał tablicowy zamiast ilości elementów
może zawierać ... a podczas kompilacji ilość elementów zostanie policzona.
arr5 := [...]int{1,2,3,4,5}
Literały wycinków
Literał slice różni się od tablic brakiem deklaracji ilości elementów, tym i w zasadzie tylko tym. Zatem dozwolone są wszystkie poniższe formy:
type intSli []int sli := intSli{0: 1, 1: 2, 3: 4, 4: 5, 5: 6, 6: 3} sli1 := []int{0: 1, 1: 2, 3: 4, 4: 5, 5: 6, 6: 3} sli2 := []int{1, 2, 4, 5, 6, 3}
Literały struktur
Ten rodzaj literałów występuje najczęściej w kodzie Go, bo tworzenie instancji structów za pomocą literałów jest najzwyczajniej w świecie wygodne.
type point struct{ x int y int } p1 := point{x: 1, y: 2}
Etykietami w tym przypadku są nazwy pola structa. Możemy pominąć nazwy pól, z tym że w takim przypadku należy pamiętać by wartości wpisywać zgodnie z kolejnością definicji typu structa, czyli:
type namedPoint struct { name string x int y int } np := namedPoint{"nazwa", 1, 1}
A tak swoją drogą: powyższa definicja typu wydaje się być nieco pokraczna, szczególnie gdy typ point mamy zadeklarowany przykład wcześniej. Możemy, w tym przypadku, skorzystać z osadzania structów:
type point struct { x int y int } type namedPoint { name string point } np := namedPoint{"nazwa", point{1,1}}
O osadzaniu więcej jest w innym artykule tu o tylko wspominam, by pokazać jak definiuje się literały structów z anonimowo osadzonym structem.
Literały funkcyjne
Także funkcje mają swoje literały, te do złudzenia przypominają definicje
funkcji. Tak naprawdę to jedyna różnica między literałem a definicją funkcji
jest taka, że literał to anonimowa funkcja, więc nie ma nazwy między słowem
kluczowym func a sygnaturą, ani nie ma możliwości stania się metodą typu.
Literał funkcyjny możemy przekazać do zmiennej np. tak:
f := func(a, b int ) int { return a + b; } f(2,2)//returns 4
Można go także wywołać w miejscu:
func(a, b int ) int { return a + b; }(2,2)//returns 4
Najważniejszą cechą literałów funkcyjnych jest to że tworzą domknięcie (clousure) tak często i z powodzeniem wykorzystywane przez m.in. programistów JavaScript.
Wszystko o języku programowania Go