konkurencia vs. paralelizmus

37
Konkurentné Go Peter Borovanský, KAI, I-18, borovan(a)ii.fmph.uniba.sk Dnes bude: štruktúry a metódy implicitný interface a typovanie konkurencia

Upload: megan-hinton

Post on 30-Dec-2015

53 views

Category:

Documents


4 download

DESCRIPTION

Konkurencia vs. paralelizmus. k ompo zícia nezávislých výpočtov spôsob myslenia, ako výpočet (pr ácu ) rozdeliť medzi nezávislých agentov keďže okolitý svet je paralelný, je to spôsob ako lepšie interagovať s ním málo kto z nás má skutočne paralelný HW, max. tak 2-,4-, 8-jadro... - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Konkurencia vs. paralelizmus

Konkurentné GoPeter Borovanský, KAI, I-18, borovan(a)ii.fmph.uniba.sk

Dnes bude: štruktúry a metódy implicitný interface a typovanie konkurencia

Page 2: Konkurencia vs. paralelizmus

Štruktúry a smerníkyProgram, ktorý generuje fibonacciho strom definovaný štruktúrou:type FibTree struct {

    left *FibTree

    right *FibTree

}

func generate(n int) *FibTree {

    if n < 2 {

        return nil

    } else {

        return &FibTree{generate(n - 1), generate(n - 2)}

 alebo  return &FibTree{left:generate(n-1),right:generate(n-2)}

 alebo  bt := new(FibTree) // alokuje krabicu pre FibTree

        bt.left = generate(n - 1)

        bt.right = generate(n - 2)

        return bt   } } BinTree.go

Page 3: Konkurencia vs. paralelizmus

MetódyNie je žiadne this, ani self, ako v iných jazykoch.

Parameter označujúci objekt, na ktorý sa metóda aplikuje, je explicitne prítomný v jej hlavičke, a to v zátvorkách pred menom metódy

func (bt *FibTree) size() int {

    if bt == nil { // metódu možno aplikovať na nil !!!

        return 1

    } else {

        return bt.left.size() + bt.right.size()

    }

} size = 1 nilsize = 1 nilsize = 2 <nil,nil>size = 3 <<nil,nil>,nil>size = 5 <<<nil,nil>,nil>,<nil,nil>>size = 8 <<<<nil,nil>,nil>,<nil,nil>>,<<nil,nil>,nil>>size = 13 <<<<<nil,nil>,nil>,<nil,nil>>,<<nil,nil>,nil>>,<<<nil,nil>,nil>,<nil,nil>>> BinTree.go

Page 4: Konkurencia vs. paralelizmus

Fibonacci a matice(z minulej prednášky)

package main 

type Matrix struct {  // struct ako v C++

   a11, a12, a21, a22 int}  // reprezentácia matice 2x2

func (m *Matrix) multiply(n *Matrix) *Matrix { 

   var c = &Matrix{  // konštruktor struct

       m.a11*n.a11 + m.a12*n.a21, // násobenie matíc 2x2,

       m.a11*n.a12 + m.a12*n.a22, // hardcode-žiadne cykly

       m.a21*n.a11 + m.a22*n.a21, 

       m.a21*n.a12 + m.a22*n.a22}    

return c // vráti pointer na maticu

} // teda Go má pointre, operátory &, * skoro ako C++

func FibMatrix(n int) int { 

   m := &Matrix{a11: 1, a12: 1, a21: 1, a22: 0} 

   p := m.power(n) 

   return p.a12}  

fibMatrix.go

Page 5: Konkurencia vs. paralelizmus

Logaritmický power(z minulej prednášky)

A už len to nezabiť tým, že power(), alias mocninu urobíme lineárnu...

Lepšia bude logaritmická verzia

func (m *Matrix) power(n int) *Matrix { 

   if n == 1 { 

       return m 

   } else if n%2 == 0 {  // mn = (mn/2)2, pre n párne

       m2 := m.power(n / 2) 

       return m2.multiply(m2) 

   } else {  // mn = m*(mn-1), pre n nepárne

       return m.power(n - 1).multiply(m) 

}}

fibMatrix.go

Page 6: Konkurencia vs. paralelizmus

Logaritmický power(z cvičení)

type realnaFunckia /*=*/ func(float64) float64

func power(n int, f realnaFunckia) realnaFunckia {

    if n == 0 {

        return func(x float64) float64 { return x }

    } else if n%2 == 0 {

// return kompozicia(power(n / 2, f), power(n / 2, f) )

        return func(x float64) float64 {  

        rf := power(n / 2, f) // rf : realnaFunckia  

         return rf(rf(x))   // fn = (fn/2) o(fn/2),pre n párne

      }     

} else {   // fn = f o (fn-1), pre n nepárne

// return kompozicia(f, power(n-1, f) )

       rf := power(n - 1, f) // rf : realnaFunckia

       return func(x float64) float64 { return f(rf(x)) }

   }

} funkcionaly.go

Page 7: Konkurencia vs. paralelizmus

Logaritmický power(z cvičení)

type realnaFunckia /*=*/ func(float64) float64

func power(n int, f realnaFunckia) realnaFunckia {

    if n == 0 {

        return func(x float64) float64 { return x }

    } else if n%2 == 0 {

rf := power(n / 2, f)

return kompozicia(rf, rf )

} else {  

return kompozicia(f, power(n-1, f) )

    }

}

funkcionaly.go

Page 8: Konkurencia vs. paralelizmus

IdentifiedPet - Dogtype IdetifiedPet interface { // interface popisuje zoznam

    getName() string } // metód, ktoré implementátor

// musí spĺňať

type Dog struct {

    name string  

    color int }

// Go nepozná pojem default-konštruktora

func NewDog(name string) *Dog { // ľub.meno konštruktora

    d := new(Dog) // toto realne nalokuje pamäť

    d.name = name  

   d.color = 0 // nejaká default-farba

    return d } // return nový objekt

func (d Dog) getName() string { return d.name }

func (d Dog) hello() string { return d.name + ": haf-haf" } duck.go

Page 9: Konkurencia vs. paralelizmus

Je už Dog IdentifiedPet ?type Cat struct {

    name string

    size int }

func (c Cat) getName() string { return c.name }

func (c Cat) hello() string { return c.name + ": mnau-mnau" }

Je…, veď už aj Cat je, lebo oba definujú predpísanú metódu getName() string

To, či typ spĺňa/nespĺňa nejaký interface, nikde nedeklarujeme, je to implicitne určené z jeho definície - na rozdiel od Javy, kde trieda implements interface

type FriendlyPet interface {

    hello() string // vie odpovedať .hello()

}

func hello(z FriendlyPet) string { // tak nech povie hello()

    return z.hello() } duck.go

Page 10: Konkurencia vs. paralelizmus

Kto je FriendlyPet ?Dog aj Cat…

Definujme jeden objekt, ktorý nie je FriendlyPet, ale je IdentifiedPet

type Mouse struct { // unfriendly mouse

    name string }

func (m Mouse) getName() string {

    return m.name }

Definujme interface obsahovo ekvivalentný s IdentifiedPet, ale s iným menom

type PetWithName interface {   

   name    string  // nejde v interface predpísať aké

položky objekt má mať, ale len aké metódy

getName() string } duck.go

Page 11: Konkurencia vs. paralelizmus

Malá Zoofunc main() {     

doggie := Dog{} // prázdny objekt Dog

   doggie := NewDog("Pluto") // objekt Dog s menom Pluto

   cattie := Cat{2} // too few values in struct initializer     

cattie := Cat{"Tom", 2}

   cattie := Cat{size: 2, name: "Tom"}

   cattie := Cat{name: "Tom"}

   var zoo []FriendlyPet  zoo = make([]FriendlyPet, 2)

   zoo[0] = doggie

   zoo[1] = cattie

   zoo := []FriendlyPet{doggie, cattie} zoo := []FriendlyPet{doggie, cattie, Mouse{name: "Jerry"}}

cannot use Mouse literal (type Mouse) as type FriendlyPet

in array element     // Jerry is not friendly duck.go

Page 12: Konkurencia vs. paralelizmus

   for _, z := range zoo { // zoo::[]FriendlyPet

        fmt.Println(z.hello() + "," + hello(z))

   } var fz PetWithName   

fz = doggie      // je doggie PetWithName ?

fz = cattie // je cattie PetWithName ?

fz = Mouse{name: "Jerry“} // je “Jerry” PetWithName ?

  fmt.Println(fz.getName()) // zdá sa, že

IdentifiedPet == PetWithName, štrukturálna rovnosť typov

var iz IdetifiedPet     

iz = fz // ???     

fmt.Println(iz.getName()) }

IdentifiedPet =?= PetWithName

Pluto: haf-haf,Pluto: haf-hafTom: mnau-mnau,Tom: mnau-mnauJerryJerry

duck.go

Page 13: Konkurencia vs. paralelizmus

Duck typing (prevzaté z prednášky Python)

je forma dynamického typovania, dynamická náhrada virtuálnych metód

class pes(): # definujeme dve triedy bez akejkoľvek defičnosti def zvuk(self): # obe definujú metódu zvuk() return "haw-haw" class macka(): def zvuk(self): return "mnau-mnau"

def zvuk(zviera): # otázkou (statického programátora) je, akého typu je

print(zviera.zvuk()) # premenná zviera, keď na ňu aplikujeme .zvuk()

# odpoveď: uvidí sa v RT podľa hodnoty premennejfarma = [pes(), macka()] # heterogénny zoznam objektov

for zviera in farma: zvuk(zviera)

If it looks like a duck and quacks like a duck, it must be a duck

haw-hawmnau-mnau

pes macka

Page 14: Konkurencia vs. paralelizmus

Ako by to pascalista s dedičnosťouclass animal(): # nadtrieda def zvuk(self): # náznak virtuálnej metódy return "no sound" # neviem aký zvuk, resp. pass

class dog(animal): # dog ako podtrieda animal def zvuk(self): # override metódy zvuk z animal

return "haw-haw" class cat(animal): # cat ako podtrieda animal def zvuk(self): # override metódy zvuk z animal return "mnau-mnau"

class cow(animal): # cow ako podtrieda animal pass # nemá vlastnú metódu zvuk

# pass znamená prázdny príkazfor animal in [dog(), cat(), cow()] : print(animal.zvuk())

haw-hawmnau-mnauno sound

dog cat cow

animal

Page 15: Konkurencia vs. paralelizmus

Konkurencia

http://talks.golang.org/2012/waza.slide#1

Page 16: Konkurencia vs. paralelizmus

kompozícia nezávislých výpočtov spôsob myslenia, ako výpočet (prácu) rozdeliť medzi nezávislých agentov keďže okolitý svet je paralelný, je to spôsob ako lepšie interagovať s ním málo kto z nás má skutočne paralelný HW, max. tak 2-,4-, 8-jadro...

na jednom procesore paralelizmus neurobíte, ale konkurentný výpočet ánoale ... konkurentný výpočet na jednom procesore bude pravdepodobne pomalší ako

sekvenčná verzia, takže viac ide o konkurentnú paradigmu ako o čas

Go konkurencia založená na CommunicatingSequentialProcesses (T. Hoare, 1978) poskytuje: konkurentné procedúry (tzv. gorutiny) synchronizáciu a komunikáciu prostredníctvom kanálov príkaz select

http://www.youtube.com/watch?v=f6kdp27TYZs

Konkurencia vs. paralelizmus

Page 17: Konkurencia vs. paralelizmus

Gorutina - príkladGorutina loopForever sa vykonáva ako funkcia loopForever len sa

nečaká na jej výsledok, resp. skončenie

package main 

import (    "fmt"    "math/rand"    "time") 

func loopForever(task string) { 

   for i := 1; ; i++ {  // počítame do nekonečna

       fmt.Printf("%s:%d\n", task, i) 

       time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)  }} 

func main() {    

go loopForever("prvy")  // spustenie 1.gorutiny

go loopForever("druhy")  // spustenie 2.gorutiny

var input string // toto čaká na input, v opačnom    

fmt.Scanln(&input) // prípade, keď umrie hlavné vlákno

fmt.Println("main stop")} // umrie v Go všetko...  concurrent1.go

Page 18: Konkurencia vs. paralelizmus

GorutinaGorutina je nezávisle vykonávaná funkcia má vlastný stack, ktorý rastie/scvrkáva sa podľa jej potrieb môže ich byť veľa, aj veľmi veľa (uvidíme ~ 100.000) je to menej ako vlákno (thread), ale k nemu to má najbližšie

Anonymná gorutina je de-facto bezmenná funkcia, ktorú aj hneď zavoláme:

func main() {    

go func /*tu chýba meno fcie*/ (task string) { 

    for i := 1; ; i++ {  // počítame do nekonečna

        fmt.Printf("%s:%d\n", task, i) 

        time.Sleep(...) 

}

} ("prvy") // tu hneď voláme anonymnú fciu s argumentom

Page 19: Konkurencia vs. paralelizmus

Pomocou kanálov (nebuffrovaná verzia):var ch chan int resp. ch := make(chan int)

ch = make(chan int)

zápis do kanála je blokujúca operácia, kým hodnotu niekto neprečíta z kanálach <- 123

čítanie z kanála je blokujúca operácia, až kým hodnotu niekto nezapíše do kanála

x = <-ch

takže ide o komunikáciu (prenos dát), ale aj o synchronizáciu.

V prípade buffrovaných kanálov make(chan int, 10) prídeme o synchronizáciu, tažke to skúsime neskôr...

Komunikácia a synchronizácia

Page 20: Konkurencia vs. paralelizmus

Dvaja píšu, jeden čítafunc loopAndSend(task string, ch chan string) { 

   for i := 1; i < 100; i++ {        

ch <- fmt.Sprintf("%s:%d\n", task, i)  

     time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)    

}} 

func main() {   // dve gorutiny píšu do   

ch := make(chan string)     // toho istého kanála ch

go loopAndSend("prvy", ch)    

go loopAndSend("druhy", ch)    

for {       // tu to čítame for msg := range ch {

msg := <-ch // range prebieha obsahom

// celého kanála

       fmt.Print(msg)     fmt.Print(msg)

}    }

fmt.Println("main stop") } // nikdy neskončí, prečo ?

concurrent2.go

Page 21: Konkurencia vs. paralelizmus

Funkcia vráti kanálchan string je typ kanála stringov, funkcia ho môže vrátiť ako výsledok

func loopToChannel(task string) chan string { 

   ch := make(chan string) // vytvor kanál

   go func() {  // pusti nezávislú gorutinu

       for i := 1; i < 100; i++ {  // ktorá píše do kanála

           ch <- fmt.Sprintf("%s:%d\n", task, i) 

           time.Sleep(...) } 

   }()  // argumenty anonymnej funkcie

   return ch }  // vráť kanál ch:chan string

func main() { 

   ch1 := loopToChannel("prvy") 

   ch2 := loopToChannel("druhy") 

   for {        

fmt.Print(<-ch1)  // čo dostaneme ???

       fmt.Print(<-ch2)  // chápeme už synchronizáciu ??

}concurrent3.go

Page 22: Konkurencia vs. paralelizmus

Kanálový sútokfunc multiplexor(ch1, ch2 chan string) chan string { 

   ch := make(chan string) 

   go func() {  // prvá gorutina

       for { ch <- <-ch1 }  // čítaj z ch1 a píš to do ch

   }() 

   go func() {  // druhá gorutina

       for { ch <- <-ch2 } // čítaj z ch2 a píš to do ch

   }() 

   return ch} 

func main() { 

   ch1 := loopToChannel("prvy")  // tretia gorutina

   ch2 := loopToChannel("druhy") // štvrtá gorutina

   ch := multiplexor(ch1, ch2) 

   for { 

       fmt.Print(<-ch)    } 

concurrent3.go

Page 23: Konkurencia vs. paralelizmus

Selectselect je príkaz syntaxou podobný switch, à la java

func multiplexorSelect(ch1, ch2 chan string) chan string { 

   ch := make(chan string) 

   go func() {  // jednu gorutinu sme ušetrili :-)

       for { 

           select { // select vykoná niektorý neblokovaný

           case val := <-ch1: // komunikačný case-príkaz

               ch <- val  // ak niekto zapísal do ch1

           case val := <-ch2: // číta sa z ch1, ak ch2,

               ch <- val  // tak z ch2, inak je blokovaný

           } 

       }  // select odpáli nejaká komunikačná udalosť

// (zápis/čítanie z/do kanála) v case príkazoch

   }()  // alebo timeout...

   return ch }

concurrent3.go

Page 24: Konkurencia vs. paralelizmus

Select a timeoutfunc multiplexorSelect(ch1, ch2 chan string) chan string { 

   ch := make(chan string) 

   go func() { 

       gameOver := time.After(10 * time.Second) 

       for { 

           select { 

           case val := <-ch1: ch <- val 

           case val := <-ch2: ch <- val 

           case <-gameOver: 

               ch <- "GAME OVER\n" 

               close(ch) 

         } 

       }    

}()    

return ch

}  concurrent3.go

Page 25: Konkurencia vs. paralelizmus

TimeoutgameOver := time.After(10*time.Second)         

alebo vlastný kód

gameOver := make(chan bool)         

go func(seconds int) {         

time.Sleep(10 * time.Second)  

  gameOver <- true // timeout        

}(10)  

concurrent3.go

je kanál už zavretý ?for { fmt.Print( <-ch) }   

zle skončí, ak close(ch)

for {         

val, opened := <-ch     

if !opened { break }         

fmt.Print(val)     

}    

Page 26: Konkurencia vs. paralelizmus

Producer-Consumerfunc producer(ch chan int) { 

   for i := 1; i <= 30; i++ { 

       ch <- i 

       fmt.Println("produce: " + strconv.Itoa(i))         //time.Sleep(time.Second) // lenivá produkcia

   }} 

func consumer(ch chan int) { 

   for i := 1; i <= 30; i++ { 

       fmt.Println("consume: ", <-ch)         time.Sleep(time.Second)  // lenivá spotreba

   }} 

func main() { 

   ch := make(chan int, 5) // buffrovaný kanál

   go producer(ch) 

   go consumer(ch) 

   time.Sleep(100000000000)} // skoro večnosť producerconsumer.go

Page 27: Konkurencia vs. paralelizmus

Čínsky šepkárivar number = 100000 

func main() {     start := time.Now() 

   prev := make(chan int) 

   first := prev  // ľavé ucho (ľ.u.) nultého šepkára

   go func() { first <- 0 }()  // nultému šepneme 0 do ľ.u.

   for i := 0; i < number; i++ { // 40000 číňanov

       next := make(chan int)  // kanál z p.u.i-teho

       go func(from, to chan int) { // do ľ.u. i+1-vého

           for { to <- 1 + <- from } // šepnem ďalej 1+čo

       }(prev, next)  // počujem

       prev = next  // pokračujem, i k i+1

   }    elapsed := time.Since(start) 

   fmt.Println(<-prev) fmt.Println(elapsed)}  

chinees.go

Page 28: Konkurencia vs. paralelizmus

Prvočísla(Eratosténovo sito)

prev := make(chan int)    

first := prev 

go func() { 

   for i := 2; ; i++ { first <- i } }() // do first sypeme 2,3,4, …

for i := 0; i < 10000; i++ {  // čínski preosievači prvočísel

   prime := <-prev  // prvé preosiate musí byť prvočíslo

   fmt.Println(prime) 

   next := make(chan int)  // kanál pre ďalšieho preosievača

   go func(prime int, from, to chan int) { // číta z from, píše do

      for {  // do to, vyčiarkne deliteľné prime

          val := <-from  // číta z from – vstupný kanál

          if val%prime > 0 {  // je deliteľné prime ?

              to <- val  // ak nie je, píš do to - výstupný

          } 

      } 

       }(prime, prev, next)  // spustenie nezávislého preosievača

       prev = next  // výsledok ide ďalšiemu osievačovi

   }

 

chineprimes.go

Page 29: Konkurencia vs. paralelizmus

Quicksort - pivotizáciaNekonkurentná pivotizácia, nepekné dvojité testy …

func pivot(pole []int) int { 

   i, j, pivot := 1, len(pole)-1, pole[0] 

   for i <= j { 

// hľadanie maxiputána medzi liliputánmi

      for i <= j && pole[i] <= pivot{ i++ } 

// hľadanie liliputána medzi maxiputánmi

    for j >= i && pole[j] >= pivot{ j-- } 

      if i < j { // nájdení kandidáti sa vymenia

           pole[i], pole[j] = pole[j], pole[i] 

      } 

   }  // pivota pichni medzi liliputánov a maxiputánov

   pole[0], pole[j] = pole[j], pole[0] 

   return i }quicksort.go

Page 30: Konkurencia vs. paralelizmus

Quicksort func cquickSort(pole []int, done chan bool) { 

   if len(pole) <= 1 { 

 

   } else { 

       index := pivot(pole) 

        cquickSort(pole[:(index-1)], left) 

        cquickSort(pole[index:], right) 

   }

}

func cquickSort(pole []int, done chan bool) { 

   if len(pole) <= 1 { 

       done <- true 

   } else { 

       index := pivot(pole) 

       left, right := make(chan bool), make(chan bool) 

       go cquickSort(pole[:(index-1)], left) 

       go cquickSort(pole[index:], right) 

       done <- (<-left && <-right) 

   }

}

func cquickSort(pole []int, done chan bool) { 

   if len(pole) <= 1 { 

       done <- true 

   } else if len(pole) < granularity { 

       squickSort(pole) 

       done <- true 

   } else { 

       index := pivot(pole) 

       left, right := make(chan bool), make(chan bool) 

       go cquickSort(pole[:(index-1)], left) 

       go cquickSort(pole[index:], right) 

       done <- (<-left && <-right) 

   }

}quicksort.go

Page 31: Konkurencia vs. paralelizmus

Quicksort výsledkySize Granularity time500.000 500.000 109ms500.000 50.000 62ms500.000 5.000 78ms500.000 500 62ms500.000 50 171ms500.000 5 1375ms500.000 1 niet dosť kanálov...

Page 32: Konkurencia vs. paralelizmus

Fibonacciho agentiVyrobíme niekoľko nezávislých agentov, ktorí zipf(ch1, ch2 chan int, f func(int, int) int) chan int

spája dvojice prvkov z kanála ch1 a ch2 pomocou funkcie f (u nás +) tail(ch1 chan int) chan int

číta z kanála ch1, priamo píše do výstupu, akurát prvý prvok z ch1 zabudne func fib1() chan int

podivným spôsobom generuje fibonacciho čisla...

aj to len trochu... spliter(ch chan int) (ch1 chan int, ch2 chan int)

číta z ch, a výsledky konkurentne kopíruje do ch1 aj ch2

Page 33: Konkurencia vs. paralelizmus

Agent zip

func zipf(ch1, ch2 chan int, f func(int, int) int) chan int {

    ch := make(chan int)

    zipCount++

    go func() {

        for {

            f1 := <-ch1 // číta dvojice f1

            f2 := <-ch2 // f2 z ch1 a ch2

            ch <- f(f1, f2) // píše f(f1, f2), alias f1+f2

        }

    }()

    return ch

}

fibStream.go

Page 34: Konkurencia vs. paralelizmus

Agent tailfunc tail(ch1 chan int) chan int {

    ch := make(chan int)

    tailCount++

    <-ch1 // prvý prvok zabudne

    go func() {

        for {

            ch <- <-ch1

        }

    }()

    return ch

}

fibStream.go

Page 35: Konkurencia vs. paralelizmus

Agent fib1(katastrofické výsledky)

func fib1() chan int {

    ch := make(chan int)

    fibCount++

    go func() {

        ch <- 1

        ch <- 1

        for val := range zipf(fib1(), tail(fib1()),

func(x, y int) int { return x + y }) {

            ch <- val

        }

    }()

    return ch

}

fibStream.go

fib (fibCount, zipCount, tailCount)1 (1,0,0)1 (1,0,0)2 (7,1,3)3 (23,7,11)5 (63,31,31)8 (255,71,127)13 (1023,255,511)21 (2111,1023,1055)34 (8191,4095,4095)

Page 36: Konkurencia vs. paralelizmus

Agent splitterfunc spliter(ch chan int) (ch1 chan int, ch2 chan int) {

    ch1 = make(chan int)

    ch2 = make(chan int)

    spliterCount++

    go func() {

        for {

            val := <-ch

            // ch1 <- val deadlock! why ?

            // ch2 <- val

            go func() { ch1 <- val }()

            go func() { ch2 <- val }()

        }

    }()

    return ch1, ch2

}fibStream.go

Page 37: Konkurencia vs. paralelizmus

Agent fib(prijatelné výsledky)

func fib() chan int {

    ch := make(chan int)

    fibCount++

    go func() {

        ch <- 1

        ch <- 1

        ch1, ch2 := splitter(fib()) // použitie splittera

        for val := range zipf(ch1, tail(ch2),

func(x, y int) int { return x + y }) {

            ch <- val

        }

    }()

    return ch

}

fibStream.go

1 (1,0,0, 0)1 (1,0,0, 0)2 (5,2,4, 4)3 (8,6,7, 7)5 (12,9,11, 11)8 (15,13,14, 14)13 (19,16,18, 18)21 (22,20,21, 21)..........40.Fibonacciho číslo165580141 (138,135,137, 137)Success: process exited with code 0.