Golang面試題解析 4

31. 算法

package main

import (
    "fmt"
    "strings"
)

func main() {
    fmt.Println(Utf8Index("桃園龜山最美麗", "龜山"))
    fmt.Println(strings.Index("桃園龜山最美麗", "章"))
    fmt.Println(strings.Index("", "章"))
    fmt.Println(Utf8Index("33ws桃園龜山最美麗", "龜山"))
}

func Utf8Index(str, substr string) int {
    asciiPos := strings.Index(str, substr)
    if asciiPos == -1 || asciiPos == 0 {
        return asciiPos
    }
    pos := 0
    totalSize := 0
    reader := strings.NewReader(str)
    for _, size, err := reader.ReadRune(); err == nil; _, size, err = reader.ReadRune() {
        totalSize += size
        pos++
        // 匹配到
        if totalSize == asciiPos {
            return pos
        }
    }
    return pos
}
// 2
// -1
// -1
// 6

32.Singleton

https://github.com/kimi0230/DesignPatternGolang/tree/master/Singleton

package main

import "sync"

type singleton struct{}

var ins *singleton
var mu sync.Mutex

// 方法ㄧ 加鎖: 雖然解決並發的問題,但每次加鎖是要付出代價的
func GetIns() *singleton {
    mu.Lock()
    defer mu.Unlock()

    if ins == nil {
        ins = &singleton{}
    }
    return ins
}

//方法二 雙重鎖:避免了每次加鎖,提高代碼效率
func GetIns1() *singleton {
    if ins == nil {
        mu.Lock()
        defer mu.Unlock()
        if ins == nil {
            ins = &singleton{}
        }
    }
    return ins
}

//sync.Once
var once sync.Once

func GetIns2() *singleton {
    once.Do(func() {
        ins = &singleton{}
    })
    return ins
}

sync.Once 是 Golang package 中使方法只執行一次的對象實現,作用與 init 函數類似。但也有所不同。

init 函數是在文件包首次被加載的時候執行,且只執行一次

sync.Onc 是在代碼運行中需要的時候執行,且只執行一次 當一個函數不希望程序在一開始的時候就被執行的時候,我們可以使用 sync.Once 。 https://zhuanlan.zhihu.com/p/44360489


33.編譯執行下面代碼會出現什麼?

  1. 在沒有修改channel的狀態時, 此時沒有一個簡單通用的方式可以去檢查channel 是否關閉
  2. 關閉一個 close的 channel會發生 panic.
  3. 將值發送到一個關閉的channel 會發生panic.
  4. 不要在接收端關閉 channel
  5. 如果有多個發送端, 也不要在發送端關閉 channel
  6. 只有一個發送端, 才在發送端關閉 channel
  7. 不要關閉已關閉的 channel
  8. 不要送值到已關閉的 channel The Channel Closing Prirciple

https://play.golang.org/p/ajo3CD5uCZ6 往已經關閉的channel寫入數據會panic的

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int, 1000)
    go func() {
        for i := 0; i < 10; i++ {
            ch <- i
        }
    }()
    go func() {
        for {
            a, ok := <-ch
            if !ok {
                fmt.Println("close")
                return
            }
            fmt.Println("a: ", a)
        }
    }()
    close(ch)
    fmt.Println("ok")
    time.Sleep(time.Second * 100)
}
/*
ok
close
panic: send on closed channel

goroutine 18 [running]:
main.main.func1(0xc0000bc000)
    /tmp/sandbox420965894/prog.go:12 +0x43
created by main.main
    /tmp/sandbox420965894/prog.go:10 +0x5c
*/

34.編譯執行下面代碼會出現什麼?

package main

import "fmt"

type ConfigOne struct {
    Daemon string
}

func (c *ConfigOne) String() string {
    return fmt.Sprintf("print: %v", c)
}

func main() {
    c := &ConfigOne{}
    c.String()
}

如果類型實現String(),%v和%v格式將使用String()的值。因此,對該類型的String()函數內的類型使用%v會導致無限遞歸。 編譯報錯:

runtime: goroutine stack exceeds 1000000000-byte limit fatal error: stack overflow


35.程式題 : 反轉整數

反轉整數 反轉一個整數 例子1: x = 123, return 321 例子2: x = -123, return -321, 輸入的整數要求是一個 32bit 有符號數,如果反轉後溢出,則輸0.

https://play.golang.org/p/DkiQgA393c7

package main

import (
    "fmt"
    "math"
)

func reverse(x int32) int32 {
    var num int64
    x64 := int64(x)
    for x64 != 0 {
        num = num*10 + x64%10
        x64 = x64 / 10
        //fmt.Println("num: ",num)
    }
    // 使用 math 包中定義好的最大最小值
    if num > math.MaxInt32 || num < math.MinInt32 {
        return 0
    }
    return int32(num)
}

func main() {
    fmt.Println(reverse(321))
}

36.程式題 : 合併重疊區間

合併重疊區間 給定一組 區間,合併所有重疊的 區間。 例如: 給定:[1,3],[2,6],[8,10],[15,18] 返回:[1,6],[8,10],[15,18]

https://go.dev/play/p/VEFkw-N0_yS

package main

import (
    "fmt"
    "sort"
)

type Interval struct {
    Start int
    End   int
}

func merge(intervals []Interval) []Interval {
    if len(intervals) <= 1 {
        return intervals
    }

    // 先由小排到大 起始位子
    sort.Slice(intervals, func(i, j int) bool {
        return intervals[i].Start < intervals[j].Start
    })
    //fmt.Println("intervals", intervals)

    res := make([]Interval, 0)
    swap := Interval{}
    for k, v := range intervals {
        if k == 0 {
            swap = v
            continue
        }
        if v.Start <= swap.End {
            // 如果當下的開始比上一個結束小時, 將結束設回當下的結束
            swap.End = v.End
        } else {
            res = append(res, swap)
            swap = v
        }
        //fmt.Println("res:", res)
    }
    res = append(res, swap)
    return res
}

func main() {
    inter := []Interval{
        {2, 6},
        {1, 3},
        {8, 10},
        {15, 18},
    }
    fmt.Println(merge(inter))
}

37. 輸出什麼 : 每個中文字,3個byte

https://go.dev/play/p/FEhwHNcMXRS

package main

import (
    "fmt"
)

func main() {
    fmt.Println(len("bj!"))
    fmt.Println(len("你好"))
    fmt.Println(len("你好bj!"))
}
/*
3
6
9
*/

每個中文字,3個byte


38.編譯並運行如下代碼會發生什麼?

map的value本身是不可尋址的,因為map中的值會在內存中移動

package main

import "fmt"

type Test struct {
    Name string
}

var list map[string]Test

func main() {

    list = make(map[string]Test)
    name := Test{"xiaoming"}
    list["name"] = name // 出錯!
    list["name"].Name = "Hello"
    fmt.Println(list["name"])
}

編程報錯cannot assign to struct field list["name"].Name in map 因為list[“name”]不是一個普通的指針值,map的value本身是不可尋址的,因為map中的值會在內存中移動,並且舊的指針地址在map改變時會變得無效。 定義的是var list map[string]Test,注意哦Test不是指針,而且map我們都知道是可以自動擴容的,那麼原來的存儲na​​me的Test可能在地址A,但是如果map擴容了地址A就不是原來的Test了,所以go就不允許我們寫數據。你改為var list map[string]*Test試試看。

解法

https://play.golang.org/p/pxA9bQDrztB

package main

import "fmt"

type Test struct {
    Name string
}
var list map[string]*Test // 改成 *Test

func main() {

    list = make(map[string]*Test)
    name := &Test{"kimi"}
    list["name"] = name
    list["name"].Name = "Hello"
    fmt.Println(list["name"])
}

make(map[string]string, 3)的len是0 https://go.dev/play/p/be1yY3UfVcQ

    a := make([]int, 3)
    fmt.Println(len(a)) // 3

    b := make(map[string]string, 3)
    fmt.Println(len(b)) // 0

39.ABCD中哪一行存在錯誤?

https://play.golang.org/p/PU8krulJH67

func g(x *interface{}) 只能接受 *interface{}

package main

type S struct {
}

func f(x interface{}) {
}

func g(x *interface{}) {
}

func main() {
    s := S{}
    p := &s
    f(s) //A
    g(s) //B 錯
    f(p) //C
    g(p) //D 錯
}
/*
./prog.go:16:3: cannot use s (type S) as type *interface {} in argument to g:
    *interface {} is pointer to interface, not interface
./prog.go:18:3: cannot use p (type *S) as type *interface {} in argument to g:
    *interface {} is pointer to interface, not interface
*/

>

看Golang是強類型語言,interface是所有golang類型的父類, 類似Java的Object。函數中func f(x interface{})的interface{}可以支持傳入golang的任何類型,包括指針, 但是函數func g(x interface{}) 只能接受 interface{}


40.編譯並運行如下代碼會發生什麼?

package main

import (
    "sync"
    //"time"
)

const N = 10

var wg = &sync.WaitGroup{}

func main() {

    for i := 0; i < N; i++ {
        go func(i int) {
            wg.Add(1)
            println(i)
            defer wg.Done()
        }(i)
    }
    wg.Wait()

}

這是使用WaitGroup經常犯下的錯誤!請各位同學多次運行就會發現輸出都會不同甚至又出現報錯的問題。 這是因為go執行太快了,導致wg.Add(1)還沒有執行main函數就執行完畢了。

解法

https://play.golang.org/p/vpKDPDG9F6i

package main

import (
    "sync"
    //"time"
)

const N = 10

var wg = &sync.WaitGroup{}

func main() {

    for i := 0; i < N; i++ {
        wg.Add(1) // 寫在這
        go func(i int) {
            println(i)
            defer wg.Done()
        }(i)
    }
    wg.Wait()

}

Reference

© Kimi Tsai all right reserved.            Updated : 2023-07-12 09:04:53

results matching ""

    No results matching ""

    results matching ""

      No results matching ""