Sunday, 26 December 2021
Why do we need an accordant pace in computer systems
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. slice := []int{1,2,3}
newSlice := make([]int, 0)
newSlice = append(slice, 4)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])
}[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? slice := []int{1,2,3}
newSlice := make([]int, 0)
newSlice = append(newSlice, append(slice, 4)...) slice := []int{1,2,3}
newSlice := make([]int, 0)
newSlice = append(newSlice, slice...)
newSlice = append(newSlice, 4)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.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 ------
Sunday, 24 October 2021
Pointer Receiver vs Value Receiver
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.
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.
3. String type in Javascript
There are three types of quotes in Javascript:
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.
4, Undefined type in Javascript
The meaning of undefined is 'value is not assigned'.
5, Object type in Javascript
Everything in Javascript is Object. There are two ways to create objects.
You can initialise object with properties like below:
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:
Then type u and press "enter".
Monday, 12 April 2021
Channels in Go
This is a study note for YouTube tutorial: https://www.youtube.com/watch?v=YS4e4q9oBaU&t=20036s
Channel basics
You can only create channels using make function.
Below is an example how to use channel to transfer data between goroutines.
The result is:
42
Restricting data flow
Be noted that channel operation will block the goroutine when executing. If you have unequal receiver and sender, then the additional goroutines will be blocked and give runtime errors.
27 fatal error: all goroutines are asleep - deadlock!
You can use bidirectional communication using channel in goroutines.
42 27
Goroutines can automatically manage the directions of the communication. However in most cases, we would like to make channel in single directions, which means one goroutine is dedicated to be receiver and another be a sender. To do this, you can put channel as function parameter for goroutine to restrict the data flow. Because function parameter can be used to cast a bi channel to a single direction channel.
42
Now the first goroutine is restricted to receive-only, and the second is restricted to send-only.
Buffered channels
You can add a buffer for a channel by simply specifying the second parameter of make function with buffer size.
27 will be buffered instead of throwing a "deadlock" error.
For...range loops with channels
The above code is not a correct way to handle buffered channels. "for...range" loops is preferred in this case.
The result is:
42 27 fatal error: all goroutines are asleep - deadlock!
You can see there is "deadlock" error again, but this time it is not because of the sender goroutine, it is because of the for range loop in the receiver goroutine. Because the receiver goroutine keeps trying to receive data even there is no data sent anymore.
To solve this problem, you can use "close(ch)" function to close channel in the sender goroutine if there is no data to send anymore
The result is:
42 27
In fact, channel has a second return value which you can use for checking if the channel is open or closed.
The result is:
42 27
Select statements
You can use a select statement to manage the workflow in a goroutine. Select statement is similar to switch statement, but select statement is only used for communications. The case statement refers to the communications on different channels.
References: https://www.youtube.com/watch?v=YS4e4q9oBaU&t=20036s
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...
-
ASIC (Application Specified Integrated Circuit) is a customized integrated circuit. It is usually used by a person or company for a very li...
-
This post will talk about the basic understanding of TLS-PSK protocol. It will not cover TLS's history and how its mathematical algorit...
-
Full stack and empty stack Full stack: A full stack is where the stack pointer points to the last data item written Empty stack: ...