語言學習-effective go中文版Go是一個新雖然借鑒了現(xiàn)有但是它獨有特性可以使得高_第1頁
語言學習-effective go中文版Go是一個新雖然借鑒了現(xiàn)有但是它獨有特性可以使得高_第2頁
語言學習-effective go中文版Go是一個新雖然借鑒了現(xiàn)有但是它獨有特性可以使得高_第3頁
語言學習-effective go中文版Go是一個新雖然借鑒了現(xiàn)有但是它獨有特性可以使得高_第4頁
語言學習-effective go中文版Go是一個新雖然借鑒了現(xiàn)有但是它獨有特性可以使得高_第5頁
已閱讀5頁,還剩61頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

編寫的程序相比,大不相同。直接將C++或者Java程序轉(zhuǎn)換為Go程序,是不可能產(chǎn)生令人滿意的結(jié)果—Java程序是使用Java編寫的,而不是Go。另一方面,從Go有的約定也非常重要,例如命名,格式,程序結(jié)構(gòu),等等。這會使得其他Go程序員容易理解你編寫的程specification,TourofGo和HowtoWriteGoCode,然后將該文檔作為擴展閱讀。Gopageous旨在不僅作為來用,而且還可以作為如何使用語言例子。此,許多程序包都包含了可以在onog上獨執(zhí)行例子,例如這一個(如果需要,點單詞"pl來打開)格式化是一個最具爭議,但又無關(guān)緊要的問題。人們可以于不同的格式風格。但是,最好不必這樣,對于Gogofmt(go況,可以運行g(shù)ofmt;如果答案看起來不正確,則需要重新組織你的程序(或者提交一個關(guān)于 go 將會替你完成這些。給定一typetypeTstructnamestring//nameoftheobjectvalueint//itsvalue}typetypeTstruct string//nameoftheobjectvalueint //itsvalue}

x<<8x<<8+Go提供了C**/和C程序—同時又是網(wǎng)絡服務器—godoc,用來處理Go源文件,抽取有關(guān)程序包內(nèi)容的文檔。在頂層之 每個程序包都應該有一個包注釋,一個位于package子句之前的塊注釋。對于有多個文件的程序注 PackageregexpimplementsasimplelibraryforregularThesyntaxoftheregularexpressionsacceptedconcatenation{'|'concatenation}term['*'|'+'|'?']'['['^']character-ranges']''('regexp')'package////Packagepathimplementsutilityroutines要依靠用空格進行對齊 ,將會逐字的 。對于縮進的文本

godoc在程序包里面,任何直接位于頂 之前的注釋,都會作為 的文檔注釋。程序中每一個被導出////Compileparsesaregularexpressionandreturns,ifsuccessful,a//objectthatcanbeusedtomatchagainsttext.funcCompile(strstring)(regexp*Regexp,errerror){grepgodoc$$godocregexp|grepTisogp$$godocregexp|grepCompileparsesaregularexpressionandreturns,ifsuccessful,aRegexpparsed.Itsimplifiessafeinitializationofglobalvariablesholdingcannotbeparsed.Itsimplifiessafeinitializationofglobal$////Errorcodesreturnedbyfailurestoparseanexpression.var( =errors.New("regexp:internalerror")ErrUnmatchedLpar=errors.New("regexp:unmatched'('"))varvarcountLocksync.MutexinputCountuint32outputCountuint32errorCountuint32)Get方法importimport 使用小寫,一個單詞的名字;不需要使用下劃線或者混合大小寫。要力求簡短,因為每個使用你的程序包另一種約定是,程序包名為其源的基礎名;在src/pkg/encoding/base64。

,而不 bufio程序包中的帶緩沖的讀入類型叫做Reader,而不是BufReader,因為用戶看到的是bufio.Reader,一個清晰,簡明的名字。而且,因為被導入的實體總是通過它們的程序包名來尋址,所 和io.Reader并不。類似的,為ring.Ring創(chuàng)建一個新實例的函數(shù)—在Go中是定

once.Doonce.Do(setuponce.DoOrWaitUntilDone(setup適的。但是,在Get方法的名字中加上Get,是不符合語言的,并且也沒有必要。如果你有一個域叫owner(小寫,不被導出),則GetOwner(大寫,被導出),GetOwnerownerowner:=obj.Owner()ifowner!=user{} Read,Write,F(xiàn)lush,String等,都具有規(guī)范的簽名和含義。為了避免,不要為你的方法使用這些名字,除非其

,而不 或

這樣的單詞),breakbreakcontinuefallthroughreturn++--)gogofunc(){for{dst<-<-src} ,for,switch或select)的左大括號放ififi<{}ififi<f()// }類型Go的控制結(jié)構(gòu)與C的相關(guān),但是有重要的區(qū)別。沒有do或者whileforswitchifswitchforbreakcontinue(multiwaycommunicationsmultiplexer),ifx>ifx>{return}

構(gòu)體包含了一條控制語句,例 或 既 ififerr:=od(0664);err!=returnerr} f,f,err:=os.Open(name)iferr!=nil{return} f,f,err:=os.Open(name)iferr!=nil{return}d,err:=f.Stat()iferr!=nil{}f ff,f,err:=該語句了兩個變量 d,d,err:=d這看起來像是又 。但是,注 是d第一條語句中 ,而在第二條語句中只是被重新賦值。這意味著使用之前已 過vv 中,變 即使已經(jīng)被過,也可以出現(xiàn),前提是vv

該和v已有的在相同的作用域中(如果建一個新的變量§)

v初始化中相應的值是可以被賦 v ////LikeaCforinit;condition;post{//LikeaCwhileforcondition{//LikeaCfor(;;)for{}sumsum:=fori:=0;i<10;{sum+=} forforkey,value:=range{newMap[key]=}forforkey:=range{ifkey.expired(){delete(m,key)}}如果你只需要range中的第二項(value),則可以使用空白標識符,一個下劃線,來丟棄第一個:sumsum:=for_,value:=range{sum+=}語,用于指定一個單獨的Unicode編碼點。詳情參見thelanguagespecification)

forforpos,char:=range\x80\x80isanillegalUTF-8fmt.Printf("character%#Ustartsatbyteposition%d\n",char,}characterU+65E5'日'startsatbyteposition0characterU+672CcharacterU+65E5'日'startsatbyteposition0characterU+672Cstartsatbyteposition3characterU+FFFD'startsatbyteposition6characterU+8A9Estartsatbyteposition- 中運行多個-- )-////Reversefori,j:=0,len(a)-1;i<j;i,j=i+1,j-1a[i],a[j]=a[j],}Go

進行求值,直到找到匹配的。如 — 鏈寫成一 funcfuncunhex(cbyte){switchcase'0'<=c&&c<='9':returnc-'0'case'a'<=c&&c<='f':returnc-'a'+10case'A'<=c&&c<='F':returnc-'A'+10}return}funcfuncshouldEscape(cbyte){switchccase'','?','&','=','#','+',return}return} 語句來提前中止switch在Go中幾乎不怎么常見。不過,有時forn:=0;n<len(src);n+={switchcasesrc[n]<sizeOne:ifvalidateOnly{}size=1casesrc[n]<sizeTwo:ifn+1>=len(src)err=errShortInputbreakLoop}if{}size=update(src[n]+}}當然 語句////Comparereturnsanintegercomparingthetwobyte////Theresultwillbe0ifa==b,-1ifa<b,and+1ifa>bfuncCompare(a,b[]byte)int{fori:=0;i<len(a)&&i<len(b);{switchcasea[i]>b[i]:return1casea[i]<b[i]:return-1}}switchcaselen(a)>len(b):return1caselen(a)<len(b):return-1}return}switch還可用于獲得一個接口變量的動態(tài)類型。這種類型switch使用類型斷言的語法,在括號中使用 。如果switch在表達式中了一個變量,則變量會在每個子句中具有對應的類型。比較符合varvartt=functionOfSomeType()switcht:=t.(type){fmt.Printf("unexpectedtype%T",t) //%Tprintswhatevertypethascasebool:caseint:fmt.Printf("integer%d\n",t)case*bool://thastype//thastypefmt.Printf("pointertoboolean%t\n",*t)//thastype*boolcase*int:fmt.Printf("pointertointeger%d\n",*t)//thastype}- - funcfunc(file*File)Write(b[]byte)(nint,err

funcfuncnextInt(b[]byte,iint)(int,{for;i<len(b)&&!isDigit(b[i]);{}x:=for;i<len(b)&&isDigit(b[i]);{x=x*10+int(b[i])-}returnx,}你可以使用它來掃描輸入切 中的數(shù)字,如forfori:=0;i<{x,i=nextInt(b,i)}bb

funcfuncnextInt(b[]byte,posint)(value,nextPosint)funcfuncReadFull(rReader,buf[]byte)(nint,err{forlen(buf)>0&&err==nil{varnrintnr,err=r.Read(buf)n+=nrbuf=}}Go 語句用來調(diào)度一個函數(shù)調(diào)用(被延期的函數(shù)),使其在執(zhí)行defer的函數(shù)即將返回之前才////Contentsreturnsthefile'scontentsasastring.funcContents(filenamestring)(string,error){f,err:=os.Open(filename)iferr!=nil{return"",}deferf.Close()//f.Closewillrunwhenwe'revarresultbuf:=make([]byte,100)for{n,err:=result=append(result,buf[0:n]...)//appendisdiscussedlater.iferr!=nil{iferr=={}return"",err//fwillbeclosedifwereturn}}returnstring(result),nil//fwillbeclosedifwereturn}對 fori:=0;i<5;i++{fori:=0;i<5;i++{deferfmt.Printf("%d",i)}被延期的函數(shù)按照LIFO43210。一個更加真實funcfunctrace(sstring){fmt.Println("entering:",s)}funcuntrace(sstring){fmt.Println("leaving:",s)//Usethemlikethis:funca(){defer//do}funcfunctrace(sstring){fmt.Println("entering:",s)returns}funcun(s}funca()fmt.Println("ina")}funcb()fmt.Println("inb")}{}inbentering:ainaleaving:aleaving:b

使

和make T語言中,它并不初始化內(nèi)存,只是將其置零new(TTT

由于new返回的內(nèi)存是被置零的,這會有助于你將數(shù)據(jù)結(jié)構(gòu)設計成,每個類型的零值都可以使用,而不需要進一步初始化。這意味著,數(shù)據(jù)結(jié)構(gòu)的用戶可以使用new來創(chuàng)建數(shù)據(jù),并正確使用。例如,bytes.Buffer的文檔說道,"Buffer的零值是一個可以使用的空緩沖"。類似的,sync.Mutex沒有顯式的構(gòu)造器和Init方法。相反的, {locksync.Mutexbufferbytes.Buffer}vpvp

都不需要進一pp:=new(SyncedBuffer)//type*SyncedBuffervarvSyncedBuffer //typeSyncedBuffer 的例子funcfuncNewFile(fdint,namestring){iffd<0{}f:=new(File)f.fd=fdf.dirinfo=nilf.nepipe=0returnf}有許多這樣的模版。我們可以使用復合文字(composite literal)進行簡化,其為一個表達式,在每次求funcfuncNewFile(fdint,namestring){iffd<0{}f:=File{fd,name,nil,0}return&f}returnreturn&File{fd,name,nil,復合文字的域按順序排列,并且必須都存在。然而,通過field:value顯式地為元素添加標號,則初始化可returnreturn&File{fd:fd,name: aa:=[...]string{Enone:"noerror",Eio:"Eio",Einval:"invalidargument"}s:= {Enone:"noerror",Eio:"Eio",Einval:"invalidm:=map[int]string{Enone:"noerror",Eio:"Eio",Einval:"invalid)使 )

channel,并且返回一個初始化的(而不是置零),類型為T的值(而不是*T)。之所以有所不同,是因這三個類型的背后是象征著,對使用前必須初始化的數(shù)據(jù)結(jié)構(gòu)的。例如,slice是一個三項描述符,包含一個指向數(shù)據(jù)(在數(shù)組中)的指針,長度,以及容量,在這些項被初始化之前,slice都是的。對于 初始化數(shù)據(jù)結(jié)構(gòu),并準備好可用的值。例如,make([]int,make([]int,10, 這些例子闡釋 之間的差別varvarp*[]int=//allocatesslicestructure;*p==nil;rarelyvarv[]int=make([]int,100)//theslicevnowreferstoanewarrayof100varp*[]int=new([]int)*p=make([]int,100,//v:=make([]int,makemap,slice和channel,并且不返回指針。要獲得一個顯式的指針,使用

特別是,如果你給函數(shù)傳遞一個數(shù)組,其將收到一個數(shù)組的拷貝,而不是它的指針。數(shù)組的大小是其類型的一部分。型 和 是不同的。funcfuncSum(a*[3]float64)(sum{for_,v:=range*a{sum+=v}}array:=[...]float64{7.0,8.5,x:=Sum(&array)//Notetheexplicitaddress-of切片持有對底層數(shù)組的,如果你將一個切片賦值給另一個,二者都將同一個數(shù)組。如果函數(shù)接受針。因此,Read函數(shù)可以接受一個切片參數(shù),而不是一個指針和一個計數(shù);切片中的長度已經(jīng)設定了要的數(shù)據(jù)的上限。這是程序包中,類型的 funcfunc(file*File)Read(buf[]byte)(nint,errbb

n,n,err:=varvarnvarerrfori:=0;i<32;i++nbytes,e:=f.Read(buf[i:i+1])//Readonebyte.ifnbytes==0||e!=nil{err=e}n+=}只要還符合底層數(shù)組的限制,切片的長度就可以進行改變;直接將其賦值給它自己的切片。切片的容量可以通過內(nèi)建函 切片時 是合法的,并且返回funcfuncAppend(slice,data[]byte){l:=ifl+len(data)>cap(slice){////Allocatedoublewhat'sneeded,forfuturegrowth.newSlice:=make([]byte,(l+len(data))*2)//Thecopyfunctionispredeclaredandworksforanyslicetype.copy(newSlice,slice)slice=}fori,c:=rangedata{slice[l+i]=}return}Append

typetypeTransform[3][3]float64//A3x3array,reallyanarrayofarrays.typeLinesOfText[][]byte //Asliceofbyteslices.texttextLinesOfText{[]byte("Nowisthetime"),[]byte("forallgood[]byte("tobringsomefuntothe}種是獨立的分配每一個切片;另一種是分配單個數(shù)組,為其指定單獨的切片們。使用哪式取決于你構(gòu)建單個分配會更加有效。作為參考,這里有兩種方式的框架。首先是一次一行:////Allocatethetop-levelpicture:=make([][]uint8,YSize)//Onerowperunitof//Loopovertherows,allocatingthesliceforeachrow.fori:=rangepicture{picture[i]=make([]uint8,}//Allocatethetop-levelslice,thesameasbefore.picture//Allocatethetop-levelslice,thesameasbefore.picture:=make([][]uint8,YSize)//Onerowperunitofy.//Allocateonelargeslicetoholdallthepixels:=make([]uint8,XSize*YSize)//Hastype[]uint8eventhoughpictureis//Loopovertherows,slicingeachrowfromthefrontoftheremainingpixelsslice.fori:=rangepicture{picture[i],pixels=pixels[:XSize],}Map是便,強大的內(nèi)建數(shù)據(jù)結(jié)構(gòu),其將一個類型的值(key)與另一個類型的值(ment或au)只要其動態(tài)類型支持等于操作),結(jié)構(gòu)體和數(shù)組。切片不能作為map的,因為它們沒有定義等于操作。和切片類似,map持有對底層數(shù)據(jù)結(jié)構(gòu)的。如果將map傳遞給函數(shù),其對ma內(nèi)容做了改變,則這vartimeZone=map[string]int{vartimeZone=map[string]int{"UTC":"EST":-"CST":-"MST":-"PST":-}offsetoffset:=含的是整數(shù),則查找一個不存在的key0boolmap來實現(xiàn)一個集合。將map項設置為,來將值放在集合中,然后通過簡單的索引來進試。map[string]bool{"Ann":true,"Joe":true,}ifattended[}]{//willbefalse isnotinthe,"wasatthe

varvarsecondsintvarokboolseconds,ok=這被形象的稱作為“commaoktzseconds

funcfuncoffset(tzstring)intifseconds,ok:=timeZone[tz];{return}log.Println("unknowntimezone:",tz)return0}_ __,_,present:=

delete(timeZone,delete(timeZone,"PDT")//NowonStandardGo中的格式化打印使用了與C中 類似的風格,不過更加豐富和通用。這些函數(shù)位于fmt程序fmt.Printffmt.Fprintf

等等。字符串函數(shù) 你不需要提供一個格式串。對每 和Sprintf,都有另外一對相應的函數(shù),例如PrintlnPrintlnPrint o%d\n",23)fmt.Fprint(os.Stdout," o",23,"\n") o",23) o",格式化打印函 接口的對象;變 是常見的實例

varvarxuint64=1<<64-fmt.Printf("%d%x;%d%x\n",x,x,int64(x),v(代表“value”);PrintPrintln所產(chǎn)生的結(jié)果。而且,這個格式可以打印任意的的值,甚至是數(shù)組,切片,結(jié)構(gòu)體和map。這是fmt.Printf("%v\n",fmt.Printf("%v\n",timeZone)//orjust當然,map的key可能會按照任意順序被輸出。當打印一個結(jié)構(gòu)體時,帶修飾的格式%+v會將結(jié)構(gòu)體的域 typetypeTstructabc}t:=&T{7,-2.35,"abc\tdef"}fmt.Printf("%v\n",t)fmt.Printf("%+v\n",t)fmt.Printf("%#v\n",t)&{7&{7-2.35abc&{a:7b:-2.35c:abc &main.T{a:7,b:-2.35,c:"abc\tdef"}(注意符號&)qstring或[]byte % %String()TTT另一個方便的格式 ,其可以打印出值的類String()TTT funcfunc(t*T)String()string} 的值,同時需要指 vs.valuereceivers)我們 SprintfString如果Sprintf調(diào)用嘗試將接收者直接作為字符串進行打印,就會導致再次調(diào)用該方法,發(fā)生這樣的情況。typetypeMyStringfunc(mMyString)String()stringreturnfmt.Sprintf("MyString=%s",m)//Error:willrecur}typetypeMyStringfunc(mMyString)String()string}funcfuncPrintf(formatstring,erface{})(nint,errerror)在函 ,v就像是一個類型為[]interface{}的變量,但是如果其被傳遞給另一個可變參數(shù)的 ////Printlnprintstothestandardloggerinthemanneroffmt.Println.funcPrintln(erface{}){std.Output(2,fmt.Sprintln(v...))//Outputtakesparameters(int,}Sprintlnv

vv vv

funcfuncMin()intmin:=int(^uint(0)>>1)//largestintfor_,i:=rangea{ifi<{min=}}return}append

funcfuncappend(slice[]T,elements...T)TT

xx:=x=append(x,4,5,6) [12345但是,如果我們想按照我們的Append那樣做,給切片增加一個切片,那么該怎么辦?簡單:在調(diào)用點使 [12345xx:=y:=x=append(x,y...) ,則會因為類型錯誤而無法編譯 不 y y在Goiota

const =iota//valuebyassigningtoblankKBByteSize=1<<(10*iota))可以將一個方法,比 這樣的浮點類型funcfunc(bByteSize)String(){switch{caseb>=YB:returnfmt.Sprintf("%.2fYB",b/YB)caseb>=ZB:returnfmt.Sprintf("%.2fZB",b/ZB)caseb>=EB:returnfmt.Sprintf("%.2fEB",b/EB)caseb>=PB:returnfmt.Sprintf("%.2fPB",b/PB)caseb>=TB:returnfmt.Sprintf("%.2fTB",b/TB)caseb>=GB:returnfmt.Sprintf("%.2fGB",b/GB)caseb>=MB:returnfmt.Sprintf("%.2fMB",b/MB)caseb>=KB:}} 會打印 , 會打印 這里使 來實現(xiàn)ByteSize的String方法是安全的(避免了無窮遞歸),這并不是因為做了換,而是因為它是使用%f來調(diào) 只有當想要一個字符串

varvarhome=os.Getenv("HOME")user=os.Getenv("USER")gopath=os.Getenv("GOPATH"))

funcfuncinit()ifuser=={log.Fatal("$USERnot}ifhome==""home="/home/"+}ifgopath==""gopath=home+}//gopathmaybeoverriddenby--gopathflagoncommandline.flag.StringVar(&gopath,"gopath",gopath,"overridedefaultGOPATH")}vsByteSize(指針和接口除外)都可以定義方法(method);在上面有關(guān)切片的中,我們編寫了一個Append函數(shù)。我們還可以將其定義成切片的方法。為此,我typetypeByteSlicefunc(sliceByteSlice)Append(data[]byte)[]byte//Bodyexactlythesameas}

的指針funcfunc(p*ByteSlice)Append(data{slice:=//Bodyasabove,withoutthe*p=} 方法的樣子,像這樣funcfunc(p*ByteSlice)Write(data[]byte)(nint,err{slice:=//Againas*p=returnlen(data),}

varvarbfmt.Fprintf(&b,"Thishourhas%ddays\n",

Go中的接口為指定對象的行為提供了式:如果事情可以這樣做,那么它就可以在這里使用。我們

方法來實現(xiàn),而Fprintf

Less(i,jint)Less(i,jint)Swap(ijint)sort式器。在這個人造的例子中 同時符合這些條件typetypeSequence//Methodsrequiredbysort.Interface.func(sSequence)Len()int{return}func(sSequence)Less(i,jint){returns[i]<}func(sSequence)Swap(i,j{s[i],s[j]=s[j],}//Methodforprinting-sortstheelementsbeforeprinting.func(sSequence)String()string{str:="["fori,elem:=range{ifi>0{str+=""}str+= }returnstr+} 方法重復 之前,轉(zhuǎn)換為普通 ,則可以共享所做的工作funcfunc(sSequence)String(){return}

名字,這兩個類型(Sequence和[]int)是相同的,在它們之間進行轉(zhuǎn)換是合法的。該轉(zhuǎn)換并不創(chuàng)建新 typetypeSequence//Methodforprinting-sortstheelementsbeforeprintingfunc(sSequence)String()string{}(Sequence

typetypeStringer{String()}varvalueinterface{}//Valueprovidedbycaller.switchstr:=value.(type){casestring:caseStringer:}如果我們只關(guān)心一種類型該如何做?如果我們知道值為一個string,只是想將它抽取出來該如何做?只有 typeNamestrstr:=,的用法來安全地測試值是否為一個字符串:str,str,ok:=value.(string)ifok{fmt.Printf("stringvalueis:%q\n",}elsefmt.Printf("valueisnota} ififstr,ok:=value.(string);{return}elseifstr,ok:=value.(Stringer);{return} crc32.NewIEEEadler32.Newhash.Hash32Go程序中,用CRC-32算法來類似的方式可以使得在不同crypto程序包中的流算法,可以與鏈在一起的塊分離開crypto/cipher程序包中的Block接口,指定了塊的行為,即提供對單個數(shù)據(jù)塊的加密。然后,根

typetypeBlock{BlockSize()intEncrypt(src,dst[]byte)Decrypt(src,dst}typeStream{XORKeyStream(dst,src}////NewCTRreturnsaStreamthatencrypts/decryptsusingthegivenBlock//countermode.ThelengthofivmustbethesameastheBlock'sblocksize.funcNewCTR(blockBlock,iv[]byte)StreamNewCTRBlock

typetypeHandler} ////Simplecounterserver.typeCounterstruct{n}func(ctr*Counter)ServeHTTP(whttp.ResponseWriter,req{fmt.Fprintf(w,"counter=%d\n",}Fprintf

importimportctr:=new(Counter)////Simplercounterserver.typeCounterintfmt.Fprintf(w,"counter=%d\n",}////Achanneldsanotificationoneach//(Probablywantthechanneltobebuffered.)typeChanchan*http.Requestfunc(chChan)ServeHTTP(whttp.ResponseWriter,req{ch<-}

funcfunc}我們怎么將它轉(zhuǎn)換成HTTPArgServer編寫一個方法。程序含了這樣的代碼:////TheHandlerFunctypeisanadaptertoallowtheuse//ordinaryfunctionsasHTTPhandlers.Iffisa//withtheappropriatesignature,HandlerFunc(f)is//Handlerobjectthatcalls//ServeHTTPcallsf(c,func(fHandlerFunc)ServeHTTP(wResponseWriter,req{f(w,}HandlerFuncServeHTTPHTTPff要

////ArgumentfuncArgServer(whttp.ResponseWriter,req{fmt.Fprintln(w,}現(xiàn)在具有和HandlerFunc相同的簽名,所以其可以被轉(zhuǎn)換為那個類型,然后它的方法, f(c,當有人頁面/args時,在該頁上安裝的處理者就具有值ArgServer和類型HandlerFunc。HTTP服務將會調(diào)用該類型的方法ServeHTTP,將ArgServer作為接收者,其將轉(zhuǎn)而調(diào) (通過f(c,調(diào) )。然后,參數(shù)就被顯示出來了 另一次是在講maps結(jié)構(gòu)時??瞻讟俗R符可以賦值給任意變量或者為任意類型,只要忽略這些值不會來問題就可以。這有點像在Unix系統(tǒng)中 文件寫入數(shù)據(jù):它為那些需要出現(xiàn)但值其實可以忽空白標識符 數(shù)同時返回一個值和一個ooifif_,err:=os.Stat(path);{fmt.Printf("%sdoesnotexist\n",}應該總是檢查返回的error值,因為訴我們錯誤發(fā)生的原因。////Bad!Thiscodewillcrashifpathdoesnotexist.fi,_:=os.Stat(path)iffi.IsDir()fmt.Printf("%sisadiectory\n",}以下一段代碼包含了兩個未使用的導入包(fmtio)

import)funcmain()fd,err:=os.Open("test.go")iferr!=nil{}//TODO:use} import()var_=fmt.Printf//Fordebugging;deletewhendone.var_ //Fordebugging;deletewhenfuncmain()fd,err:=os.Open("test.go")iferr!=nil{}//TODO:use_=}注釋信息——這些規(guī)定使得將來很容易找并刪除這些語句。像上面例子中的導入的包,fmt或io,最終要么被使用,要么被刪除:使用空白標識符只是一種臨時性net/http/pprof包會在其導入階段調(diào)用init函數(shù),該函數(shù)HTTP處理程序以提供調(diào)試信息。這個包中importimport_符號。(如果導入的包不是的,而在程序中又沒有使用到其的符號,那么編譯器將報錯。)態(tài)完成的。例如,將一個*os.File類型傳入一個接受 encoding/json m,m,ok:=段錯誤檢查代碼中——那么可以使用空白標識符來忽略類型斷言的返回值:ifif_,ok:=val.(json.Marshaler);okfmt.Printf("value%voftype%Timplementsjson.Marshaler\n",val,}在某些情況下,須在包的確保某個類型確實滿足某個接口的定義。例如類型json.RawMessage,如果它要提供一種定制的JSON格式,就必須實現(xiàn)json.Marshaler接口,但是編譯 作,只不過不是用定制的方式。為了確保接口實現(xiàn)的正確性,可以在包,利用空白標識符進行一個全varvar_json.Marshaler=在該中,賦值語句導致了 到Marshaler的類型轉(zhuǎn)換,這要求*RawMessage必 內(nèi)嵌 這兩個接口,以下是它們的實現(xiàn)typetypeReaderinterfaceRead(p[]byte)(nint,err}typeWriterinterfaceWrite(p[]byte)(nint,err} ReadWriteReadWrite

////ReadWriteristheinterfacethatcombinestheReaderandWriterinterfaces.typeReadWriterinterface{}ReadWriterReaderWriter類似的想法也可以應用于結(jié)構(gòu)體的定義,其實現(xiàn)稍稍復雜一些。在bufio包中,有兩個結(jié)構(gòu)體類型:bufio.Reader和bufio.Writerio包中的類似接口。bufio包還實現(xiàn)了一個帶緩沖的////ReadWriterstorespointerstoaReaderanda//Itimplementsio.ReadWriter.typeReadWriterstruct{*Reader//*Writer//}typetypeReadWriter{reader*Readerwriter*Writer}為了使各字段對應的方法能滿 funcfunc(rw*ReadWriter)Read(p[]byte)(nint,err{return}用,換句話說 類型不僅具有bufio.Reader 兩個方法,同時也滿 這三個接口變成外部類型的方法,但是當這些方法被調(diào)用時,其接收的參數(shù)仍然是類型,而非外部類型。在本例中,一個bufio.ReadWriter類型的Read方法被調(diào)用時,其效果和調(diào)用我們剛剛實現(xiàn)的那個Read方法是一樣的,只不過前者接收的參數(shù)是ReadWriter的 typetypeJob{Command}JobLogLogf以及*log.LoggerLogger

funcfuncNewJob(commandstring,logger*log.Logger){return&Job{command,}jobjob:=&Job{command,log.New(os.Stderr,"Job:",用,正如之前在ReaderWriter結(jié)構(gòu)體的Read方法中實現(xiàn)的那樣??梢杂胘ob.Logger Job類型變 的*log.Logger字段。當需要重新定義Logger的方法時,這種 funcfunc(job*Job)Logf(formatstring,args{job.Logger.Logf("%q:%s", mand,fmt.Sprintf(format,}內(nèi)嵌類型會引入命字,但是解決的方法也很簡單。首先,一個名為X的字段或方法可以將其它名的類型隱藏在更的嵌套之中。假以 字段對其進行封裝

中也包含一個名 字段或方法,那么 本來已經(jīng)包含了一個名為的字段或方法,再繼續(xù)內(nèi)嵌log.Logger就是不對的。但假設這個重復的名字并沒有在定義之外的地方被個類型的字段有命名,但該字段名沒有被過,那么就不會引起任何問題由于需要考慮很多繁瑣的細節(jié)以保證對共享變量的正確型,使得并發(fā)編程在很多情況下都會變得異常復雜。o變量通過Cnnl相互傳遞真正在不同的執(zhí)行線程間共享數(shù)據(jù)oot可以某個變量。數(shù)據(jù)競爭問題在設計上被規(guī)避了為了鼓勵采用這種思維模式,其總結(jié)為句:CPU僅有的同步原語。Unix操作系統(tǒng)的管道(Pipeline)就是上述模型的一個很好實例。盡管Go語言的并發(fā)模型源自Hoare的CSP模型(CommunicatingSequentialProcesses,國內(nèi)譯為“通信順序進程”,之所以稱之為ootn,主要是由于現(xiàn)有的一些概念—“線程”、“協(xié)程”以及“進程”以準確描述其內(nèi)涵。每個ootne都對應一個非常簡單的模型:它是一個并發(fā)的函數(shù)執(zhí)行線索,并且在多個并發(fā)的oune間,資源是共享的。ootn非常輕量,創(chuàng)建的開銷不會比??臻g分配的開銷大多少。并且其初始棧空間很小——比如等待一個尚未到達的IO——其他Goroutine可以繼續(xù)執(zhí)行。我們在實現(xiàn)中了許多底層關(guān)于線程創(chuàng)在一個函數(shù)或是方法前加 & 里 符號在啟動一個命令。&gogolist.Sort()//runlist.Sortconcurrently;don'twaitforfuncfuncAnnounce(messagestring,delay{gofunc(){}()//Notetheparentheses-mustcallthe} cici:=make(chancj:=make(chanint,//unbufferedchannelof//unbufferedchannelofcs:=make(chan*os.File,100)//bufferedchannelofpointerstocc:=make(chanint)//Allocatea//Startthesortinagoroutine;whenitcompletes,signalonthechannel.gofunc(){c<-1//Sendasignal;valuedoesnot<-c//Waitforsorttofinish;discardsent帶緩沖區(qū)的nnlhdle函數(shù),該函數(shù)從hnlhnnl寫入

varvarsem=make(chanint,MaxOutstanding)funchandle(r*Request){<- //Waitforactivequeueto //Maytakealongsem<- //Done;enablenextrequestto}funcinit()fori:=0;i<MaxOutstanding;{sem<-}}funcServe(queuechan{forreq:=<-gohandle(req)//Don'twaitforhandleto}}這樣的設計會引入一個問題:ServeGoroutine,修 的實現(xiàn)來對Goroutine的創(chuàng)建進行限制。以下給出一個簡單的實現(xiàn),請注意其中包含一個funcfuncServe(queuechan{forreq:=rangequeue<-gofunc()process(req)//Buggy;seeexplanationbelow.sem<-1}}剛才說的BUG源自Go中for循環(huán)的實現(xiàn),循環(huán)的迭代變量會在循環(huán)中被重用,因此req變量會在所有 funcfuncServe(queuechan{forreq:=rangequeue<-gofunc(reqsem<-1}}funcfuncServe(queuechan{forreq:=rangequeue<-req:=req//Createnewinstanceofreqforthegoroutine.gofunc(){sem<-1}}reqreq:= Serve函數(shù)還需要一個額外的channel參數(shù),用來等待退出通知;當創(chuàng)建完所有的GoroutineServerchannelfuncfunchandle(queuechan{forr:=rangequeue{}}funcServe(clientRequestschan*Request,quitchanbool)//Startfori:=0;i<MaxOutstanding;{go}<-quit//Waittobetoldto}typetypeRequest{ resultChanchanint}funcfuncsum(a[]int)(s{for_,v:=rangea{s+=v}}request:=&Request{[]int{3,4,5},sum,make(chan//Sendrequest//Waitforfuncfunchandle(queuechan{forreq:=rangequeue{}}typetypeVector//Applytheoperationtov[i],v[i+1]...uptov[n-1].func(vVector)DoSome(i,nint,uVector,cchanint){for;i<n;i++{v[i]+=u.Op(v[i])}c<- //signalthatthispieceis}constconstNCPU=4//numberofCPUcoresfunc(vVector)DoAll(uVector){c:=make(chanint,NCPU)//Bufferingoptional fori:=0;i<NCPU;i++}//Drainthefori:=0;i<NCPU;i++ //waitforonetaskto}//All}提供一個物理CPU進行處理。任意數(shù)目的Goroutine可以阻塞在系統(tǒng)調(diào)用上,但默認情況下,在任意時設

,來告訴 注意“并發(fā)”和“并行”這兩個概念搞混:“并發(fā)”是指用一些彼此獨立的執(zhí)行模塊構(gòu)建程序;o非所有的并行編程模式都適用于o語言模型要進一步區(qū)分兩者的概念,請參考篇博客的關(guān)。并發(fā)編程的工具甚至可以更簡單的達一些非發(fā)的想法。下面提供一個示例,它從的個包里抽象而來的??蛻舳藦哪承┰础热缇W(wǎng)絡——循環(huán)接收數(shù)據(jù)。為了避免頻繁的分、內(nèi)緩沖,程序在實現(xiàn)了一個空閑鏈表,并一個f指針型nnl將其封裝。當該nnl為空,程序為其分一新的f對。旦息沖緒它會經(jīng)由 發(fā)送到服務器端。varvarfreeList=make(chan*Buffer,100)varserverChan=make(chan*Buffer){forvarb//Grababufferifavailable;allocateifnot.select{caseb=<-//Gotone;nothingmoretodo.//Nonefree,soallocateanewone.b=new(Buffer)} //Readnextmessagefromthenet.serverChan<-b //Sendtoserver.}}{forb:=<-serverChan //Waitforwork.//Reusebufferifthere'sroom.select{casefreeList<-//Bufferonfreelist;nothingmoretodo.//Freelistfull,justcarry}}}客戶端會嘗試從空閑鏈表freeList中獲取Buffer對象;如果沒有可用對象,則分配一個新的。服務器端會將用完的Buffer對象b加入到空閑鏈表freeList中,如果鏈表已滿,則將b丟棄,收集器會在未來某個時刻自動回收對應的內(nèi)存單元。(select語句中的default分支會在沒有其他case分支滿足條件時執(zhí)行,這意味著select語句塊不會阻塞。)以上就是一個LeakyBucketFreeList的簡單實現(xiàn),借助Go語 typetypeerror{Error()} ////PathErrorrecordsanerrorandtheoperation//filepaththatcausedit.typePathErrorstruct{Opstring //"open","unlink",etc.Pathstring//Theassociatedfile.Err //Returnedbythesystem}func(e*PathError)Error()stringreturne.Op+""+e.Path+":"+} openopen/etc/passwx:nosuchfileor即使它冒出來的時候距離真正錯誤發(fā)生時刻已經(jīng)間隔了很久,也不會給調(diào)試分析帶來很大,比直接輸出一句“nosuchfileordirectory”要友好的多。稱的前綴信息。例如在image包中,用來輸出未知類型的錯誤信息的格式是這樣的:“image:unknownformat”。對于需要確分錯誤息的調(diào)者,以通類型開關(guān)類型言的式查看體的誤并入錯誤的就 類型而言這些節(jié)信包含在個的 字段中,以被來進錯誤恢forfortry:=0;try<2;try++file,err=os.Create(filename)iferr==nil{}ife,ok:=err.(*os.PathError);ok&&e.Err=={deleteTempFiles()//Recoversomespace.}}e在上面例子中,第二 語句是另一種形式的類型斷言。如該斷言失敗,ok的值將為false且e的值eoktrue,說明當前的錯誤,也就是

該方法返回一個字節(jié)計數(shù)值和一個error變量。但是對于那些不可恢復的錯誤,比如錯誤發(fā)生后程序?qū)⒉?/p>

////AtoyimplementationofcuberootusingNewton'smethod.funcCubeRoot(xfloat64)float64{z:=x/3//Arbitraryinitialvaluefori:=0;i<1e6;i++{prevz:=z-=(z*z*z-x)/(3*z*z)ifveryClose(z,prevz)return}}//Amillionit

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論