Yeah, it's not going to be easy

Search

Search IconIcon to open search

Tucker's Golang programming Ch18 ~ Ch22

Oct 21, 2023

This post is a translation of the original Korean post.


This text is a summary of chapters 18 to 22 of the book “Golden Rabbit: [ Tucker’s Golang programming ]”.

Ch18 ~ Ch22

  • Slice
  • Method
  • Interface
  • Function
  • Data Structure

# slice

Dynamic Arrays

  • A data structure that automatically increases the size of an array

Declaration

1
2
3
4
5
6
var slice []int

// Initialize
var slice1 = []int{1, 2, 3}

var slice2 = make([]int, 3)

# How slices work

Internal implementation

1
2
3
4
5
6
7
type SliceHeader struct {
	Data uintptr // Pointer to the actual array
	Len  int     // Number of elements
	Cap  int     // actual array length
}

var slice = make([]int, 3, 5) // Create a slice with len:3, cap: 5

make() function

Allocates memory for heap and initializes it with zero value

  • Used for slices, maps, and channels


Adding an element

Using the append() function

1
slice2 := append(slice, 3, 4, 5)
  • Returns a new slice with the element added (same array pointer, len, cap)

internal behavior

  • Check how much free space is left (cap - len)
    • empty space >= number of elements added
      • add to existing slice array as is
      • change only len
    • Empty space < number of elements added
      • create an array twice the size of the existing slice array
      • copy all existing slice array elements to new array and add elements to it
      • Replace array pointer with pointer to new array
      • Change array pointer, len, cap all

# Slicing

Pick up a portion of the array and slice and return it as a slice

1
slice[startindex:endindex(:maxindex)] // contains up to one endindex
  • array pointer: points to a part of an existing array
  • len: the extracted length
  • cap: length from the start index to the end of the array (slice)
    • When defining maxindex: Maxindex - startindex.

# Duplicate a slice

Duplicate into slices that point to different arrays with the same values.

Using the append() function

1
slice2 := append([]int{}, slice1...)

… keyword

Unpack slices into elements and pass them as arguments


Using the copy() function

1
2
3
func copy(dst, src []Type) int // Actual number of elements copied (max: len of dst)

cnt := copy(slice2, slice1)

# Add and delete slice elements

Delete an element

1
slice = append(slice[:idx], slice[idx+1:]...)

Add an element

1
2
3
4
5
6
7
slice = append(slice[:idx], append([]int{100}, slice[idx+1:]...)...)


// without temporary slices
slice = append(slice, 0) // add element at the end
copy(slice[idx+1:], slice[idx:])
slice[idx] = 100

# Sorting the slices

Using the default built-in package, the sort package

  • Requires slices to implement Len(), Less(), Swap() methods (using alias types)

example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type Students []Student

func (s Students) Len() int { return len(s) }
func (s Students) Less(i, j int) bool { return s[i].Age < s[j].Age }
func (s Students) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

...

// sort
sort.Sort(Students(s))

# Method

A type of function that belongs to a structure

Declaration

1
2
3
func (r Rabbit) info() int { // receiver, method name
	return r.width * r.height
}

receiver

Tells you what type the method belongs to

  • Struct, alias types

# Pointer methods vs value type methods

Value type receivers

All values of the receiver type are copied

  • Another object when the object’s properties change

Pointer receiver

The address value of the memory the pointer is pointing to is copied

  • Same object when object properties change

# Interface

A set of methods that does not contain an implementation

Declaration

1
2
3
4
type DuckInterface interface {
	Fly() // method set
	...
}

# Duck typing

Implementing an interface

  • Determined by including methods defined by the interface
  • Not explicitly indicated

Service user-centered coding

  • Interface is created and used by the user, not the service provider

💡 Create a relationship with a materialized object and use it later by creating an interface

# interface{}

Any type can be used because there are no methods to have

Type switch

switch t := v.(type)

  • Compares types instead of values
  • used for type-specific processing using an empty interface

# Type conversion

Use

1
2
3
4
5
// Convert a type to a concrete type
t := a.(ConcreteType) // compile-time error if interface is not implemented

// Cast to another interface
b := a.(BInterface)

❗Runtime error

Converting a type to a materialized type

  • when converting to a different materialized type that implements the same interface

Converting a type to a different interface

  • if the materialized type in the interface does not implement the interface being converted


Returns whether type conversion succeeded or failed

1
t, ok := a.(ConcreteType) // Return the default value of ConcreteType if conversion fails
  • No runtime errors

# Function

# Variable-argument function

Functions with an undetermined number of arguments

  • Use the ... keyword to indicate that it is a variable argument that takes multiple arguments of that type
  • Variable arguments are treated as slice types inside the function
1
2
3
4
// example
func sum(nums ...int) int

func Print(args ...interface{}) string // Empty interfaces allow you to mix and match types

# defer delayed execution

1
defer statement
  • Execute immediately before the function ends
  • Execute multiple defer declarations in reverse order, starting with the last defer declared

# Function type variable

A variable whose value is a function

  • Has a function start point (value pointing to a function)

Function type

1
2
3
4
5
// Function Definition
func (int, int) int // Parameter names are optional

// Using an Alias Type
type opFunc func (int, int) int


Capture

Function Literal (Anonymous Function) ’s external variables into the local scope

Obtained by reference rather than value copying

  • accessing the address of an external variable in pointer form


Dependency Injection

Function types and function literals are used to inject logic from outside

  • This can also be achieved through interfaces

# Data Structures

# Lists

Store elements in non-contiguous memory by linking each data with pointers.

  • Connecting the elements containing each piece of data with pointers
1
2
3
4
5
type Element struct {
	Value interface{}
	Next *Element
	Prev *Element
}

Array vs List

OperationArrayList
Add to the FrontO(N)
∙ Shift each element one position
∙ Change the first value
O(1)
∙ Add an element to the front and link it
Access a Specific ElementO(1)
∙ Start of the array + (Index x Type Size)
O(N)
∙ Traverse N-1 links
  • Array
    • Favorable for frequent access.
    • High data locality (how closely data is packed)
  • List
    • Ideal for frequent insertions and deletions.

Queue

Follow the FIFO structure for First-In, First-Out

Stack

Follow the FILO structure for First-In, Last-Out

Ring (Circular List)

A list that connects the beginning and the end

  • Fixed in size
  • Has no distinct start and end, just a current position
  • Use
    • Suitable for use when you have a fixed number of items, and old ones can be removed

# Map

A data structure that stores data in the form of key-value pairs

Creation

1
2
// map[key]value
m := make(map[string]string)

Iteration

1
2
3
for k, v := range m { // k: key, v: value
	...
}

Deletion of an Element

1
delete(m, key) // m: map variable, key: key to delete

Checking for an Element

1
2
// Distinguishes between the absence of the element and a value of 0 (zero value)
v, ok := m[3] // v: value, ok: existence status

# Map Mechanism

Hash Function

  • Same input -> Same output
  • Inputs can be infinite, but outputs are within a specific range
  • e.g.
    • Trigonometric functions
    • Modulus operation

Implementation Using Arrays

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const M = 10

func hash(d int) int {
	return d % M
}

func main() {
	m : = [M]int{} 

	m[hash(23)] = 10
	m[hash(33)] = 50 // Hash collision: Different input, but the same output
}

Handling Hash Collisions

  • Store a list of key-value pairs instead of a single value

# References