實驗樓 樓賽 第15期 Go語言 解題報告

2021-08-08 07:50:19 字數 4531 閱讀 1718

前記:本解題方法僅為一家之言

有任何不明題意的地方,請自行移步:

第一題:透明地修改http請求

題意說明:

算出http請求body的md5值,加到header裡(x-md5),如果沒有body,則不加x-md5。

解題思路:

這道題很簡單了,實現http.roundtripper

介面。讀出http.body,計算md5,放入header。

解題**:

注意事項:

1. req.body有可能為nil;

2. req.body==nil || req.body.len==0

的情況下不設定header.x-md5;

3. 記得req.body.close()

,見$goroot/src/net/http/request.go: line 164;即便拋開這個理由,你把人body都狸貓換太子了,總要記得close body吧。

一家之言:

1. 不管有沒有body,統一處理,不搞特殊化;

2. 組合操作,不明顯地運算元據;

後來想想,多數時候**可能會寫成下面這樣:

先把資料讀出,再做具體操作。這樣的好處很明顯,大家都明白在幹什麼,上面解法卻不能一眼看懂。不過我還是用上面解法過了,逼格高嘛,呵呵。流操作,免去了自己操作變數的負擔。

第二題:快取請求執行結果

題意說明:

說白了,就是做個本地快取,過期更新。

解題思路:

記錄每個key對應的val和expire,超時則呼叫fn()

重新獲取。

解題**:

package cacheflight

import (

"hash/crc32"

"sync"

"time"

)const bsize = 131

type value struct

err error

mut sync.mutex

}// group is core struct

type group struct

// newgroup return a new ttl cache group

func newgroup(cacheexpiration time.duration) (group *group)

for i := range group.buckets

} return

}// do cache

func (g *group) do(key string, fn func() (inte***ce{}, error)) (ret inte***ce{}, err error)

g.buckets[idx][key] = val

} g.muts[idx].unlock()

val.mut.lock()

if val.err == nil && time.since(val.time) < g.expire else

val.mut.unlock()

return

}

注意事項:測試要求有且只有乙個協程呼叫fn(),所以一定要控制好併發。

一家之言:

其實我伊始不是用上面方法解題的,可以看出對同乙個key完全序列化,簡單粗暴,但同時失去了同乙個key併發的機會。嗯,裝逼的我一開始就去掉相同key的這把鎖,結果一直失敗,把我惹毛了,直接簡單粗暴來了上面一版解題成功。經我不懈努力,還是做到了如下**:

package cacheflight

import (

"hash/crc32"

"sync"

"sync/atomic"

"time"

)const bsize = 131

type value struct

err error

wait chan struct{}

mut sync.rwmutex

cnt uint32

}// group is core struct

type group struct

// newgroup return a new ttl cache group

func newgroup(cacheexpiration time.duration) (group *group)

for i := range group.buckets

} return

}// do cache

func (g *group) do(key string, fn func() (inte***ce{}, error)) (ret inte***ce{}, err error)

idx := crc32.checksumieee(byte(key)) % uint32(len(g.buckets))

g.muts[idx].lock()

val := g.buckets[idx][key]

if val == nil )}

g.buckets[idx][key] = val

} g.muts[idx].unlock()

val.mut.rlock()

if val.err == nil && time.since(val.time) < g.expire

wait := val.wait

val.mut.runlock()

if atomic.adduint32(&val.cnt, 1) == 1 )

val.mut.unlock()

atomic.storeuint32(&val.cnt, 0)

return

} return val.val, val.err

}

沒錯,用讀鎖讓同乙個key的也併發起來,用原子操作決定只有第乙個呼叫fn()

,後面進來的協程統一wait

。實際應用中,fn()有io操作肯定是要慢一些的,所以不把它放在鎖裡面。當返回後,加鎖寫val,通知其他協程可讀了,同時準備好下次的wait訊號。寫完後記得重置記數。其他協程經過wait後讀資料不加鎖,不加鎖,不加鎖,重要的事情說三遍,因為此時剛寫完,要經一段時間後才會有寫,所以此時的讀不需要加任何鎖,放心大膽地讀就是了。

理論上,inc(val.cnt)

和reset(val.cnt, 0)併發會有問題,一是經寫鎖攔住了漏下來的協程第一句是inc()

,二是經過呼叫加寫鎖、寫資料這些步驟後,還有協程沒有做inc()

操作的機率能有多大?所以這種情況理論上會出現,但實際中出現概率低到幾乎不可能,可忽略掉。

還有乙個問題是atomic.inc

真的會比lock;n++;unlock

這種模式快嗎?在我測試結果看來是這樣的,至少3倍以上,多數時候是4倍左右。

第三題:實現json注釋解析器

題意說明:

在json中'#'之後的都是注釋。

解題思路:

掃一遍字串,找到'#'返回即可。

解題**:

注意事項:

1. 在字串中,字元'"'是用'\"'的形式表示的,所以會有反斜線'\\'轉義。

一家之言:

注意記錄狀態,是否在字串內,如果在字串內有#是要略過的,其他時候完全忽略錯誤是因為題目給的這個函式完全沒有err返回值。在判斷'\\'時應該判斷是不是在字串內。

第一次看到題目時,以為要解析像/*comment*/這種注釋呢,心中有些期待,結果看到題目後有些小失望呢。

最後再吹兩句:

整體來說,三道題都有一定的實用價值,可以看出出題者用心了。

第一題是語言相關的,很常見,對介面的理解有一定要求;

第二題可以說有點語言影子,但其實這類問題是不區分語言一定要解決的了;

第三題完全可以說語言無關了,任何一門語言都可以解決。

Numpy實驗樓報告

最近入門machine learning,再看peter harrington的 machine learning in action 發現numpy真的是python語言裡非常重要的乙個庫,尤其在機器學習演算法的程式設計中有廣泛的應用。於是筆者到 實驗樓 上去學習了一波,下面吧連線 numpy的介...

實驗樓 玩轉函式

請在 home shiyanlou code寫出乙個minutestohours.py指令碼檔案,實現乙個函式hours 將使用者輸入的分鐘數轉化為小時數和分鐘數,並要求小時數盡量大。將結果以xx h,xx m的形式列印出來。注意列印格式中的空格 usr bin env python3 import...

實驗樓學習小記

linux基本操作總結圖 python基礎型別和基本語法 python的流程控制 python類基礎 class student object init 是乙個特殊方法用於在建立物件時進行初始化操作 通過這個方法我們可以為學生物件繫結name 和 age兩個屬性 def init self,name...