Golangi õpetus: õppige Go programmeerimiskeelt algajatele

Mis on Go?

Go (tuntud ka kui Golang) on ​​avatud lähtekoodiga programmeerimiskeel, mille on välja töötanud Google. See on staatiliselt tipitud kompileeritud keel. Go toetab samaaegset programmeerimist, st võimaldab käivitada mitut protsessi samaaegselt. See saavutatakse kanalite, gorutiinide jms abil. Go Languageil on prügikogumine, mis ise haldab mälu ja võimaldab funktsioonide edasilükkamist.

Selles Learn Go Language õpetuses õpime kõiki Golangi põhitõdesid.

Kuidas GO alla laadida ja installida

Step 1) Minna https://golang.org/dl/. Laadige oma OS-i jaoks alla binaarfail.

Step 2) Double klõpsake installiprogrammil ja klõpsake nuppu Käivita.

Step 3) Klõpsake nuppu Edasi

Step 4) Valige installikaust ja klõpsake nuppu Edasi.

Step 5) Kui installimine on lõppenud, klõpsake nuppu Lõpeta.

Step 6) Kui installimine on lõppenud, saate seda kontrollida, avades terminali ja tippides

go version

See kuvab installitud go versiooni

Sinu esimene Go programm – Go Hello World!

Looge kaust nimega studyGo. Selles Go keele õpetuses loome sellesse kausta oma go programmid. Go failid luuakse laiendiga .mine. Saate käivitada Go programme süntaksi abil

go run <filename>

Looge fail nimega first.go ja lisage sellele allolev kood ning salvestage

package main
import ("fmt")

func main() {
	fmt.Println("Hello World! This is my first Go program\n")
}

Liikuge oma terminalis sellesse kausta. Käivitage programm käsuga

mine enne jooksma.mine

Näete väljatrükki

Hello World! This is my first Go program

Nüüd arutame ülaltoodud programmi.

paketi põhi – iga Go Language programm peaks algama paketi nimega. Go võimaldab meil kasutada pakette teises go programmides ja toetab seega koodi korduvkasutatavust. Go programmi täitmine algab paketi sees oleva koodiga nimega main.

import fmt – impordib paketi fmt. See pakett rakendab I/O funktsioone.

func main() – see on funktsioon, millest algab programmi täitmine. Põhifunktsioon tuleks alati paigutada põhipakendisse. Main() all saate koodi kirjutada sisse { }.

fmt.Println – see prindib ekraanil oleva teksti fmt funktsiooni Println abil.

Märkus. Selle Go õpetuse allolevates jaotistes, kui mainite koodi käivitamist/käitamist, tähendab see koodi salvestamist faili laiendiga .go ja selle käivitamist süntaksi abil.

    go run <filename>

Andmetüübid

Tüübid (andmetüübid) tähistavad muutujas salvestatud väärtuse tüüpi, funktsiooni tagastatava väärtuse tüüpi jne.

Go Languages ​​on kolm põhitüüpi

Numbrilised tüübid – Esitage arvväärtusi, mis sisaldavad täisarve, ujukoma ja kompleksväärtusi. Erinevad numbritüübid on:

int8 – 8-bitised märgiga täisarvud.

int16 – 16-bitised märgiga täisarvud.

int32 – 32-bitised märgiga täisarvud.

int64 – 64-bitised märgiga täisarvud.

uint8 – 8-bitised märgita täisarvud.

uint16 – 16-bitised märgita täisarvud.

uint32 – 32-bitised märgita täisarvud.

uint64 – 64-bitised märgita täisarvud.

float32 – 32-bitised ujukomaarvud.

float64 – 64-bitised ujukomaarvud.

kompleks64 – sisaldab float32 reaalset ja imaginaarset osa.

kompleks128 – sisaldab float32 reaalset ja imaginaarset osa.

Stringi tüübid – esindab baitide (märkide) jada. Stringidega saate teha mitmesuguseid toiminguid, nagu stringide ühendamine, alamstringi eraldamine jne

Boole'i ​​tüübid – tähistab kahte väärtust, kas tõene või väär.

Golangi liides

Golangi liides on meetodisignatuuride kogum, mida tüüp kasutab objektide käitumise rakendamiseks. Golangi liidese peamine eesmärk on pakkuda meetodisignatuure koos nimede, argumentide ja tagastustüüpidega. Meetodi deklareerimine ja rakendamine on tüübi ülesandeks. Golangi liidese saab deklareerida märksõna "liides" abil.

Muutujad

Muutujad osutavad mälukohale, mis salvestab mingisuguse väärtuse. Tüübiparameeter (allolevas süntaksis) tähistab väärtuse tüüpi, mida saab mälukohta salvestada.

Muutuja saab deklareerida süntaksi abil

    var <variable_name> <type>

Kui olete teatud tüüpi muutuja deklareerinud, saate muutuja määrata selle tüübi mis tahes väärtusele.

Saate anda muutujale algväärtuse ka deklaratsiooni enda kasutades

    var <variable_name> <type> = <value>

Kui deklareerite muutuja algväärtusega, järeldage määratud väärtuse tüübi põhjal muutuja tüüp. Seega saate süntaksi abil deklareerimise ajal tüübi välja jätta

    var <variable_name> = <value>

Samuti saate süntaksiga deklareerida mitu muutujat

    var <variable_name1>, <variable_name2>  = <value1>, <value2>

Selle Go õpetuse allolevas programmis on mõned Golangi näited muutujate deklaratsioonidest

 
package main
import "fmt"

func main() {
    //declaring a integer variable x
    var x int
    x=3 //assigning x the value 3 
    fmt.Println("x:", x) //prints 3
    
    //declaring a integer variable y with value 20 in a single statement and prints it
    var y int=20
    fmt.Println("y:", y)
    
    //declaring a variable z with value 50 and prints it
    //Here type int is not explicitly mentioned 
    var z=50
    fmt.Println("z:", z)
    
    //Multiple variables are assigned in single line- i with an integer and j with a string
    var i, j = 100,"hello"
    fmt.Println("i and j:", i,j)
}

Väljund saab olema

x: 3
y: 20
z: 50
i and j: 100 hello

Go Language pakub ka lihtsat viisi muutujate väärtusega deklareerimiseks, jättes välja märksõna var use

    <variable_name> := <value>

Pange tähele, et kasutasite := asemel =. Te ei saa kasutada := lihtsalt väärtuse määramiseks juba deklareeritud muutujale. := kasutatakse väärtuse deklareerimiseks ja määramiseks.

Looge fail nimega assign.go järgmise koodiga

package main
import ("fmt")

func main() {
	a := 20
	fmt.Println(a)

	//gives error since a is already declared
	a := 30
	fmt.Println(a)
}

Käivitage mine käivitage assign.go, et näha tulemust kui

./assign.go:7:4: no new variables on left side of :=		

Ilma algväärtuseta deklareeritud muutujatel on numbritüüpide puhul 0, Boole'i ​​puhul false ja stringide puhul tühi string

Konstandid

Püsimuutujad on need muutujad, mille väärtust ei saa pärast määramist muuta. Go programmeerimiskeele konstant deklareeritakse märksõna "const" abil

Looge fail nimega konstant.go ja järgmise koodiga

package main
import ("fmt")

func main() {
	const b =10
	fmt.Println(b)
	b = 30
	fmt.Println(b)
}

Käivitage mine käivita konstant.go, et näha tulemust kui

.constant.go:7:4: cannot assign to b

Silmusnäidete jaoks

Tsükleid kasutatakse lausete ploki korduvaks täitmiseks tingimuse alusel. Enamik programmeerimiskeeli pakuvad kolme tüüpi silmuseid – for, while, do while. Kuid Go programmeerimiskeel toetab ainult tsüklit.

Golangi tsükli süntaks on

for initialisation_expression; evaluation_expression; iteration_expression{
   // one or more statement
}

Initsialiseerimise_avaldis käivitatakse esimesena (ja ainult üks kord) Golang for tsüklis.

Seejärel hinnatakse hindamisavaldist ja kui see on tõsi, siis käivitatakse ploki sees olev kood.

Käitatakse iteratsiooni_avaldise ID ja hindamisavaldist hinnatakse uuesti. Kui see on tõsi, käivitatakse lauseplokk uuesti. See jätkub, kuni hindamisavaldis muutub vääraks.

Kopeerige allolev programm faili ja käivitage see, et näha Golangi tsükli trükkimiseks numbrid vahemikus 1 kuni 5

package main
import "fmt"

func main() {  
var i int
for i = 1; i <= 5; i++ {
fmt.Println(i)
    }
}

Väljund on

1
2
3
4
5

Kui veel

Kui muidu on tingimuslause. Sünaks on

if condition{
// statements_1
}else{
// statements_2
}

Siin hinnatakse tingimust ja kui see on tõene, täidetakse laused_1, muidu käivitatakse laused_2.

Võite kasutada if-lauset ka ilma muuta. Samuti võite olla aheldatud kui else avaldused. Allpool olevad programmid selgitavad rohkem, kui veel.

Käivitage allolev programm. See kontrollib, kas arv x on väiksem kui 10. Kui jah, siis prindib "x on väiksem kui 10".

package main
import "fmt"

func main() {  
    var x = 50
    if x < 10 {
        //Executes if x < 10
        fmt.Println("x is less than 10")
    } 
}

Kuna x väärtus on suurem kui 10, siis ploki tingimuse sees olevat avaldust ei täideta.

Nüüd vaadake allolevat programmi. Selles Go programmeerimiskeele õpetuses on meil muu plokk, mis käivitatakse, kui hindamine ebaõnnestub.

package main
import "fmt"

func main() {  
    var x = 50
    if x < 10 {
        //Executes if x is less than 10
        fmt.Println("x is less than 10")
    } else {
        //Executes if x >= 10
        fmt.Println("x is greater than or equals 10")
    }
}

See programm annab teile väljundi

x is greater than or equals 10

Nüüd selles Go õpetuses näeme programmi, millel on mitu if else plokki (aheldatud kui else). Käivitage allolev Go näide. See kontrollib, kas arv on väiksem kui 10 või vahemikus 10–90 või suurem kui 90.

package main
import "fmt"

func main() {  
    var x = 100
    if x < 10 {
        //Executes if x is less than 10
        fmt.Println("x is less than 10")
    } else if x >= 10 && x <= 90 {
        //Executes if x >= 10 and x<=90
        fmt.Println("x is between 10 and 90")
    } else {
        //Executes if both above cases fail i.e x>90
        fmt.Println("x is greater than 90")
    }
}

Siin kontrollib kõigepealt tingimus, kas x on väiksem kui 10 ja see pole nii. Seega kontrollib see järgmist tingimust (muidu kui), kas see on vahemikus 10 kuni 90, mis on samuti vale. Seejärel käivitab see ploki muu jaotises, mis annab väljundi

x is greater than 90

Lüliti

Switch on veel üks tingimuslause. Lülituslaused hindavad avaldist ja tulemust võrreldakse saadaolevate väärtuste (juhtumite) komplektiga. Kui vaste on leitud, täidetakse selle vastega (juhtumiga) seotud avaldused. Kui vastet ei leita, ei teostata midagi. Lülitumiseks saate lisada ka vaikejuhtumi, mis käivitatakse juhul, kui teisi vasteid ei leita. Lüliti süntaks on

switch expression {
    case value_1:
        statements_1
    case value_2:
        statements_2
    case value_n:
        statements_n
    default:
        statements_default
    }

Siin võrreldakse avaldise väärtust iga juhtumi väärtustega. Kui vaste on leitud, täidetakse selle juhtumiga seotud avaldused. Kui vastet ei leita, käivitatakse vaikejaotise all olevad avaldused.

Käivitage allolev programm

package main
import "fmt"

func main() {  
    a,b := 2,1
    switch a+b {
    case 1:
        fmt.Println("Sum is 1")
    case 2:
        fmt.Println("Sum is 2")
    case 3:
        fmt.Println("Sum is 3")
    default:
        fmt.Println("Printing default")
    }
}

Saate väljundi kujul

Sum is 3		

Muutke a ja b väärtused 3-ks ja tulemus on selline

Printing default

Ühel juhul võib olla ka mitu väärtust, eraldades need komaga.

Massiraadid

Massiiv tähistab sama tüüpi elementide fikseeritud suurusega, nimega jada. Teil ei saa olla massiivi, mis sisaldab nii täisarvu kui ka märke. Massiivi suurust ei saa muuta, kui olete suuruse määranud.

Massiivi deklareerimise süntaks on

var arrayname [size] type

Igale massiivi elemendile saab süntaksi abil määrata väärtuse

arrayname [index] = value

Massiiviindeks algab alates 0 suurusele-1.

Deklareerimise ajal saate määrata massiivi elementidele väärtusi süntaksi abil

arrayname := [size] type {value_0,value_1,…,value_size-1} 

Samuti saate massiivi väärtustega deklareerimisel ignoreerida suuruse parameetrit, asendades suuruse väärtusega ... ja kompilaator leiab pikkuse väärtuste arvust. Süntaks on

arrayname :=  […] type {value_0,value_1,…,value_size-1}

Massiivi pikkuse leiate süntaksi abil

len(arrayname)

Massiivi mõistmiseks käivitage allolev Go näide

package main
import "fmt"

func main() {  
    var numbers [3] string //Declaring a string array of size 3 and adding elements 
    numbers[0] = "One"
    numbers[1] = "Two"
    numbers[2] = "Three"
    fmt.Println(numbers[1]) //prints Two
    fmt.Println(len(numbers)) //prints 3
    fmt.Println(numbers) // prints [One Two Three]

    directions := [...] int {1,2,3,4,5} // creating an integer array and the size of the array is defined by the number of elements 
    fmt.Println(directions) //prints [1 2 3 4 5]
    fmt.Println(len(directions)) //prints 5

    //Executing the below commented statement prints invalid array index 5 (out of bounds for 5-element array)
    //fmt.Println(directions[5]) 
}

Väljund

Two
3
[One Two Three]
[1 2 3 4 5]
5

Golangi viilutamise ja lisamise funktsioon

Lõik on massiivi osa või segment. Või on see vaade või osaline vaade aluseks olevast massiivist, millele see osutab. Lõigu elementidele pääsete juurde, kasutades lõigu nime ja indeksi numbrit samamoodi nagu massiivi puhul. Massiivi pikkust muuta ei saa, aga lõigu suurust saab muuta.

Lõigu sisu on tegelikult viidad massiivi elementidele. See tähendab kui muudate lõigul mõnda elementi, mõjutab see ka aluseks olevat massiivi sisu.

Lõigu loomise süntaks on

var slice_name [] type = array_name[start:end]

See loob massiivist nimega massiivi_nimi viilu nimega viilu_nimi, mille indeksi elemendid on algusest lõpuni-1.

Nüüd selles Golangi õpetuses käivitame alloleva programmi. Programm loob massiivist lõigu ja prindib selle. Samuti näete, et lõigu sisu muutmine muudab tegelikku massiivi.

package main
import "fmt"

func main() {  
    // declaring array
    a := [5] string {"one", "two", "three", "four", "five"}
    fmt.Println("Array after creation:",a)

    var b [] string = a[1:4] //created a slice named b
    fmt.Println("Slice after creation:",b)

    b[0]="changed" // changed the slice data
    fmt.Println("Slice after modifying:",b)
    fmt.Println("Array after slice modification:",a)
}

Tulemus prinditakse kujul

Array after creation: [one two three four five]
Slice after creation: [two three four]
Slice after modifying: [changed three four]
Array after slice modification: [one changed three four five]

On teatud funktsioone, nagu Golang len, Golang append, mida saate viiludele rakendada

len(lõigu_nimi) – tagastab lõigu pikkuse

append(lõigu_nimi, väärtus_1, väärtus_2) – Golangi lisamist kasutatakse väärtuse_1 ja väärtus_2 lisamiseks olemasolevale lõigule.

append(slice_nale1,slice_name2…) – lisab lõigu_nimi2 jaotisele slice_name1

Käivitage järgmine programm.

package main
import "fmt"

func main() {  
	a := [5] string {"1","2","3","4","5"}
	slice_a := a[1:3]
	b := [5] string {"one","two","three","four","five"}
	slice_b := b[1:3]

    fmt.Println("Slice_a:", slice_a)
    fmt.Println("Slice_b:", slice_b)
    fmt.Println("Length of slice_a:", len(slice_a))
    fmt.Println("Length of slice_b:", len(slice_b))

    slice_a = append(slice_a,slice_b...) // appending slice
    fmt.Println("New Slice_a after appending slice_b :", slice_a)
    
    slice_a = append(slice_a,"text1") // appending value
    fmt.Println("New Slice_a after appending text1 :", slice_a)
}

Väljund saab olema

Slice_a: [2 3]
Slice_b: [two three]
Length of slice_a: 2
Length of slice_b: 2
New Slice_a after appending slice_b : [2 3 two three]
New Slice_a after appending text1 : [2 3 two three text1]

Programm loob esmalt 2 viilu ja prindib selle pikkuse. Seejärel lisas see ühe lõigu teisele ja seejärel lõi saadud lõigule stringi.

Funktsioonid

Funktsioon esindab avalduste plokki, mis täidab konkreetset ülesannet. Funktsiooni deklaratsioon ütleb meile funktsiooni nime, tagastustüübi ja sisendparameetrid. Funktsiooni definitsioon tähistab funktsioonis sisalduvat koodi. Funktsiooni deklareerimise süntaks on

func function_name(parameter_1 type, parameter_n type) return_type {
//statements
}

Parameetrid ja tagastustüübid on valikulised. Samuti saate funktsioonist tagastada mitu väärtust.

Nüüd selles Golangi õpetuses käivitame järgmise Golangi näite. Siin aktsepteerib funktsioon nimega calc 2 numbrit ja teeb liitmise ja lahutamise ning tagastab mõlemad väärtused.

package main
import "fmt"

//calc is the function name which accepts two integers num1 and num2
//(int, int) says that the function returns two values, both of integer type.
func calc(num1 int, num2 int)(int, int) {  
    sum := num1 + num2
    diff := num1 - num2
    return sum, diff
}

func main() {  
    x,y := 15,10

    //calls the function calc with x and y an d gets sum, diff as output
    sum, diff := calc(x,y) 
    fmt.Println("Sum",sum)
    fmt.Println("Diff",diff) 
}

Väljund saab olema

Sum 25
Diff 5

Paketid

Koodi korrastamiseks kasutatakse pakette. Suure projekti puhul ei ole võimalik koodi kirjutada ühte faili. Go programmeerimiskeel võimaldab meil korraldada koodi erinevate pakettide alla. See suurendab koodi loetavust ja korduvkasutatavust. Käivitatav Go programm peaks sisaldama paketti nimega main ja programmi täitmine algab funktsioonist nimega main. Teisi meie programmi pakette saate importida süntaksi abil

import package_name

Selles Golangi õpetuses näeme ja arutame, kuidas järgmises Golangi näites pakette luua ja kasutada.

Step 1) Looge fail nimega package_example.go ja lisage allolev kood

package main
import "fmt"
//the package to be created
import "calculation"

func main() {  
	x,y := 15,10
	//the package will have function Do_add()
sum := calculation.Do_add(x,y)
fmt.Println("Sum",sum) 
}

Ülaltoodud programmis on fmt pakett, mille Go programmeerimiskeel pakub meile peamiselt I/O eesmärkidel. Samuti näete paketti nimega arvutus. Main() sees näete sammusummat := arvutus. Do_add(x,y). See tähendab, et käivitate paketi arvutamisel funktsiooni Do_add.

Step 2) Esiteks peaksite looma paketi arvutuse sama nimega kaustas src kausta all. Paigaldatud käigutee leiate muutujast PATH.

Maci puhul leidke tee, käivitades echo $PATH

Nii et tee on /usr/local/go

Windowsi puhul leidke tee, käivitades echo %GOROOT%

Siin on tee C:\Go\

Step 3) Liikuge kausta src (/usr/local/go/src Maci jaoks ja C:\Go\src Windowsi jaoks). Nüüd koodist on paketi nimi arvutus. Go nõuab, et pakett tuleks paigutada src kataloogi all olevasse samanimelisse kataloogi. Looge src kaustas kataloog nimega arvutus.

Step 4) Looge arvutuskataloogi fail nimega calc.go (võite anda mis tahes nime, kuid koodis olev paketi nimi on oluline. Siin peaks see olema arvutus) ja lisage allolev kood

package calculation
  
func Do_add(num1 int, num2 int)(int) {
    sum := num1 + num2
    return sum
}

Step 5) Käivitage arvutuskataloogist käsk go install, mis kompileerib faili calc.go.

Step 6) Nüüd minge tagasi saidile package_example.go ja käivitage käsk, käivitage package_example.go. Väljund on summa 25.

Pange tähele, et funktsiooni Do_add nimi algab suure tähega. Selle põhjuseks on asjaolu, et kui funktsiooni nimi algab Go-s suure tähega, tähendab see seda, et teised programmid näevad seda (juurdepääsevad), muidu ei pääse teised programmid sellele juurde. Kui funktsiooni nimi oleks do_add, oleks viga saanud

ei saa viidata eksportimata nimede arvutamisele.arvutus..

Edasilükkamine ja edasilükkamiste virnastamine

Edasilükkamise lauseid kasutatakse funktsioonikutse täitmise edasilükkamiseks, kuni edasilükkamislauset sisaldav funktsioon täitmise lõpetab.

Õppige seda näitega:

package main
import "fmt"

func sample() {  
    fmt.Println("Inside the sample()")
}
func main() {  
    //sample() will be invoked only after executing the statements of main()
    defer sample()
    fmt.Println("Inside the main()")
}

Väljund saab olema

Inside the main()
Inside the sample()

Siin lükatakse proovi() täitmine edasi, kuni sulgemisfunktsiooni(main()) täitmine on lõppenud.

Edasilükkamise virnastamine kasutab mitut edasilükkamise avaldust. Oletame, et teil on funktsiooni sees mitu edasilükkamislauset. Go paigutab kõik edasilükatud funktsioonikutsed virna ja kui ümbritsev funktsioon naaseb, käivitatakse virnastatud funktsioonid Last In First Out (LIFO) tellimus. Seda näete allolevas näites.

Käivitage allolev kood

package main
import "fmt"

func display(a int) {  
    fmt.Println(a)
}
func main() {  
    defer display(1)
    defer display(2)
    defer display(3)
    fmt.Println(4)
}

Väljund saab olema

4
3
2
1			

Siin käivitatakse esmalt main() sees olev kood ja seejärel edasilükatud funktsioonikutsed vastupidises järjekorras, st 4, 3,2,1, XNUMX, XNUMX.

viiteid

Enne näpunäidete selgitamist käsitleme kõigepealt operaatorit '&'. Muutuja aadressi saamiseks kasutatakse operaatorit '&'. See tähendab, et '&a' prindib muutuja a mäluaadressi.

Selles Golangi õpetuses käivitame alloleva programmi, et kuvada muutuja väärtus ja selle muutuja aadress

package main
import "fmt"

func main() {
	a := 20
	fmt.Println("Address:",&a)
	fmt.Println("Value:",a)
}

Tulemus saab olema

Address: 0xc000078008
Value: 20

Osutimuutuja salvestab teise muutuja mäluaadressi. Kursori saate määratleda süntaksi abil

	var variable_name *type

Tärn (*) tähistab muutujat, mis on osuti. Alloleva programmi käivitamisel saate rohkem aru

package main
import "fmt"

func main() {
	//Create an integer variable a with value 20
	a := 20
	
	//Create a pointer variable b and assigned the address of a
	var b *int = &a

	//print address of a(&a) and value of a  
	fmt.Println("Address of a:",&a)
	fmt.Println("Value of a:",a)

	//print b which contains the memory address of a i.e. &a
	fmt.Println("Address of pointer b:",b)

	//*b prints the value in memory address which b contains i.e. the value of a
	fmt.Println("Value of pointer b",*b)

	//increment the value of variable a using the variable b
	*b = *b+1

	//prints the new value using a and *b
	fmt.Println("Value of pointer b",*b)
	fmt.Println("Value of a:",a)}

Väljund saab olema

Address of a: 0x416020
Value of a: 20
Address of pointer b: 0x416020
Value of pointer b 20
Value of pointer b 21
Value of a: 21

Struktuurid

Struktuur on kasutaja määratletud andmetüüp, mis ise sisaldab veel ühte sama või erinevat tüüpi elementi.

Struktuuri kasutamine on kaheastmeline protsess.

Esiteks looge (deklareerige) struktuuri tüüp

Teiseks loo väärtuste salvestamiseks seda tüüpi muutujad.

Struktuure kasutatakse peamiselt siis, kui soovite seotud andmeid koos salvestada.

Mõelge töötaja teabele, millel on nimi, vanus ja aadress. Saate sellega hakkama kahel viisil

Loo 3 massiivi – üks massiiv salvestab töötajate nimed, üks vanus ja kolmas vanus.

Struktuuritüübi deklareerimine kolme väljaga – nimi, aadress ja vanus. Looge seda tüüpi struktuuriga massiiv, kus iga element on struktuuriobjekt, millel on nimi, aadress ja vanus.

Esimene meetod ei ole tõhus. Seda tüüpi stsenaariumide korral on struktuurid mugavamad.

Struktuuri deklareerimise süntaks on

type structname struct {
   variable_1 variable_1_type
   variable_2 variable_2_type
   variable_n variable_n_type
}

Struktuurideklaratsiooni näide on

type emp struct {
    name string
    address string
    age int
}

Siin luuakse uus kasutaja määratud tüüp nimega emp. Nüüd saate süntaksi abil luua emp-tüüpi muutujaid

	var variable_name struct_name

Näitena võib tuua

var empdata1 emp 

Saate määrata empdata1 väärtused as

empdata1.name = "John"
	empdata1.address = "Street-1, Bangalore"
	empdata1.age = 30

Samuti saate luua struktuurimuutuja ja määrata väärtused

empdata2 := emp{"Raj", "Building-1, Delhi", 25}

Siin peate säilitama elementide järjekorra. Raj kaardistatakse nimega, järgmine element aadressiga ja viimane vanusega.

Käivitage allolev kood

package main
import "fmt"

//declared the structure named emp
type emp struct {
        name string
        address string
        age int
}       

//function which accepts variable of emp type and prints name property
func display(e emp) {
          fmt.Println(e.name)
}

func main() {
// declares a variable, empdata1, of the type emp
var empdata1 emp
//assign values to members of empdata1
empdata1.name = "John"
empdata1.address = "Street-1, London"
empdata1.age = 30

//declares and assign values to variable empdata2 of type emp
empdata2 := emp{"Raj", "Building-1, Paris", 25}

//prints the member name of empdata1 and empdata2 using display function
display(empdata1)
display(empdata2)
}

Väljund

John
Raj

Meetodid (mitte funktsioonid)

Meetod on vastuvõtja argumendiga funktsioon. Archistruktuuriliselt jääb see märksõna func ja meetodi nime vahele. Meetodi süntaks on

func (variable variabletype) methodName(parameter1 paramether1type) {  
}

Teisendame ülaltoodud näidisprogrammi funktsiooni asemel meetodeid kasutama.

package main
import "fmt"

//declared the structure named emp
type emp struct {
    name string
    address string
    age int
}

//Declaring a function with receiver of the type emp
func(e emp) display() {
    fmt.Println(e.name)
}

func main() {
    //declaring a variable of type emp
    var empdata1 emp
    
    //Assign values to members
    empdata1.name = "John"
    empdata1.address = "Street-1, Lodon"
    empdata1.age = 30

    //declaring a variable of type emp and assign values to members
    empdata2 := emp {
        "Raj", "Building-1, Paris", 25}

    //Invoking the method using the receiver of the type emp
   // syntax is variable.methodname()
    empdata1.display()
    empdata2.display()
}

Go ei ole objektorienteeritud keel ja sellel puudub klassi mõiste. Meetodid annavad aimu sellest, mida teete objektorienteeritud programmides, kus klassi funktsioone kutsutakse välja süntaksiga objektinimi.funktsiooninimi()

Kooskõla

Go toetab ülesannete samaaegset täitmist. See tähendab, et Go saab korraga täita mitut ülesannet. See erineb paralleelsuse mõistest. Paralleelsuse korral jagatakse ülesanne väikesteks alamülesanneteks ja täidetakse paralleelselt. Kuid samaaegselt täidetakse korraga mitu ülesannet. Samaaegsus saavutatakse Go-s Goroutines'i ja kanalite abil.

Gorutines

Gorutine on funktsioon, mis võib töötada samaaegselt teiste funktsioonidega. Tavaliselt kantakse funktsiooni käivitamisel juhtelement väljakutsutavasse funktsiooni ja kui selle täitmise juhtimine naaseb kutsuva funktsiooni juurde. Seejärel jätkab helistamisfunktsioon oma täitmist. Väljakutsuv funktsioon ootab, kuni käivitatud funktsioon täitmise lõpetab, enne kui jätkab ülejäänud lausetega.

Kuid gorutiini puhul ei oota kutsumisfunktsioon kutsutud funktsiooni täitmise lõpuleviimist. Selle täitmist jätkatakse järgmiste avaldustega. Ühes programmis võib olla mitu gorutiini.

Samuti väljub põhiprogramm, kui ta on oma avalduste täitmise lõpetanud, ega oota kutsutud gorutiinide lõpetamist.

Goroutine'i käivitamiseks kasutatakse märksõna go, millele järgneb funktsioonikutse.

Näide

go add(x,y)

Alltoodud Golangi näidete abil saate aru gorutiinidest. Käivitage allolev programm

package main
import "fmt"
    
func display() {
	for i:=0; i<5; i++ {
		fmt.Println("In display")
	}
}

func main() {
	//invoking the goroutine display()
	go display()
	//The main() continues without waiting for display()
	for i:=0; i<5; i++ {
		fmt.Println("In main")
	}
}

Väljund saab olema

In main
In main
In main
In main
In main

Siin lõpetas põhiprogramm täitmise juba enne gorutiini algust. Display() on gorutiin, mis käivitatakse süntaksi abil

go function_name(parameter list)

Ülaltoodud koodis ei oota main() displei() lõpetamist ja main() lõpetas täitmise enne, kui display() oma koodi käivitas. Nii et display() sees olevat printimislauset ei trükitud.

Nüüd muudame programmi, et printida väljavõtted ka väljast display(). Lisame 2-sekundilise viivituse main() for-tsüklile ja 1-sekundilise viivituse display() for-tsüklile.

package main
import "fmt"
import "time"
    
func display() {
	for i:=0; i<5; i++ {
		time.Sleep(1 * time.Second)
		fmt.Println("In display")
	}
}

func main() {
	//invoking the goroutine display()
	go display()
	for i:=0; i<5; i++ {
		time.Sleep(2 * time.Second)
		fmt.Println("In main")
	}
}

Väljund on mõnevõrra sarnane

In display
In main
In display
In display
In main
In display
In display
In main
In main
In main

Siin näete, et mõlemad tsüklid täidetakse samaaegse täitmise tõttu kattuval viisil.

Kanalid

Kanalid on viis, kuidas funktsioonid saavad omavahel suhelda. Seda võib pidada meediumiks, kuhu üks rutiin paigutab andmed ja millele pääseb juurde teine ​​rutiin Golangi serveris.

Kanali saab deklareerida süntaksiga

channel_variable := make(chan datatype)

Näide:

	ch := make(chan int)

Saate saata andmeid kanalile süntaksi abil

channel_variable <- variable_name

Näide

    ch <- x

Saate kanalilt andmeid vastu võtta süntaksi abil

    variable_name := <- channel_variable

Näide

   y := <- ch

Ülaltoodud go-keelsetes gorutiini näidetes olete näinud, et põhiprogramm ei oota gorutiini. Kuid see pole nii, kui kanalid on kaasatud. Oletame, et kui gorutiin surub andmed kanalisse, ootab main() kanali andmeid vastuvõtvat lauset, kuni ta andmed hangib.

Seda näete allpool Go keele näidetes. Kõigepealt kirjutage tavaline gorutiin ja vaadake käitumist. Seejärel muutke programmi kanalite kasutamiseks ja käitumise vaatamiseks.

Käivitage allolev programm

package main
import "fmt"
import "time"
    
func display() {
	time.Sleep(5 * time.Second)
	fmt.Println("Inside display()")
}

func main() {
	go display()
	fmt.Println("Inside main()")
}

Väljund saab olema

Inside main()

Main() lõpetas täitmise ja väljus enne gorutiini käivitamist. Nii et kuva() sees olevat printimist ei teostatud.

Nüüd muutke ülaltoodud programmi kanalite kasutamiseks ja käitumise vaatamiseks.

package main
import "fmt"
import "time"
    
func display(ch chan int) {
	time.Sleep(5 * time.Second)
	fmt.Println("Inside display()")
	ch <- 1234
}

func main() {
	ch := make(chan int) 
	go display(ch)
	x := <-ch
	fmt.Println("Inside main()")
	fmt.Println("Printing x in main() after taking from channel:",x)
}

Väljund saab olema

Inside display()
Inside main()
Printing x in main() after taking from channel: 1234

Siin juhtub, et main() x saavutamisel := <-ch ootab andmeid kanalil ch. Kuva() ootab 5 sekundit ja seejärel edastab andmed kanalile ch. Main() kanalilt andmete vastuvõtmisel deblokeeritakse ja jätkab selle täitmist.

Saatja, kes edastab andmed kanalisse, saab kanali sulgemisega teavitada vastuvõtjaid, et kanalile rohkem andmeid ei lisata. Seda kasutatakse peamiselt siis, kui kasutate andmete kanalisse suunamiseks silmust. Kanali saab sulgeda kasutades

close(channel_name)

Ja vastuvõtja otsas on võimalik kontrollida, kas kanal on suletud, kasutades lisamuutujat, samal ajal kui kanalilt andmeid tuuakse kasutades

variable_name, status := <- channel_variable

Kui olek on Tõene, tähendab see, et saite kanalilt andmeid. Kui see on vale, tähendab see, et proovite lugeda suletud kanalist

Gorutiinide vaheliseks suhtluseks saate kasutada ka kanaleid. Vaja kasutada 2 gorutiini – üks lükkab andmed kanalisse ja teine ​​võtab andmed kanalilt vastu. Vaadake allolevat programmi

package main
import "fmt"
import "time"

//This subroutine pushes numbers 0 to 9 to the channel and closes the channel
func add_to_channel(ch chan int) {	
	fmt.Println("Send data")
	for i:=0; i<10; i++ {
		ch <- i //pushing data to channel
	}
	close(ch) //closing the channel

}

//This subroutine fetches data from the channel and prints it.
func fetch_from_channel(ch chan int) {
	fmt.Println("Read data")
	for {
		//fetch data from channel
x, flag := <- ch

		//flag is true if data is received from the channel
//flag is false when the channel is closed
if flag == true {
			fmt.Println(x)
		}else{
			fmt.Println("Empty channel")
			break	
		}	
	}
}

func main() {
	//creating a channel variable to transport integer values
	ch := make(chan int)

	//invoking the subroutines to add and fetch from the channel
	//These routines execute simultaneously
	go add_to_channel(ch)
	go fetch_from_channel(ch)

	//delay is to prevent the exiting of main() before goroutines finish
	time.Sleep(5 * time.Second)
	fmt.Println("Inside main()")
}

Siin on 2 alamprogrammi, millest üks edastab andmed kanalile ja teine ​​prindib andmed kanalile. Funktsioon add_to_channel liidab numbrid vahemikus 0 kuni 9 ja sulgeb kanali. Samal ajal ootab funktsioon Fetch_from_channel kell

x, lipp := <- ch ja kui andmed muutuvad kättesaadavaks, prindib see andmed välja. See väljub, kui lipp on vale, mis tähendab, et kanal on suletud.

Main()-s oodatakse main()-st väljumist, kuni gorutiinid täitmise lõpetavad.

Käivitage kood ja vaadake väljundit kui

Read data
Send data
0
1
2
3
4
5
6
7
8
9
Empty channel
Inside main()

valima

Valikut saab vaadata lüliti avaldusena, mis töötab kanalitel. Siin on juhtumiavaldused kanalioperatsioon. Tavaliselt loetakse iga juhtumi avaldused kanalist. Kui mõni juhtudest on valmis (kanalit loetakse), täidetakse selle juhtumiga seotud avaldus. Kui valmis on mitu juhtumit, valib see juhusliku. Teil võib olla vaikejuhtum, mis käivitatakse, kui ükski juhtudest pole valmis.

Vaatame allolevat koodi

package main
import "fmt"
import "time"

//push data to channel with a 4 second delay
func data1(ch chan string) {  
    time.Sleep(4 * time.Second)
    ch <- "from data1()"
}

//push data to channel with a 2 second delay
func data2(ch chan string) {  
    time.Sleep(2 * time.Second)
    ch <- "from data2()"
}

func main() {
    //creating channel variables for transporting string values
    chan1 := make(chan string)
    chan2 := make(chan string)
    
    //invoking the subroutines with channel variables
    go data1(chan1)
    go data2(chan2)
    
    //Both case statements wait for data in the chan1 or chan2.
    //chan2 gets data first since the delay is only 2 sec in data2().
    //So the second case will execute and exits the select block
    select {
    case x := <-chan1:
        fmt.Println(x)
    case y := <-chan2:
        fmt.Println(y)
    }
}

Ülaltoodud programmi käivitamine annab väljundi:

from data2()

Siin ootab valikuavaldus, kuni andmed on saadaval mis tahes kanalis. Data2() lisab andmed kanalile pärast 2-sekundilist unerežiimi, mis käivitab teise juhtumi käivitamise.

Lisage samas programmis valikule vaiketäht ja vaadake väljundit. Kui valikuplokini jõudmisel ühelgi juhul pole kanalil andmeid valmis, siis käivitab see vaikeploki, ootamata, kuni andmed on ühelgi kanalil saadaval.

package main
import "fmt"
import "time"

//push data to channel with a 4 second delay
func data1(ch chan string) {  
    time.Sleep(4 * time.Second)
    ch <- "from data1()"
}

//push data to channel with a 2 second delay
func data2(ch chan string) {  
    time.Sleep(2 * time.Second)
    ch <- "from data2()"
}

func main() {
    //creating channel variables for transporting string values  
    chan1 := make(chan string)
    chan2 := make(chan string)
    
    //invoking the subroutines with channel variables
    go data1(chan1)
    go data2(chan2)

    //Both case statements check for data in chan1 or chan2.
    //But data is not available (both routines have a delay of 2 and 4 sec)
    //So the default block will be executed without waiting for data in channels.
    select {
    case x := <-chan1:
        fmt.Println(x)
    case y := <-chan2:
        fmt.Println(y)
    default:
    	fmt.Println("Default case executed")
    }
}

See programm annab väljundi:

Default case executed			

Selle põhjuseks on asjaolu, et valikuploki jõudmisel polnud ühelgi kanalil lugemiseks andmeid. Seega täidetakse vaikejuhtum.

mutexid

Mutex on vastastikuse välistamise lühivorm. Mutexi kasutatakse siis, kui te ei soovi lubada ressursile juurdepääsu korraga mitmele alamprogrammile. Mutexil on 2 meetodit – lukustada ja avada. Mutex sisaldub sünkroonimispaketis. Seega peate sünkroonimispaketi importima. Avaldused, mida tuleb üksteist välistavalt täita, saab paigutada mutex.Lock() ja mutex.Unlock() sisse.

Õppime mutexi näitega, mis loeb tsükli käivitamise kordade arvu. Selles programmis eeldame, et rutiin jookseb tsüklit 10 korda ja loendus salvestatakse summana. Kutsute seda rutiini 3 korda, nii et koguarv peaks olema 30. Arv salvestatakse globaalsesse muutujate loendisse.

Esiteks käivitate programmi ilma mutexita

package main
import "fmt"
import "time"
import "strconv"
import "math/rand"
//declare count variable, which is accessed by all the routine instances
var count = 0

//copies count to temp, do some processing(increment) and store back to count
//random delay is added between reading and writing of count variable
func process(n int) {
	//loop incrementing the count by 10
	for i := 0; i < 10; i++ {
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		temp := count
		temp++
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		count = temp
	}
	fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count))
}

func main() {
	//loop calling the process() 3 times
	for i := 1; i < 4; i++ {
		go process(i)
	}

	//delay to wait for the routines to complete
	time.Sleep(25 * time.Second)
	fmt.Println("Final Count:", count)
}

Vaadake tulemust

 Count after i=1 Count: 11
Count after i=3 Count: 12
Count after i=2 Count: 13
Final Count: 13

Tulemus võib selle täitmisel olla erinev, kuid lõpptulemus ei ole 30.

Siin juhtub, et 3 gorutiini üritavad suurendada muutuja loendusesse salvestatud silmuste arvu. Oletame, et hetkel on arv 5 ja goroutine1 suurendab arvu 6-ni. Peamised sammud hõlmavad

Kopeeri loendur temp

Temp

Salvestage temperatuur loendamiseks

Oletame, et varsti pärast sammu 3 sooritamist goroutine1 poolt; teisel gorutiinil võib olla vana väärtus, näiteks 3 teeb ülaltoodud toimingud ja salvestab 4 tagasi, mis on vale. Seda saab vältida Mutexi kasutamisega, mis paneb teised rutiinid ootama, kui üks rutiin juba seda muutujat kasutab.

Nüüd käivitate programmi mutexiga. Siin teostatakse ülalmainitud 3 sammu mutexis.

package main
import "fmt"
import "time"
import "sync"
import "strconv"
import "math/rand"

//declare a mutex instance
var mu sync.Mutex

//declare count variable, which is accessed by all the routine instances
var count = 0

//copies count to temp, do some processing(increment) and store back to count
//random delay is added between reading and writing of count variable
func process(n int) {
	//loop incrementing the count by 10
	for i := 0; i < 10; i++ {
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		//lock starts here
		mu.Lock()
		temp := count
		temp++
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		count = temp
		//lock ends here
		mu.Unlock()
	}
	fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count))
}

func main() {
	//loop calling the process() 3 times
	for i := 1; i < 4; i++ {
		go process(i)
	}

	//delay to wait for the routines to complete
	time.Sleep(25 * time.Second)
	fmt.Println("Final Count:", count)
}

Nüüd on väljund

 Count after i=3 Count: 21
Count after i=2 Count: 28
Count after i=1 Count: 30
Final Count: 30

Siin saame lõppväljundina oodatud tulemuse. Kuna loenduse lugemise, suurendamise ja tagasikirjutamise avaldused täidetakse mutexis.

Viga käsitlemisel

Vead on ebanormaalsed tingimused, nagu faili sulgemine, mida ei avata, faili avamine, mida pole olemas, jne. Funktsioonid tagastavad tavaliselt vead viimase tagastatava väärtusena.

Allolev näide selgitab vea kohta lähemalt.

package main
import "fmt"
import "os"

//function accepts a filename and tries to open it.
func fileopen(name string) {
    f, er := os.Open(name)

    //er will be nil if the file exists else it returns an error object  
    if er != nil {
        fmt.Println(er)
        return
    }else{
    	fmt.Println("file opened", f.Name())
    }
}

func main() {  
    fileopen("invalid.txt")
}

Väljundiks on:

open /invalid.txt: no such file or directory

Siin proovisime avada olematut faili ja see tagastas vea muutujale er. Kui fail on kehtiv, on viga null

Kohandatud vead

Seda funktsiooni kasutades saate luua kohandatud vigu. Seda tehakse veapaketi New() abil. Kirjutame ülaltoodud programmi ümber, et kasutada kohandatud vigu.

Käivitage allolev programm

package main
import "fmt"
import "os"
import "errors"

//function accepts a filename and tries to open it.
func fileopen(name string) (string, error) {
    f, er := os.Open(name)

    //er will be nil if the file exists else it returns an error object  
    if er != nil {
        //created a new error object and returns it  
        return "", errors.New("Custom error message: File name is wrong")
    }else{
    	return f.Name(),nil
    }
}

func main() {  
    //receives custom error or nil after trying to open the file
    filename, error := fileopen("invalid.txt")
    if error != nil {
        fmt.Println(error)
    }else{
    	fmt.Println("file opened", filename)
    }  
}

Väljundiks on:

Custom error message:File name is wrong

Siin tagastab area() ruudu pindala. Kui sisend on väiksem kui 1, tagastab area() veateate.

Failide lugemine

Andmete salvestamiseks kasutatakse faile. Go võimaldab meil failidest andmeid lugeda

Esmalt looge oma praeguses kataloogis alloleva sisuga fail data.txt.

Line one
Line two
Line three

Nüüd käivitage allolev programm, et näha, et see prindib väljundina kogu faili sisu

package main
import "fmt"
import "io/ioutil"

func main() {  
    data, err := ioutil.ReadFile("data.txt")
    if err != nil {
        fmt.Println("File reading error", err)
        return
    }
    fmt.Println("Contents of file:", string(data))
}

Siin loeb andmed err := ioutil.ReadFile(“data.txt”) andmed ja tagastab baidijada. Printimise ajal teisendatakse see stringivormingusse.

Failide kirjutamine

Seda näete programmiga

package main
import "fmt"
import "os"

func main() {  
    f, err := os.Create("file1.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    l, err := f.WriteString("Write Line one")
    if err != nil {
        fmt.Println(err)
        f.Close()
        return
    }
    fmt.Println(l, "bytes written")
    err = f.Close()
    if err != nil {
        fmt.Println(err)
        return
    }
}

Siin luuakse fail test.txt. Kui fail on juba olemas, siis faili sisu kärbitakse. Writeline() kasutatakse faili sisu kirjutamiseks. Pärast seda sulgesite faili Close() abil.

Cheat Sheet

Selles Go õpetuses käsitlesime

teema Kirjeldus Süntaks
Põhitüübid Numbrid, string, bool
Muutujad Muutujate deklareerimine ja väärtuste määramine muutuja_nime tüüp
muutuja_nimi tüüp = väärtus
muutuja_nimi1, muutuja_nimi2 = väärtus1, väärtus2
muutuja_nimi := väärtus
Konstandid Muutujad, mille väärtust ei saa pärast määramist muuta const muutuja = väärtus
Loopi jaoks Käivitage avaldused tsüklina. initsialiseerimise_väljenduse jaoks; hindamine_avaldis; iteratsiooni_avaldis{
// üks või mitu väidet
}
Kui veel See on tingimuslik väide if tingimus{
// väited_1
} Else {
// väited_2
}
lüliti Tingimuslik avaldus mitme juhtumiga lüliti avaldis {
case value_1:
avaldused_1
case value_2:
avaldused_2
case value_n:
avaldused_n
default:
avaldused_vaikeväärtus
}
Array Fikseeritud suurusega nimega sama tüüpi elementide jada massiivinimi := [suurus] tüüp {väärtus_0,väärtus_1,…,väärtus_suurus-1}
Viil Massiivi osa või segment var slice_name [] type = massiivi_nimi[algus:lõpp]
Funktsioonid Avalduste plokk, mis täidab konkreetset ülesannet func funktsiooni_nimi(parameetri_1 tüüp, parameetri_n tüüp) return_type {
//avaldused
}
Paketid Kasutatakse koodi korraldamiseks. Suurendab koodi loetavust ja korduvkasutatavust impordi paketi_nimi
Edasilükkamine Lülitab funktsiooni täitmise edasi, kuni sisaldav funktsioon on täitmise lõpetanud edasilükka funktsiooni_nimi(parameetrite_loend)
viiteid Salvestab teise muutuja mäluaadressi. muutuja_nimi *tüüp
struktuur Kasutaja määratletud andmetüüp, mis ise sisaldab veel ühte sama või erinevat tüüpi elementi tüüp structname struct {
muutuja_1 muutuja_1_tüüp
muutuja_2 muutuja_2_tüüp
muutuja_n muutuja_n_tüüp
}
Meetodid Meetod on vastuvõtja argumendiga funktsioon func (muutuja muutujatüüp) methodName(parameetri_loend) {
}
Gorutine Funktsioon, mis võib töötada samaaegselt teiste funktsioonidega. go function_name(parameetrite_loend)
Kanal Funktsioonide üksteisega suhtlemise viis. Meedium, kuhu üks rutiin paigutab andmed ja millele pääseb juurde teine ​​rutiin. Kuulutama:
ch := make(chan int)
Saada andmed kanalile:
kanali_muutuja <- muutuja_nimi
Saate kanalilt:
muutuja_nimi := <- kanali_muutuja
valima Lüliti avaldus, mis töötab kanalitel. Juhtumiavaldused on kanalioperatsioon. Kui mõni kanal on andmetega valmis, käivitatakse selle juhtumiga seotud avaldus vali {
juhtum x := <-chan1:
fmt.Println(x)
juhtum y := <-chan2:
fmt.Println(y)
}
mutexid Mutexi kasutatakse siis, kui te ei soovi lubada ressursile juurdepääsu korraga mitmele alamprogrammile. Mutexil on 2 meetodit – lukustada ja avada mutex.Lock()
//avaldused
mutex.Unlock().
Lugege faile Loeb andmed ja tagastab baidijada. Andmed, err := ioutil.ReadFile(failinimi)
Kirjuta faili Kirjutab andmed faili l, err := f.WriteString(kirjutatav_tekst)