Monday, 17 January 2022

Difference between "docker stop" and "docker kill"

 To stop a running container, you can use either "docker stop" or "docker kill" command to do so. Although it seems doing the same thing, actually there is a slight difference between them.

"docker stop" command will send a hardware signal SIGTERM to the container, if the process received the SIGTERM signal, the developer can code to do some clean up and releasing some resources before terminating the process.

However "docker kill" command will send a hardware signal SIGKILL to the container and stop it immediately.

Usually "docker stop" is used to stop a running container. If the container is hanging there for a long time or failed to stop. Then you can try to use "docker kill"to kill the container anyway.

Sunday, 26 December 2021

Why do we need an accordant pace in computer systems

In the past, we always know clock is used to adjust the pace of the computer systems. But we don't understand why we need an accordant pace in computer systems. Why don't we just let the components in the computer do as much and fast as they can?

The reason is that the different components have dependency between each other. So if we image the whole computer systems is a succession program, of course we don't need accordant pace any more. But this will make the components in computer wait for each other's result to go on. So this can't make components work in parallel way.

So obviously clock has this advantage which is that it can make different components work in parallel way become possible.

Sunday, 28 November 2021

DO NOT use the return of append function to create new slice in Go

For Golang developers, it is very common to use append function to append new elements into slice. The common syntax is like below:

    var slice := make([]int, 0)
    slice = append(slice, 123)
And for some requirements, you may want to create a new slice based on an existing slice. We know the below the code will work perfectly.
    slice := []int{1,2,3}
    newSlice := make([]int, 0)
    newSlice = append(newSlice, append(slice, 4)...)
Below is complete example code to show the result:
package main

import (
    "fmt"
)

func main() {
    // here create the origin slice with cap 10 to make sure that
    // the slice is big enough to hold more data
    slice := make([]int, 0, 10)
    slice = append(slice, []int{1, 2, 3}...)
    newSlice := make([]int, 0)
    newSlice = append(newSlice, append(slice, 4)...)
    fmt.Println(slice)
    fmt.Println(newSlice)
    fmt.Printf("The address of slice: %v\n", &slice[0])
    fmt.Printf("The address of newSlice: %v\n", &newSlice[0])
}
The result is:
[1 2 3]
[1 2 3 4]
The address of slice: 0xc000014050
The address of newSlice: 0xc00007a000
You can see the two slices have different addresses.
However you may think if we can simplify the code by appending value to the origin slice and assign the return value to the newSlice variable like below:
    slice := []int{1,2,3}
    newSlice := make([]int, 0)
    newSlice = append(slice, 4)
This looks simpler and cleaner, right? NO, this won't work! Let's see an example:
package main

import (
    "fmt"
)

func main() {
    // here create the origin slice with cap 10 to make sure that
    // the slice is big enough to hold more data
    slice := make([]int, 0, 10)
    slice = append(slice, []int{1, 2, 3}...)
    newSlice := make([]int, 0)
    newSlice = append(slice, 4)
    fmt.Println(slice)
    fmt.Println(newSlice)
    fmt.Printf("The address of slice: %v\n", &slice[0])
    fmt.Printf("The address of newSlice: %v\n", &newSlice[0])
}
The result is:
[1 2 3]
[1 2 3 4]
The address of slice: 0xc00010a000
The address of newSlice: 0xc00010a000
See newSlice is pointing to the same address of slice?

This is because append function does not guarantee that it always returns a new slice. It only reallocate a new slice when the capacity of the slice in the first parameter is not enough to hold more data. So if you are expecting to use append function to get a new slice like above, you will probably mess up the origin slice and get the wrong output.
So to be safe, if you want to get a new slice based on an existing slice, you use the code below:
    slice := []int{1,2,3}
    newSlice := make([]int, 0)
    newSlice = append(newSlice, append(slice, 4)...)
Or below code if it is clearer.
    slice := []int{1,2,3}
    newSlice := make([]int, 0)
    newSlice = append(newSlice, slice...)
    newSlice = append(newSlice, 4)
I encountered this problem when I am practicing LeetCode problem 797



Wednesday, 17 November 2021

How to add optional arguments in Go function without impacting the existing function calls

Unlike C# you can put "Optional" keyword to add optional arguments in function, there is no direct way to create optional parameters in Go functions. And there is no method overloading in Go either. Even so, if you really need to add optional arguments to an existing function, maybe because this function is in a common library which is called by different software modules and you don't want to update all the modules for this new argument change, there is still a way to implement optional arguments thanks to variadic arguments in Go functions.

Variadic arguments allow developer to put any number of trailing arguments as the input of functions. For example:

package main

import (
  "fmt"
)

func main() {
  fmt.Printf("1 + 2 = %d\n", add(1, 2))
  fmt.Printf("1 + 2 + 3 + 4 + 5 = %d\n", add(1, 2, 3, 4, 5))
}

func add(args ...int) int {
  sum := 0
  for i:=0; i < len(args); i++ {
    sum += args[i]
  }
  return sum
}
The output is:
1 + 2 = 3
1 + 2 + 3 + 4 + 5 = 15
With the help of variadic arguments, you can easily to implement optional arguments. For example, you have a function to generate characters in a common library for a game development. This function can generate characters with name and sex for now. 
package main

import (
  "fmt"
)

func main() {
  createCharactor("Bertram", "Male")
}

func createCharactor(name string, sex string) {
  fmt.Println("---------- New Charactor ----------")
  fmt.Printf("Name is: %s\n", name)
  fmt.Printf("Sex is: %s\n", sex)
  fmt.Println("------ New Charactor Created ------")
}
The output is: 
---------- New Charactor ----------
Name is: Bertram
Sex is: Male
------ New Charactor Created ------
Now you want to add age argument to this function without impacting the existing function call. You can do like following:
package main

import (
  "fmt"
)

func main() {
  createCharactor("Bertram", "Male")
  fmt.Println()
  createCharactor("Kai", "Male", 15)
}

func createCharactor(name string, sex string, age ...int) {
  fmt.Println("---------- New Charactor ----------")
  fmt.Printf("Name is: %s\n", name)
  fmt.Printf("Sex is: %s\n", sex)
  if len(age) > 0 {
    fmt.Printf("Age is: %d\n", age[0])
  }
  fmt.Println("------ New Charactor Created ------")
}
The output is:
---------- New Charactor ----------
Name is: Bertram
Sex is: Male
------ New Charactor Created ------

---------- New Charactor ----------
Name is: Kai
Sex is: Male
Age is: 15
------ New Charactor Created ------
But the above code is not reusable to add other arguments with different data types. For example, what if you need to add title, email later? So in order to make the variadic argument more generic, we can use "interface{}" type as the variadic argument which gives the ability to add more arguments with different data types.

According to this, the above code can be refactored as below:
package main

import (
  "fmt"
)

func main() {
  createCharactor("Bertram", "Male")
  fmt.Println()
  createCharactor("Kai", "Male", 15, "Software Engineer", "kai@gmail.com")
  fmt.Println()
	
}

func createCharactor(name string, sex string, args ...interface{}) {
  fmt.Println("---------- New Charactor ----------")
  fmt.Printf("Name is: %s\n", name)
  fmt.Printf("Sex is: %s\n", sex)
  if len(args) > 0 {
    fmt.Printf("Age is: %d\n", args[0].(int))
    fmt.Printf("Tile is: %s\n", args[1].(string))
    fmt.Printf("Email is: %s\n", args[2].(string))
  }
  fmt.Println("------ New Charactor Created ------")
}
The output is:
---------- New Charactor ----------
Name is: Bertram
Sex is: Male
------ New Charactor Created ------

---------- New Charactor ----------
Name is: Kai
Sex is: Male
Age is: 15
Tile is: Software Engineer
Email is: kai@gmail.com
------ New Charactor Created ------

However, this solution does not convey the meaning of the optional arguments. So you either put a comment above the header to specify what it means for each optional argument, or comment where you call the functions, otherwise it will become very difficult to understand the code. So this solution is not recommended unless you have a very tight deadline to meet the delivery date and really have no time to refactor code.  

Sunday, 24 October 2021

Pointer Receiver vs Value Receiver

Receivers are same as parameters in function in Go. They are also passed by value when functions are called. Because of this, it is preferred to use pointers as receivers instead of passing values directly. This is because usually we use receivers to implement OOP programming style. If passed by value, the properties of the objects will not be able to update.

For example, we have an object called "person", it has age property. Initially when created, the person has initialised the age to 0, then we use method to change it to 18. See code below:

package main

import (
	"fmt"
)

type Person struct {
	Name string
	Age int
}

func main() {
	person := Person{Name : "Leo"}
	fmt.Println(person)
	person.SetAge(18)
	fmt.Println(person)
	
}

func (p *Person) SetAge(age int) {
	p.Age = age
}
The output of the result is:
{Leo 0}
{Leo 18}
However if the receiver is passed by value like below:

func (p Person) SetAge(age int) {
	p.Age = age
}

Then the age property will not be updated. However if the receiver is passed by value like below:

{Leo 0}
{Leo 0}

Sunday, 3 October 2021

Variables and Data Types in Javascript

This is a study note about Javascript from https://www.geeksforgeeks.org/introduction-to-javascript-course-learn-how-to-build-a-task-tracker-using-javascript/

Javascript is untyped language. Once a variable is created in javascript using 'var', you can assign any other type of data to this variable.

var text = 'Hello World'
text = 123

Above code is valid in Javascript. Variable text can be assigned a string value and then later change to a number.

1. Difference between var and let in Javascript

Basically, 'var' is function scoped, however 'let' is block scoped.

https://www.geeksforgeeks.org/difference-between-var-and-let-in-javascript/

Let's use use 'let' for most of the cases because 'var' is confusing.

2. Number type in Javascript

The number type in Javascript contains both integer and floating point numbers. Besides these numbers, we also have 'special-numbers' in Javascript that are: 'infinity', '-Infinity' and 'NaN'. The 'NaN' denotes a computational error.

let num = 2; // integer
let num2 = 1.3; // floating point number
let num3 = Infinity; // Infinity
let num4 = 'something here too'/2; // NaN

3. String type in Javascript

There are three types of quotes in Javascript:

let str = "Hello There";
let str2 = 'Single quotes works fine';
let phrase = `can embed ${str}`;

There's no difference between 'single' and "double" quotes in Javascript. Backticks provide extra functionality as with the help of them we can embed variables inside them.

let variable = "Leo";

// embed a variable
alert( `Hello, ${variable}!` ); // Hello, Leo!

4, Undefined type in Javascript

The meaning of undefined is 'value is not assigned'.

let x;
console.log(x); // undefined

5, Object type in Javascript

Everything in Javascript is Object. There are two ways to create objects.

let person = new Object();
let person = {};

You can initialise object with properties like below:

let person = {
name: "Mukul",
age: 22,
}







Sunday, 13 June 2021

Mistakes That Should Not Happen Again

 Database:

DO NOT use "key" keyword as a column name, because it is a reserved keyword in MySQL.


Docker:

Clean docker using below command from time to time to release more memory:

docker system prune

Then type u and press "enter".





Difference between "docker stop" and "docker kill"

 To stop a running container, you can use either "docker stop" or "docker kill" command to do so. Although it seems doin...