Tự học phiêu lưu ký - Phần 1: Làm quen Golang





Là một dev Fullstack Nodejs và đang cảm thấy hiện tại Nodejs đang khó kiếm việc nên mình bắt đầu học ngôn ngữ mới, tại sao lại là Go thì mình có đi tìm vài ngôn ngữ tiềm năng để học thì thấy có Go và Rust nhưng rust có vẻ thiên về hệ thống hơn còn với Go thì làm web mà mình là dev web nên mình chọn học Go, hơn nữa cú pháp của nó có vẻ khá dễ học.

Mình bắt đầu chọn học qua trang Exercism, sau khi học qua hết cái phần lộ trình cơ bản rồi nên giờ sẽ đi giải các bài tập để hiểu rõ hơn, vì cũng đã quá quen với Nodejs rồi nên giờ khá là lú trong nhiều phần như xử lý chuỗi của 2 ngôn ngữ này -.-

Bổ sung: Ban đầu mình chỉ định là làm mấy câu rồi quay ra LeetCode để giải nhưng mà hiện tại sếp lại kêu chuyển đổi công nghệ nên mình phải học thêm Remix React, tiện là mình cũng định học Golang nên mình có ý định sẽ làm luôn 1 thứ gì đó gồm Front-end, Back-end và Mobile app, lộ trình như sau:

  1. Thời điểm đầu tiên mình học cú pháp của Golang sẽ là phần 1 này, nói là cú pháp vì mình làm Nodejs mấy năm rồi nên các thứ nó cũng giống giống nhau cả khác khái niệm và cú pháp thôi.
  2. Sau khi hoàn thành phần học Golang mình sẽ chuyển qua học Remix, nhìn chung cũng thấy kha khá giống với Next, mình cũng thử tạo project Remix rồi và người ta sử dụng mặc định là Tailwind nên mình cũng sẽ học thêm cả Tailwind nữa, gọi là học nhưng có lẽ giống kiểu cưỡi ngựa xem hoa thôi và có vấn đề ở đâu tìm hiểu và giải quyết đến đó sau. Mình cũng sẽ thử nghĩ xem làm gì đó để kết hợp cả FE với BE có thể là 1 trang gì đó như chia sẻ hình ảnh sau đó người dùng có thể vào vào like và bình luận, đăng ký đăng nhập, tự nhiên thấy giống giống cái facebook nhưng làm sida phake thôi, học là chính.
  3. Phần tiếp theo là làm Back-end mình sẽ làm 1 cái BE bằng Golang sử dụng database để lưu trữ quản lý tài khoản, hình ảnh, bình luận... có lẽ là sử dụng JWT làm authen. Mới nghĩ thôi mình cũng chưa biết sẽ làm thế nào nữa :D
  4. Sau khi ổn ổn chấp nhận được thì mình sẽ làm 1 cái app mobile bằng React Native vì cũng tiện nó là React luôn chứ nghĩ thôi là đã thấy lười học thêm cái nữa lắm rồi.

Hy vọng mình sẽ hoàn thành dự định này trong vòng 1 năm tới, tại mình cũng chỉ có ý định code thêm vào 2 ngày nghỉ cuối tuần thôi.

Thôi thì bắt đầu giải bài hiện tại

ETL

Introduction

You work for a company that makes an online multiplayer game called Lexiconia.

To play the game, each player is given 13 letters, which they must rearrange to create words. Different letters have different point values, since it's easier to create words with some letters than others.

The game was originally launched in English, but it is very popular, and now the company wants to expand to other languages as well.

Different languages need to support different point values for letters. The point values are determined by how often letters are used, compared to other letters in that language.

For example, the letter 'C' is quite common in English, and is only worth 3 points. But in Norwegian it's a very rare letter, and is worth 10 points.

To make it easier to add new languages, your team needs to change the way letters and their point values are stored in the game.

Instructions

Your task is to change the data format of letters and their point values in the game.

Currently, letters are stored in groups based on their score, in a one-to-many mapping.

  • 1 point: "A", "E", "I", "O", "U", "L", "N", "R", "S", "T",
  • 2 points: "D", "G",
  • 3 points: "B", "C", "M", "P",
  • 4 points: "F", "H", "V", "W", "Y",
  • 5 points: "K",
  • 8 points: "J", "X",
  • 10 points: "Q", "Z",

This needs to be changed to store each individual letter with its score in a one-to-one mapping.

  • "a" is worth 1 point.
  • "b" is worth 3 points.
  • "c" is worth 3 points.
  • "d" is worth 2 points.
  • etc.

As part of this change, the team has also decided to change the letters to be lower-case rather than upper-case.


func Transform(in map[int][]string) map[string]int {
panic("Please implement the Transform function")
}

Hiểu bài toán

Bài yêu cầu ta chuyển đổi map từ dạng map[int][]string sang dạng map[string]int .

Ý tưởng thực hiện

Lặp qua từng key của input để biết được mảng đó chứa các ký tự nào, sau đó lưu vào map kết quả dưới dạng key là lowerCase

Code

import (
"strings"
)

func Transform(in map[int][]string) map[string]int {
res := map[string]int{}
for key, value := range in {
for _, v := range value {
res[strings.ToLower(v)] = key
}
}
return res
}

Bob

Introduction

Bob is a lackadaisical teenager. He likes to think that he's very cool. And he definitely doesn't get excited about things. That wouldn't be cool.

When people talk to him, his responses are pretty limited.

Instructions

Your task is to determine what Bob will reply to someone when they say something to him or ask him a question.

Bob only ever answers one of five things:

  • "Sure." This is his response if you ask him a question, such as "How are you?" The convention used for questions is that it ends with a question mark.
  • "Whoa, chill out!" This is his answer if you YELL AT HIM. The convention used for yelling is ALL CAPITAL LETTERS.
  • "Calm down, I know what I'm doing!" This is what he says if you yell a question at him.
  • "Fine. Be that way!" This is how he responds to silence. The convention used for silence is nothing, or various combinations of whitespace characters.
  • "Whatever." This is what he answers to anything else.
func Hey(remark string) string {

return ""
}

Hiểu bài toán

Phân tích các input để trả lời bằng các câu tương ứng

Ý tưởng thực hiện

Thực hiện trim để xem nếu == "" thì trả lời Fine. Be that way!
Nếu là một câu hỏi kết thúc bằng ? thì trả lời Sure.
Nếu là một chuỗi viết hoa thì trả lời Whoa, chill out!
Nếu là một chuỗi viết hoa và kết thúc là dấu ? thì trả lời Calm down, I know what I'm doing!
Tất cả các trường hợp còn lại thì trả lời Whatever.

Code

import (
"fmt"
"regexp"
"strings"
)

func IsUpper(s string) bool {
reg :=
regexp.MustCompile(`[a-zA-Z]`)
if !reg.MatchString(s) {
return false
}
return s == strings.ToUpper(s)
}
func Hey(remark string) string {
remark = strings.TrimSpace(remark)
isUpper := IsUpper(remark)
hasSuffix := strings.HasSuffix(remark, "?")
switch {
case remark == "":
return "Fine. Be that way!"
case isUpper && hasSuffix:
return "Calm down, I know what I'm doing!"
case isUpper:
return "Whoa, chill out!"
case hasSuffix:
return "Sure."
default:
return "Whatever."
}
}

Grains

Instructions

Calculate the number of grains of wheat on a chessboard given that the number on each square doubles.

There once was a wise servant who saved the life of a prince. The king promised to pay whatever the servant could dream up. Knowing that the king loved chess, the servant told the king he would like to have grains of wheat. One grain on the first square of a chess board, with the number of grains doubling on each successive square.

There are 64 squares on a chessboard (where square 1 has one grain, square 2 has two grains, and so on).

Write code that shows:

  • how many grains were on a given square, and
  • the total number of grains on the chessboard
func Square(number int) (uint64, error) {
panic("Please implement the Square function")
}

func Total() uint64 {
panic("Please implement the Total function")
}

Hiểu bài toán

Mình sẽ phải đi tính số hạt lúa mì từ 1 đến 64 ô tương ứng số ô trên bàn cờ vua

Ý tưởng thực hiện

Dùng vòng lặp từ 1 đến 64 để tính tổng số lượng hạt tương ứng trên từng ô và cộng tổng lại

Code

import (
"errors"
"math"
)

func Square(number int) (uint64, error) {
if number < 1 || number > 64 {
return 0, errors.New("square must be between 1 and 64")
}

return uint64(math.Pow(2, float64(number-1))), nil
}

func Total() uint64 {
var sum uint64 = 0
for i := 1; i <= 64; i++ {
res, err := Square(i)
if err != nil {
return 0
}
sum += res
}
return sum
}

Roman Numerals

Introduction

Today, most people in the world use Arabic numerals (0–9). But if you travelled back two thousand years, you'd find that most Europeans were using Roman numerals instead.

To write a Roman numeral we use the following Latin letters, each of which has a value:

MDCLXVI
1000500100501051

A Roman numeral is a sequence of these letters, and its value is the sum of the letters' values. For example, XVIII has the value 18 (10 + 5 + 1 + 1 + 1 = 18).

There's one rule that makes things trickier though, and that's that the same letter cannot be used more than three times in succession. That means that we can't express numbers such as 4 with the seemingly natural IIII. Instead, for those numbers, we use a subtraction method between two letters. So we think of 4 not as 1 + 1 + 1 + 1 but instead as 5 - 1. And slightly confusingly to our modern thinking, we write the smaller number first. This applies only in the following cases: 4 (IV), 9 (IX), 40 (XL), 90 (XC), 400 (CD) and 900 (CM).

Order matters in Roman numerals! Letters (and the special compounds above) must be ordered by decreasing value from left to right.

Here are some examples:

 105 => CV
---- => --
 100 => C
+  5 =>  V
 106 => CVI
---- => --
 100 => C
+  5 =>  V
+  1 =>   I
 104 => CIV
---- => ---
 100 => C
+  4 =>  IV

And a final more complex example:

 1996 => MCMXCVI
----- => -------
 1000 => M
+ 900 =>  CM
+  90 =>    XC
+   5 =>      V
+   1 =>       I

Instructions

Your task is to convert a number from Arabic numerals to Roman numerals.

For this exercise, we are only concerned about traditional Roman numerals, in which the largest number is MMMCMXCIX (or 3,999).

Hiểu bài toán

Chuyển đổi số la mã về dạng số hệ 10 mình sử dụng và ngược lại

Ý tưởng thực hiện

Sử dụng map để lưu trữ các giá trị chính như bảng bên trên đã cung cấp.
Sử dụng vòng lặp để duyệt ngược chuỗi la mã
Nếu số hiện tại mà nhỏ hơn số phía sau nó thì tổng sẽ trừ đi số hiện tại, nếu không thì vẫn cộng vào tổng bình thường, ví dụ IV là số 4, duyệt từ sau qua thì có V là 5, cộng 5 vào tổng, tiếp theo là I nghĩa là 1, nhưng 1 nhỏ hơn 5 nên là tổng trừ đi số hiện tại là 1 nên cuối cùng sẽ còn 4, và 4 đúng là kết quả mong muốn

Để chuyển ngược lại từ số sang la mã thì cần cung cấp mảng chi tiết hơn và sắp xếp từ lớn về bé,
  • Nếu giá trị thập phân lớn hơn hoặc bằng giá trị trong bảng, trừ giá trị đó khỏi số thập phân.
  • Thêm ký hiệu La Mã tương ứng vào kết quả.
  • Lặp lại cho đến khi số thập phân bằng 0.


  • Code

    import (
    "errors"
    "fmt"
    )

    type RomanNumeral struct {
    Value int
    Symbol string
    }

    func ToRomanNumeral(input int) (string, error) {
    if input <= 0 || input >= 4000 {
    return "", errors.New("out of range")
    }
    m := []RomanNumeral{{1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"}, {100, "C"}, {90, "XC"}, {50, "L"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}}
    res := ""
    for _, numeral := range m {
    value, symbol := numeral.Value, numeral.Symbol
    for input >= value {
    res += symbol
    input -= value
    }
    }
    return res, nil
    }

    func RomanToArabic(input string) int {
    m := map[string]int{"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000}
    res := 0
    prev := 0
    for i := len(input) - 1; i >= 0; i-- {
    curr := m[string(input[i])]
    if curr < prev {
    res -= curr
    } else {
    res += curr
    }
    prev = curr
    }
    return res
    }

    ISBN

    Instructions

    The ISBN-10 verification process is used to validate book identification numbers. These normally contain dashes and look like: 3-598-21508-8

    ISBN

    The ISBN-10 format is 9 digits (0 to 9) plus one check character (either a digit or an X only). In the case the check character is an X, this represents the value '10'. These may be communicated with or without hyphens, and can be checked for their validity by the following formula:

    (d₁ * 10 + d₂ * 9 + d₃ * 8 + d₄ * 7 + d₅ * 6 + d₆ * 5 + d₇ * 4 + d₈ * 3 + d₉ * 2 + d₁₀ * 1) mod 11 == 0
    

    If the result is 0, then it is a valid ISBN-10, otherwise it is invalid.

    Example

    Let's take the ISBN-10 3-598-21508-8. We plug it in to the formula, and get:

    (3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 == 0
    

    Since the result is 0, this proves that our ISBN is valid.

    Task

    Given a string the program should check if the provided string is a valid ISBN-10. Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN.

    The program should be able to verify ISBN-10 both with and without separating dashes.

    Caveats

    Converting from strings to numbers can be tricky in certain languages. Now, it's even trickier since the check digit of an ISBN-10 may be 'X' (representing '10'). For instance 3-598-21507-X is a valid ISBN-10.

    Hiểu bài toán

    Kiểm tra chuỗi đã cho có phải là dạng ISBN-10 hay không

    Ý tưởng thực hiện

    Nhìn ta thấy nó là 1 chuỗi có chứa các số, dấu gạch và ký tự cuối cùng có thể là X hoặc 1 số.
    Thực hiện xóa dấu gạch đi và duyệt từ ký tự đầu tiên đến cuối cùng để tính tổng theo công thức được cung cấp và lấy mod 11, nếu bằng 0 thì là hợp lệ

    Code

    import (
    "fmt"
    "strings"
    )

    func IsValidISBN(isbn string) bool {
    isbn2 := strings.ReplaceAll(isbn, "-", "")
    if len(isbn2) != 10 {
    return false
    }
    i := 10
    sum := 0
    for index, value := range isbn2 {
    if value >= '0' && value <= '9' {
    sum += int(value-'0') * i
    } else if value == 'X' && index == 9 {
    sum += 10
    } else {
    return false
    }
    i--
    }
    return sum%11 == 0
    }

    Word Count

    Introduction

    You teach English as a foreign language to high school students.

    You've decided to base your entire curriculum on TV shows. You need to analyze which words are used, and how often they're repeated.

    This will let you choose the simplest shows to start with, and to gradually increase the difficulty as time passes.

    Instructions

    Your task is to count how many times each word occurs in a subtitle of a drama.

    The subtitles from these dramas use only ASCII characters.

    The characters often speak in casual English, using contractions like they're or it's. Though these contractions come from two words (e.g. we are), the contraction (we're) is considered a single word.

    Words can be separated by any form of punctuation (e.g. ":", "!", or "?") or whitespace (e.g. "\t", "\n", or " "). The only punctuation that does not separate words is the apostrophe in contractions.

    Numbers are considered words. If the subtitles say It costs 100 dollars. then 100 will be its own word.

    Words are case insensitive. For example, the word you occurs three times in the following sentence:

    You come back, you hear me? DO YOU HEAR ME?

    The ordering of the word counts in the results doesn't matter.

    Here's an example that incorporates several of the elements discussed above:

    • simple words
    • contractions
    • numbers
    • case insensitive words
    • punctuation (including apostrophes) to separate words
    • different forms of whitespace to separate words

    "That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.

    The mapping for this subtitle would be:

    123: 1
    agent: 1
    cried: 1
    fled: 1
    i: 1
    password: 2
    so: 1
    special: 1
    that's: 1
    the: 2
    

    Hiểu bài toán

    Bài toán yêu cầu chúng ta đếm số lượng các từ xuất hiện và đưa vào map rồi trả về, kết quả trả về phải là các ký tự viết thường

    Ý tưởng thực hiện

    đầu tiên chuyển string sang lower case, yêu cầu các từ là phải không chứa các ký tự đặc biệt và ký tự xuống dòng, khoảng trắng

    Làm việc với chuỗi thế này thì mình thường sử dụng regex là nhanh nhất, tuy không quá hiểu về regex nhưng mà mô tả cho chatgpt hiểu để nó tạo ra pattern cho mình thôi.

    \b[\w']+\b
    mình được pattern như này, nó chỉ có 1 điểm duy nhất không ưng là nếu có ký tự xuống dòng thì không tách được, ví dụ \nSo thì kết quả sau khi tách ra là nSo . cho nên trước khi thực hiện tách từ thì replace hết tất cả \n thành thành rỗng và thế là hoàn thành 

    Code

    import (
    "regexp"
    "strings"
    )

    type Frequency map[string]int

    func WordCount(phrase string) Frequency {
    re := regexp.MustCompile(`\b[\w']+\b`)
    phrase = strings.ToLower(phrase)
    phrase = strings.ReplaceAll(phrase, "\\n", " ")
    arr := re.FindAllString(phrase, -1)

    res := make(Frequency)
    for _, val := range arr {
    res[val] = res[val] + 1
    }

    return res
    }

    Thật ra mình làm nhiều bài hơn nữa rồi nhưng lười viết quá nên thôi tạm dừng việc viết phần 1 tại đây, nói chung sau khi làm mấy chục bài mình cũng khá quen cú pháp Golang rồi, còn cao siêu hơn về sau vừa nghiên cứu vừa làm thôi.

    Đăng nhận xét

    0 Nhận xét