前記:本解題方法僅為一家之言
有任何不明題意的地方,請自行移步:
第一題:透明地修改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...