commit 493ce92e02c58ee06717b610c449060aaa864f28 Author: specCon18 Date: Mon Dec 18 03:40:05 2023 -0500 initial commit diff --git a/arrays/arrays b/arrays/arrays new file mode 100755 index 0000000..5ae8509 Binary files /dev/null and b/arrays/arrays differ diff --git a/arrays/arrays.go b/arrays/arrays.go new file mode 100644 index 0000000..05af449 --- /dev/null +++ b/arrays/arrays.go @@ -0,0 +1,32 @@ +package main + +import "fmt" + +func main(){ + //here we create an array a that will hold exactly 5 ints. + // arrays are typed and statically lengthed + // by default an array is zero-valued which for ints is just 0 in all positions + var a [5]int + fmt.Println("emp:",a) + + //we can set a value at an index using the arrray[index] = value syntax + //and get the value @ an index with the array[index] syntax + //the builtin len returns the length of an array. + a[4] = 100 + fmt.Println("set:", a) + fmt.Println("get:", a[4]) + fmt.Println("len:", len(a)) + + //the following can be used to declare and initalize the valaues + // of an array on a single line + b := [5]int{1,2,3,4,5} + fmt.Println("dcl:", b) + + var twoD [2][3]int + for i := 0; i < 2; i++ { + for j := 0; j < 3; j++ { + twoD[i][j] = i+j + } + } + fmt.Println("2d: ", twoD) +} \ No newline at end of file diff --git a/channel-buffering/chanbuffer b/channel-buffering/chanbuffer new file mode 100755 index 0000000..d14457a Binary files /dev/null and b/channel-buffering/chanbuffer differ diff --git a/channel-buffering/chanbuffer.go b/channel-buffering/chanbuffer.go new file mode 100644 index 0000000..1abb147 --- /dev/null +++ b/channel-buffering/chanbuffer.go @@ -0,0 +1,27 @@ +package main + +import "fmt" + +func main(){ + + //by default channels are unbuffered, + //meaning that they will only accept + //sends(chan <-) if there is a corresponding receive (<- chan) ready + //to receive the sent value. Buffered Channels accept a limited + //number of values without a corresponding receiver for those values + + + //here we make a channel of strings buffering up to 2 values + messages := make(chan string, 2) + + //because this channel is buffered, + //we can send these values into the channel without a + //corresponding concurrent receive + messages <- "buffered" + messages <- "channel" + + + //later we can receive these two values as usual + fmt.Println(<-messages) + fmt.Println(<-messages) +} \ No newline at end of file diff --git a/channel-directions/channeldirections b/channel-directions/channeldirections new file mode 100755 index 0000000..0abfdfc Binary files /dev/null and b/channel-directions/channeldirections differ diff --git a/channel-directions/channeldirections.go b/channel-directions/channeldirections.go new file mode 100644 index 0000000..15262d4 --- /dev/null +++ b/channel-directions/channeldirections.go @@ -0,0 +1,25 @@ +package main + +import "fmt" + +//this func only accepts a channel for sending values. +//it would be a compile-time error +//to try to receive on this channel. +func ping(pings chan<- string,msg string){ + pings <- msg +} + +//this pong function accepts one channel for receives (pings) +//and a second for sends(pongs) +func pong(pings <-chan string, pongs chan<- string) { + msg := <-pings + pongs <- msg +} + +func main() { + pings := make(chan string, 1) + pongs := make(chan string, 1) + ping(pings, "passed message") + pong(pings,pongs) + fmt.Println(<-pongs) +} \ No newline at end of file diff --git a/channel-sync/channelsync b/channel-sync/channelsync new file mode 100755 index 0000000..9f2263b Binary files /dev/null and b/channel-sync/channelsync differ diff --git a/channel-sync/channelsync.go b/channel-sync/channelsync.go new file mode 100644 index 0000000..0e99dad --- /dev/null +++ b/channel-sync/channelsync.go @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" + "time" +) +//we can use channels to synchronize execution across goroutines. +//here is an example of using a blocking receive to wait for a goroutine +//to finish whenwaiting for multiple goroutines to finish you may prefer to use a WaitGroup + + +//This is the function we'll run in a goroutine. +//the done channel will be used to notify another +//goroutine that this function's work is done +func worker(done chan bool) { + fmt.Print("working...") + time.Sleep(time.Second) + fmt.Println("done") + + //start a worker goroutine, giving it the channel to notify on. + done <- true +} + +func main(){ + done := make(chan bool,1) + go worker(done) + + //block until we receive a notification from the worker on the channel + <-done +} \ No newline at end of file diff --git a/channels/channels b/channels/channels new file mode 100755 index 0000000..6729263 Binary files /dev/null and b/channels/channels differ diff --git a/channels/channels.go b/channels/channels.go new file mode 100644 index 0000000..c15419a --- /dev/null +++ b/channels/channels.go @@ -0,0 +1,22 @@ +package main + +import "fmt" + + +//Channels are the pipes that connect concurrent goroutines. +//you can send values into channels +//form one goroutine and receive those values into another goroutine +func main(){ + //create a new channel with make(chan val-type) + //Channels are typed bt the values they convey + messages := make(chan string) + + //send a value into a channel using the channel <- syntax. + //here we send "ping" to the messages channel we made above, from a new goroutine + go func() { messages <- "ping"}() + + //the <- channel syntax receives a value from the channel. + // here we'll receive the "ping" message we sent above and print it out + msg := <- messages + fmt.Println(msg) +} \ No newline at end of file diff --git a/closures/closures b/closures/closures new file mode 100755 index 0000000..004b255 Binary files /dev/null and b/closures/closures differ diff --git a/closures/closures.go b/closures/closures.go new file mode 100644 index 0000000..2eccda6 --- /dev/null +++ b/closures/closures.go @@ -0,0 +1,30 @@ +package main + +import "fmt" + +// This function returns another function, +// which we define anonymously in the body of the intSeq. +// The returned function closes over the variable i to form a closure +func intSeq() func() int { + i := 0 + return func() int { + i++ + return i + } +} + +func main(){ + //we call intSeq assigning the result(a function) to nextInt. + // This function value captures its own i value, which will be updated + // each time we call nextInt + nextInt := intSeq() + + // see the effect of the closure by calling nextInt a few times. + fmt.Println(nextInt()) + fmt.Println(nextInt()) + fmt.Println(nextInt()) + + // to confirm that the state is unique to the instance create a new instance + newInts := intSeq() + fmt.Println(newInts()) +} \ No newline at end of file diff --git a/constants/constants b/constants/constants new file mode 100755 index 0000000..e0f6ce3 Binary files /dev/null and b/constants/constants differ diff --git a/constants/constants.go b/constants/constants.go new file mode 100644 index 0000000..714974c --- /dev/null +++ b/constants/constants.go @@ -0,0 +1,24 @@ +package main + +import ( + "fmt" + "math" +) + +//const declares a constant value. +const s string = "constant" + +func main(){ + fmt.Println(s) + // A const statement can appear anywhere a var statement can. + const n = 500000000 + // Constant expressions perform arithmetic with arbitrary precision. + const d = 3e20 / n + // A numeric constant has no type until it’s given one, such as by an explicit conversion. + fmt.Println(int64(d)) + // A number can be given a type by using it + // in a context that requires one, such + // as a variable assignment or function call. + // For example, here math.Sin expects a float64. + fmt.Println(math.Sin(n)) +} \ No newline at end of file diff --git a/errors/errors b/errors/errors new file mode 100755 index 0000000..0530529 Binary files /dev/null and b/errors/errors differ diff --git a/errors/errors.go b/errors/errors.go new file mode 100644 index 0000000..7a66dd0 --- /dev/null +++ b/errors/errors.go @@ -0,0 +1,68 @@ +package main + +import ( + "errors" + "fmt" +) + +//In Go its idiomatic to communicate errors via an explicit, seperate return value. +//this contrasts with the exceptions used in languages like java and Ruby +//and the verloaded single result/error value sometimes used in C. +//Go's approach makes it easy to see which functions return errors and to +//handle them using the same language constructs employed for any other non-error tasks + + +// by convention, errors are the last return value and have type error, a builtin interface +func f1(arg int) (int,error){ + if arg == 42 { + //errors.New constructs a basic error value with the given error message + return -1, errors.New("cant work with 42") + } + //a nil value in the error position indicates that there was no error + return arg + 3, nil +} + +// its possible to use custom types as errors by implementing Error() method on them. +// Heres a variant on the example above that uses a custom error type +type argError struct { + arg int + prob string +} + +func (e *argError) Error() string { + return fmt.Sprintf("%d - %s", e.arg, e.prob) +} + +func f2(arg int) (int,error) { + if arg == 42 { + return -1, &argError{arg, "can't work with it"} + } + return arg+3,nil +} + +func main(){ + //The two loops below test out each of our error-returning + //functions. Note that the use of an inline error check on the if line is a common idiom in Go code. + for _,i := range []int{7,42} { + if r,e :=f1(i); e != nil { + fmt.Println("f1 failed:",e) + } else { + fmt.Println("f1 worked:",r) + } + } + for _,i := range []int{7,42} { + if r,e := f2(i);e != nil { + fmt.Println("f2 failed:",e) + } else { + fmt.Println("f2 worked:",r) + } + } + + //if you want to programmatically use the data in a custom error, you'll need to get the error + // as an instance of the custom error type via type assertion + _, e := f2(42) + if ae, ok:= e.(*argError); ok { + fmt.Println(ae.arg) + fmt.Println(ae.prob) + } +} \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..3bfd004 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1702312524, + "narHash": "sha256-gkZJRDBUCpTPBvQk25G0B7vfbpEYM5s5OZqghkjZsnE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a9bf124c46ef298113270b1f84a164865987a91c", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..18b6bfe --- /dev/null +++ b/flake.nix @@ -0,0 +1,18 @@ +{ + description = "Go development environment"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + outputs = { self, flake-utils, nixpkgs }: + flake-utils.lib.eachDefaultSystem (system: + let pkgs = nixpkgs.legacyPackages.${system}; in + { + devShells.default = with pkgs; mkShell { + packages = [ + go + ]; + shellHook = '' + ''; + }; + } + ); +} \ No newline at end of file diff --git a/for/for.go b/for/for.go new file mode 100644 index 0000000..6d60b93 --- /dev/null +++ b/for/for.go @@ -0,0 +1,32 @@ +package main + +import "fmt" + +func main(){ + i := 1 + + // for with a single condition + for i <= 3 { + fmt.Println(i) + i = i+1 + } + + // Initial/Condition/After classic style loop + for j := 7; j <= 9; j++ { + fmt.Println(j) + } + + // due to go not having any other loop for with no condition == while + for { + fmt.Println("loop") + break + } + + //continue passes to the next iter of the loop + for n := 0; n <= 5; n++ { + if n%2 == 0 { + continue + } + fmt.Println(n) + } +} \ No newline at end of file diff --git a/functions/functions b/functions/functions new file mode 100755 index 0000000..4a33ca8 Binary files /dev/null and b/functions/functions differ diff --git a/functions/functions.go b/functions/functions.go new file mode 100644 index 0000000..890e7c9 --- /dev/null +++ b/functions/functions.go @@ -0,0 +1,25 @@ +package main + +import "fmt" + +// here is a function that takes two ints and sums them +// then returns them as an int +// go requires explicit returns meaning it wont +// automatically return the value of the last expression +func plus(a int,b int) int { + return a+b +} + +//when you have multiple consecutive params with the same type they can be type declared as such: +func plusPlus(a,b,c int) int { + return a+b+c +} + +//call functions like you would expect +func main(){ + res := plus(1,2) + fmt.Println("1+2=",res) + + res = plusPlus(1,2,3) + fmt.Println("1+2+3=",res) +} \ No newline at end of file diff --git a/generics/generics b/generics/generics new file mode 100755 index 0000000..daefa29 Binary files /dev/null and b/generics/generics differ diff --git a/generics/generics.go b/generics/generics.go new file mode 100644 index 0000000..e668a5b --- /dev/null +++ b/generics/generics.go @@ -0,0 +1,66 @@ +package main + +import "fmt" + +//as an example of a generic function, +//MapKeys takes a map of any type and returns a slice of its keys. +//this function has two type parameters - K and V; K has the comparable constraint +//meaning that we can compare values of this type with the == and != operators +//this is required for map keys in Go V has the any constraint meaning that its +// not restricted in any way(any is an alias for interface{}) +func MapKeys[K comparable, V any](m map [K]V) []K { + r := make([]K,0,len(m)) + for k := range m { + r = append(r,k) + } + return r +} + + +//as example of a generic type, List is a singly-linked list with values of any type +type List[T any] struct { + head, tail *element[T] +} + +type element[T any] struct { + next *element[T] + val T +} + +//We can define methods on generic types just like we do on regular types, +// but we have to keep the type parameters in place. +// the type is List[T] not List +func (lst *List[T]) Push(v T) { + if lst.tail == nil { + lst.head = &element[T]{val:v} + lst.tail = lst.head + } else { + lst.tail.next = &element[T]{val: v} + lst.tail = lst.tail.next + } +} + +func (lst *List[T]) GetAll() []T { + var elems []T + for e := lst.head; e != nil; e = e.next { + elems = append(elems, e.val) + } + return elems +} + +func main() { + var m = map[int]string{1: "2",2:"4",4:"8"} + + //When invoking genric functions, we can often + // rely on type inference. note that we dont have to specify + // the types for K and V when calling MapKeys - the compiler infers them automatically + fmt.Println("keys:",MapKeys(m)) + //though we could be explicit + _ = MapKeys[int,string](m) + + lst := List[int]{} + lst.Push(10) + lst.Push(13) + lst.Push(23) + fmt.Println("list:",lst.GetAll()) +} \ No newline at end of file diff --git a/goroutines/goroutines b/goroutines/goroutines new file mode 100755 index 0000000..087ff85 Binary files /dev/null and b/goroutines/goroutines differ diff --git a/goroutines/goroutines.go b/goroutines/goroutines.go new file mode 100644 index 0000000..1b5f0d6 --- /dev/null +++ b/goroutines/goroutines.go @@ -0,0 +1,33 @@ +package main + +import ( + "fmt" + "time" +) + +func f (from string){ + for i := 0; i<3; i++ { + fmt.Println(from,":",i) + } +} + +func main(){ + //suppose we have function call f(s) + //here is how wed call that in the usual way + //running it synchronously + f("direct") + + //To invoke this function in a goroutine, use go f(s) + //This new goroutine will execute concurrently with the calling one + go f("goroutine") + + //you can also start a goroutine for an anonymous function call + go func (msg string) { + fmt.Println(msg) + }("going") + + //our two function calls are running async in seperate goroutines now. + //wait for them to finish (for a most robust approach use a WaitGroup) + time.Sleep(time.Second) + fmt.Println("done") +} \ No newline at end of file diff --git a/hello-world/hello-world b/hello-world/hello-world new file mode 100755 index 0000000..14fe667 Binary files /dev/null and b/hello-world/hello-world differ diff --git a/hello-world/hello-world.go b/hello-world/hello-world.go new file mode 100644 index 0000000..46a5236 --- /dev/null +++ b/hello-world/hello-world.go @@ -0,0 +1,5 @@ +package main +import "fmt" +func main(){ + fmt.Println("hello world") +} diff --git a/if-else/if-else b/if-else/if-else new file mode 100755 index 0000000..7aafb30 Binary files /dev/null and b/if-else/if-else differ diff --git a/if-else/if-else.go b/if-else/if-else.go new file mode 100644 index 0000000..90bc162 --- /dev/null +++ b/if-else/if-else.go @@ -0,0 +1,35 @@ +package main + +import "fmt" + +func main(){ + + //There are no ternary ifs in go + + //basic if else + if 7%2 == 0 { + fmt.Println("7 is even") + } else { + fmt.Println("7 is odd") + } + //if doesnt need else + if 8%4 == 0 { + fmt.Println("8 is divisible by 4") + } + + //You can use logical operators in if such as && and || + if 7%2 == 0 || 8%2 == 0 { + fmt.Println("either 8 or 7 are even") + } + + //a statement can precede conditionals; + // any variables declared in the statement + //are avalible in the current and all subsequent branches. + if num := 9; num < 0 { + fmt.Println(num, "is negative") + } else if num < 10 { + fmt.Println(num, "has 1 digit") + } else { + fmt.Println(num, "has multiple digits") + } +} \ No newline at end of file diff --git a/interfaces/interfaces b/interfaces/interfaces new file mode 100755 index 0000000..11253aa Binary files /dev/null and b/interfaces/interfaces differ diff --git a/interfaces/interfaces.go b/interfaces/interfaces.go new file mode 100644 index 0000000..16f2d9e --- /dev/null +++ b/interfaces/interfaces.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + "math" +) +// here is a basic interface for geometric shapes +type geometry interface { + area() float64 + perim() float64 +} + +//for our example well implement this interface on rect and circle types +type rect struct { + width,height float64 +} +type circle struct { + radius float64 +} + +// to implement an interface in Go, +//we just need to implement all the methods in the interface +//here we do it for rectangles +func (r rect) area() float64 { + return r.width * r.height +} +func (r rect) perim() float64 { + return 2*r.width + 2*r.height +} +//here we do it for circles +func (c circle) area() float64 { + return math.Pi * c.radius * c.radius +} +func (c circle) perim() float64 { + return 2*math.Pi*c.radius +} + +//if a variable has an interface type, +// then we can call methods that are in the named interface +//here's a generic measure function taking advantage of this +//to work on any geometry +func measure(g geometry) { + fmt.Println(g) + fmt.Println(g.area()) + fmt.Println(g.perim()) +} + +func main(){ + r := rect{width: 3,height: 4} + c := circle{radius:5} + + measure(r) + measure(c) +} \ No newline at end of file diff --git a/maps/maps b/maps/maps new file mode 100755 index 0000000..e2a6003 Binary files /dev/null and b/maps/maps differ diff --git a/maps/maps.go b/maps/maps.go new file mode 100644 index 0000000..c0ee1ba --- /dev/null +++ b/maps/maps.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + "maps" +) + +func main() { + // to create an empty map, use the builtin make: + // make(map[key-type]val-type) + m := make(map[string]int) + + //set key/value pairs using typical name[key] = val syntax + m["k1"] = 7 + m["k2"] = 13 + + // printing a map with e.g. + // fmt.Println will show all of its key/value pairs + fmt.Println("map:",m) + + // Get a value from a key with name[key] + // if the key doesn't exist the zero value of the value type is returned + + v1 := m["k1"] + fmt.Println("v1:",v1) + + v3 := m["k3"] + fmt.Println("v3:",v3) + + //the builtin len returns the number of key/value pairs when called on a map + fmt.Println("len:", len(m)) + + //the builtin delete removes key/value pairs from a map, + //use the clear builtin to remove all key/value pairs from a map. + delete(m, "k2") + fmt.Println("map:",m) + + clear(m) + fmt.Println("map:",m) + + // the optional second return value when getting the value + // from a map indicates if the key was present in the map allowing + // to diferentiate between default zero-values and values acutally stored as 0 + // here we dont need the value itself so we ignore it with the _ or blank identifier + _, prs := m["k2"] + fmt.Println("prs:",prs) + + //you can declare and initalize a new map in the same line with the following syntax: + n := map[string]int{"foo": 1,"bar": 2} + fmt.Println("map:", n) + + //the map package also contains useful funcs for map such as .Equal + n2 := map[string]int{"foo": 1,"bar":2} + if maps.Equal(n,n2){ + fmt.Println("n == n2") + } +} \ No newline at end of file diff --git a/methods/methods b/methods/methods new file mode 100755 index 0000000..7f75175 Binary files /dev/null and b/methods/methods differ diff --git a/methods/methods.go b/methods/methods.go new file mode 100644 index 0000000..7850af7 --- /dev/null +++ b/methods/methods.go @@ -0,0 +1,34 @@ +package main + +import "fmt" + +type rect struct { + width, height int +} + +//this area method has a reciever type of *rect +func (r *rect) area() int { + return r.width * r.height +} + +// Methods can be defined for either pointer or value reciever types +// heres an example of a value receiver +func (r rect) perim() int { + return 2*r.width + 2*r.height +} + +func main() { + r := rect{width:10,height:5} + + //here we call the two methods defined for our struct + fmt.Println("area:",r.area()) + fmt.Println("perim:",r.perim()) + + //Go automatically handles conversion between values and pointers for method calls. + //you may want to use a pointer receiver type to avoid copying on method calls or to allow + //the method to mutate the receiving struct + + rp := &r + fmt.Println("area:",rp.area()) + fmt.Println("perim:",rp.perim()) +} \ No newline at end of file diff --git a/multi-return-values/mrv b/multi-return-values/mrv new file mode 100755 index 0000000..d0d6516 Binary files /dev/null and b/multi-return-values/mrv differ diff --git a/multi-return-values/mrv.go b/multi-return-values/mrv.go new file mode 100644 index 0000000..0dc3398 --- /dev/null +++ b/multi-return-values/mrv.go @@ -0,0 +1,18 @@ +package main + +import "fmt" + +//the (int,int) in the func sig shows that the func returns 2 ints +func vals()(int,int){ + return 3,7 +} + +func main(){ + //here we use the 2 different values from the call with multiple assignment + a,b := vals() + fmt.Println(a) + fmt.Println(b) + //if you only want a subset of the returned values use the blank identifier + _,c := vals() + fmt.Println(c) +} \ No newline at end of file diff --git a/pointers/pointers b/pointers/pointers new file mode 100755 index 0000000..c577de1 Binary files /dev/null and b/pointers/pointers differ diff --git a/pointers/pointers.go b/pointers/pointers.go new file mode 100644 index 0000000..aa9a0a8 --- /dev/null +++ b/pointers/pointers.go @@ -0,0 +1,37 @@ +package main + +import "fmt" + +// go supports pointers allowing you to pass refs to values and records +// in your program + + +//this func takes in an int as a param +func zeroval(ival int){ + ival = 0 +} + +//this func takes in an pointer to an int as a parameter +func zeroptr (iptr *int) { + *iptr = 0 +} + +func main() { + //display initial value of i + i := 1 + fmt.Println("initial:",i) + + //display the value of i after passing to zeroval + zeroval(i) + fmt.Println("zeroval:",i) + + //display the value of the data @ the memory address of i aka i's pointer + zeroptr(&i) + fmt.Println("zeroptr:",i) + + //pointers can be printed as well and will return their memory address + fmt.Println("pointer:",&i) + + //zeroval doesnt change the val of i but zeroptr does because it edits the data at the memory address + +} \ No newline at end of file diff --git a/range/range b/range/range new file mode 100755 index 0000000..0c4ea4f Binary files /dev/null and b/range/range differ diff --git a/range/range.go b/range/range.go new file mode 100644 index 0000000..8122d7d --- /dev/null +++ b/range/range.go @@ -0,0 +1,37 @@ +package main + +import "fmt" + +func main(){ + //here we use range to sum the numbers in a slice + nums := []int{2,3,4} + sum := 0 + for _,num := range nums { + sum += num + } + fmt.Println("sum:",sum) + + //range on arrays and slices provides both the index and value + // at each entry. above we didn't need the index, so we ignored it with the blank identifier + // but if we want the index we can use it like bellow + for i, num := range nums { + if num == 3 { + fmt.Println("index:",i) + } + } + //range on map iterates over the key value pairs. + kvs := map[string]string{"a":"apple","b":"banana"} + for k,v := range kvs{ + fmt.Printf("%s -> %s\n",k,v) + } + //range can also iterate over just the keys of a map + for k := range kvs{ + fmt.Println("key:",k) + } + // range on strings iterates over Unicode code points. + // the first value is the starting byte index of the rune + //and the second is the rune itself. + for i,c := range "go"{ + fmt.Println(i,c) + } +} \ No newline at end of file diff --git a/recursion/recursion b/recursion/recursion new file mode 100755 index 0000000..9ab4cb3 Binary files /dev/null and b/recursion/recursion differ diff --git a/recursion/recursion.go b/recursion/recursion.go new file mode 100644 index 0000000..95f776d --- /dev/null +++ b/recursion/recursion.go @@ -0,0 +1,30 @@ +package main + +import "fmt" + +//this calls itself until it reaches the base case of fact(0) +func fact(n int) int { + if n == 0 { + return 1 + } + return n * fact(n-1) +} + +func main() { + fmt.Println(fact(7)) + + // closures can also be recursive, + // but this requires the closure to be declared with + // a typed var explicitly before its defined + var fib func(n int) int + + fib = func(n int) int { + if n < 2 { + return n + } + return fib(n-1) + fib(n-2) + } + + //simce fib was previously declared in main Go knows which function to call with fib here + fmt.Println(fib(7)) +} \ No newline at end of file diff --git a/select/select b/select/select new file mode 100755 index 0000000..943cc17 Binary files /dev/null and b/select/select differ diff --git a/select/select.go b/select/select.go new file mode 100644 index 0000000..20f82eb --- /dev/null +++ b/select/select.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "time" +) +//Go's select lets you wait on multiple channel +//operations combining goroutines and channels with select +//is a powerful feature of go +func main(){ + //For our example well select across two channels. + c1 := make(chan string) + c2 := make(chan string) + + + //each channel will receive a value after some + //amount of time, to simulate e.g. blocking RPC + //operations executing in concurrent goroutines + go func(){ + time.Sleep(1*time.Second) + c1 <- "one" + }() + go func(){ + time.Sleep(2*time.Second) + c2 <- "two" + }() + + + //we'll use select to await both of these values + //simultaneously, printing each one as it arrives. + for i := 0; i < 2; i++ { + select{ + case msg1 := <-c1: + fmt.Println("received", msg1) + case msg2 := <-c2: + fmt.Println("received", msg2) + } + } +} \ No newline at end of file diff --git a/slices/slices b/slices/slices new file mode 100755 index 0000000..04e57ba Binary files /dev/null and b/slices/slices differ diff --git a/slices/slices.go b/slices/slices.go new file mode 100644 index 0000000..b9db7a1 --- /dev/null +++ b/slices/slices.go @@ -0,0 +1,76 @@ +package main + +import ( + "fmt" + "slices" +) + +func main(){ + // Unlike arrays, slices are typed only by the elements they contain. + // not the number of elements. + // an uninitalized slice equals to nil and has a len of 0 + var s []string + fmt.Println("uninit:", s, s == nil, len(s) == 0) + + // to create an emply slice with non-zero length, + // use the builtin make. here we make a slice of strings of length 3 (initally zero-valued) + // by default a new slice's capacity == its len if we know the slice is going to grow ahead of time + // its possible to pass a capacity explicitly as an additional parameter to make. + s = make([]string, 3) + fmt.Println("emp:", s, len(s), "cap:", cap(s)) + + //setting and getting vars works just like arrays + s[0] = "a" + s[1] = "b" + s[2] = "c" + fmt.Println("set:", s) + fmt.Println("get:", s[2]) + // len reutrns the length of the slice as expected. + fmt.Println("len:", len(s)) + // in addition to these basic opertaions, + // slices support several more than make them richer than arrays. + // One is the builtin append which returns a slice containing + // one or more new values. Note that we need to accept a return value + //from append as we make get a new slice value. + s = append(s, "") + s = append(s, "e", "f") + + // slices can be copy'd + // below we create an empty slice c + // of the same len as s and copy into c from s + c := make([]string, len(s)) + copy(c,s) + fmt.Println("cpy:",c) + + //slices also support a slice operator with the syntax slice[low:high]. + // for example this gets a slice of the elements s[2],s[3],s[4] + l := s[2:5] + fmt.Println("sl1", l) + + l = s[:5] + fmt.Println("sl2", l) + + l = s[2:] + fmt.Println("sl3", l) + + //single line declaration and initalization of a slice + t := []string{"g","h","i"} + + //the slices package has plenty of useful funcs for slices like .Equal + t2 := []string{"g","h","i"} + if slices.Equal(t,t2){ + fmt.Println("t == t2") + } + + // Slices can be composed into multi-dimensional data structues, + // the length of the inner slices can vary, unlike with multi-dimensional arrays. + twoD := make([][]int, 3) + for i := 0; i < 3; i ++ { + innerLen := i + 1 + twoD[i] = make([]int, innerLen) + for j := 0; j < innerLen; j++ { + twoD[i][j] = i + j + } + } + fmt.Println("2d: ", twoD) +} \ No newline at end of file diff --git a/strings-and-runes/stringsandrunes b/strings-and-runes/stringsandrunes new file mode 100755 index 0000000..845fbbe Binary files /dev/null and b/strings-and-runes/stringsandrunes differ diff --git a/strings-and-runes/stringsandrunes.go b/strings-and-runes/stringsandrunes.go new file mode 100644 index 0000000..7b53d5a --- /dev/null +++ b/strings-and-runes/stringsandrunes.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "unicode/utf8" +) + +func main() { + // s is a string assigned a literal value representing the + // word "hello" in the Thai language. + // Go string literals are UTF-8 encoded text. + const s = "สวัสดี" + //Since strings are equivalent to []byte, + //this will produce the length of the raw bytes stored within + fmt.Println("len:", len(s)) + + // indexing into a string produces the raw byte values at each index. + // This loop generates the hex values of all the bytes + // that constitute the code points in s. + for i := 0; i < len(s); i++ { + fmt.Printf("%x ", s[i]) + } + fmt.Println() + // To count how many runes are in a string, + // we ca can use the utf8 package. Note that + // the run-time of RuneCountInString depends on the size of the string + // due to having to decode each rune sequentially. + // Some Thai characters are represented by multiple UTF-8 code points, + // so the result of this count may be suprising. + fmt.Println("Rune count:",utf8.RuneCountInString(s)) + // A range loop handles strings specially and decodes each rune + // along with it's offset in the string + for idx, runeValue := range s { + fmt.Printf("%#U Starts at %d\n", runeValue, idx) + } + //we can achieve the same iteration by using the utf8.DecodeRuneInString function explicitly + fmt.Println("\nUsing DecodeRuneInString") + for i,w := 0,0; i < len(s); i += w { + runeValue, width := utf8.DecodeRuneInString(s[i:]) + fmt.Printf("%#U starts at %d\n", runeValue, i) + w = width + // This demonstrates passing a rune to a func + examineRune(runeValue) + } +} + +// Values enclosed in single quotes are rune literals. +// we can compare a rune value to a rune literal directly. +func examineRune(r rune) { + if r == 't' { + fmt.Println("found tee") + } else if r == 'ส' { + fmt.Println("found so sua") + } +} \ No newline at end of file diff --git a/struct-embedding/structembed b/struct-embedding/structembed new file mode 100755 index 0000000..825127c Binary files /dev/null and b/struct-embedding/structembed differ diff --git a/struct-embedding/structembed.go b/struct-embedding/structembed.go new file mode 100644 index 0000000..85dc1e5 --- /dev/null +++ b/struct-embedding/structembed.go @@ -0,0 +1,54 @@ +package main + +import "fmt" + + +//Go struct embedding allows for structs to be embedded into interfaces +// to express a more seamless composition of types. +type base struct { + num int +} + +func (b base) describe() string{ + return fmt.Sprintf("base with num=%v",b.num) +} + +//a container embeds a base +//an embedding looks like a field without a name +type container struct { + base + str string +} + + +func main(){ + + //when creating structs with literals, we have to + //initalize the embedding explicitly; + // here the embedded types serve as the field name + co := container{ + base: base { + num: 1, + }, + str: "some name", + } + + //we can access the base's fields directly on co, e.g. co.num + fmt.Printf("co={num: %v str: %v}\n", co.num, co.str) + + //alternativly we can spell out the full path using the embedded type name + fmt.Println("also num:", co.base.num) + //since container embeds base, the methods of base also become the methods of container + //here we invoke a method that was embedded from base directly on co + fmt.Println("describe:", co.describe()) + + //Embedding structs with methods may be used + // to bestow interface implementations onto other structs. + // here we see that container now implements the describer interface because it + // embeds base + type describer interface { + describe() string + } + var d describer = co + fmt.Println("describer:", d.describe()) +} \ No newline at end of file diff --git a/structs/structs b/structs/structs new file mode 100755 index 0000000..aace575 Binary files /dev/null and b/structs/structs differ diff --git a/structs/structs.go b/structs/structs.go new file mode 100644 index 0000000..1946d58 --- /dev/null +++ b/structs/structs.go @@ -0,0 +1,58 @@ +package main + +import "fmt" +// Structs are typed collections of fields. +// they're useful for grouping data together to form records +// the person struct type has name and age fields +type person struct { + name string + age int +} + +// newPerson constructs a new person struct with the given name +// you can safely return a pointer to local variable as a local variable +// will survive the scope of the func +func newPerson(name string) *person { + p := person{name: name} + p.age = 42 + return &p +} + +func main(){ + // This syntax creates a new struct + fmt.Println(person{"Bob", 20}) + //you can name the fields when initalizing the struct + fmt.Println(person{name: "Alice", age: 30}) + //omitted fields will be zero-valued + fmt.Println(person{name:"Fred"}) + //an & prefix yields a pointer to the struct + fmt.Println(&person{name:"Ann",age: 40}) + //its idiomatic to encapsulate new struct creation in constructor functions + fmt.Println(newPerson("Jon")) + + //access struct fields with . notation + s := person{name: "Sean", age: 50} + fmt.Println(s.name) + + // you can also use . notation with struct pointers - the pointers are + // automatically dereferenced + sp := &s + fmt.Println(sp.age) + + // structs are mutable + sp.age = 51 + fmt.Println(sp.age) + + // if a struct type is only used for a single value + // we dont have to give it a name + // the value can have an anonymous struct type. + // this tech is used commonly in table driven tests + dog := struct { + name string + isGood bool + }{ + "Rex", + true, + } + fmt.Println(dog) +} \ No newline at end of file diff --git a/switch/switch b/switch/switch new file mode 100755 index 0000000..3c3565e Binary files /dev/null and b/switch/switch differ diff --git a/switch/switch.go b/switch/switch.go new file mode 100644 index 0000000..0fd53fd --- /dev/null +++ b/switch/switch.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + + // Basic switch default is optional + i := 2 + fmt.Print("Write", i, " as ") + switch i { + case 1: + fmt.Println("one") + case 2: + fmt.Println("two") + case 3: + fmt.Println("three") + } + + // Commas can seperate multiple expressions in the same case statement. + switch time.Now().Weekday() { + case time.Saturday, time.Sunday: + fmt.Println("It's the weekend") + default: + fmt.Println("It's a weekday") + } + + //switch without an expression is a alternate way to express if/else logic + t := time.Now() + switch { + case t.Hour() < 12: + fmt.Println("It's before noon") + default: + fmt.Println("It's afte noon") + } + + // a type switch compares types instead of values. + //you can use this to discorver the type of an interface value. + whatAmI := func(i interface{}){ + switch t := i.(type) { + case bool: + fmt.Println("I'm a bool") + case int: + fmt.Println("I'm an int") + default: + fmt.Printf("Don't know the type %T\n",t) + } + } + whatAmI(true) + whatAmI(1) + whatAmI("hey") +} \ No newline at end of file diff --git a/timeouts/timeouts.go b/timeouts/timeouts.go new file mode 100644 index 0000000..59ad09d --- /dev/null +++ b/timeouts/timeouts.go @@ -0,0 +1,10 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + +} \ No newline at end of file diff --git a/values/values b/values/values new file mode 100755 index 0000000..2e6ea32 Binary files /dev/null and b/values/values differ diff --git a/values/values.go b/values/values.go new file mode 100644 index 0000000..a2fedb5 --- /dev/null +++ b/values/values.go @@ -0,0 +1,20 @@ +package main + +import "fmt" + +func main(){ + // Strings which can be concatinated by + + fmt.Println("go"+"lang") + // Strings can be iterpolated with a result + // of an expression using fmt.Println and + // seperating the expression and string by a , + // in this case the string is interpolated with + // a sum of ints and sum of floats + fmt.Println("1+1=",1+1) + fmt.Println("7.0/3.0=",7.0/3.0) + + // Booleans and boolean operators + fmt.Println(true && false) + fmt.Println(true || false) + fmt.Println(!true) +} \ No newline at end of file diff --git a/variables/variables b/variables/variables new file mode 100755 index 0000000..96dc396 Binary files /dev/null and b/variables/variables differ diff --git a/variables/variables.go b/variables/variables.go new file mode 100644 index 0000000..c6bad5c --- /dev/null +++ b/variables/variables.go @@ -0,0 +1,27 @@ +package main +import "fmt" + +func main(){ + // var declares 1 or more variables. + var a = "initial" + fmt.Println(a) + // You can declare multiple variables at once. + var b,c int = 1,2 + fmt.Println(b,c) + + // Go will infer the type of initialized variables. + var d = true + fmt.Println(d) + + // Variables declared without a corresponding + // initialization are zero-valued. For example, + // the zero value for an int is 0. + var e int + fmt.Println(e) + // The := syntax is shorthand for declaring and + // initializing a variable, e.g. + // for var f string = "apple" in this case. + // This syntax is only available inside functions. + f := "apple" + fmt.Println(f) +} \ No newline at end of file diff --git a/variadic-functions/vfunc b/variadic-functions/vfunc new file mode 100755 index 0000000..9c82ccd Binary files /dev/null and b/variadic-functions/vfunc differ diff --git a/variadic-functions/vfunc.go b/variadic-functions/vfunc.go new file mode 100644 index 0000000..b8bec98 --- /dev/null +++ b/variadic-functions/vfunc.go @@ -0,0 +1,20 @@ +package main + +import "fmt" + +// below is a func that takes in an arbitrary number of ints as args +func sum(nums ...int) { + fmt.Print(nums, " ") + total := 0 + for _, num := range nums { + total += num + } + fmt.Println(total) +} + +func main(){ + sum(1,2) + sum(1,2,3) + nums := []int{1,2,3,4} + sum(nums...) +} \ No newline at end of file