Go學習筆記Part 2-基本觀念練習 - Medium

文章推薦指數: 80 %
投票人數:10人

本篇不會細談Go語言的觀念, 僅做範例分享, 以補強基本觀念.. “Go學習筆記Part 2-基本觀念練習” is published by Dorr Lin. GetstartedOpeninappDorrLinSigninGetstarted6FollowersAboutGetstartedOpeninappGo學習筆記Part2-基本觀念練習DorrLinApr24,2019·26minread本篇不會細談Go語言的觀念,僅做範例分享,以補強基本觀念.如果是初學者,強烈推薦先閱讀或搭配閱讀其他基本觀念教學,推薦網站:Go語言Go語言玩弄中...XDopenhome.cc當然Google上也還有其他教學,挑一個適合自己的,如果你英文閱讀沒問題當然看官方文件是最好的:TheGoProgrammingLanguageSpecification-TheGoProgrammingLanguageTheforma...brepresentsthesetofcharactersfromathroughbasalternatives.Thehorizontalellipsis...isalso…golang.org進入主題.var關鍵字與Printfvar是宣告變數的關鍵字,Printf則是fmt套件中的function,可以指定列印格式.var宣告有兩種方式:個別宣告varaint=5varbstring=“bstring.”varcbool=falsefmt.Printf(“var:a=%d,b=%s,c=%t\n”,a,b,c)執行結果var:a=5,b=bstring.,c=false群體宣告var(dstring=“dstring.”efloat64=1.12fuint8=234)fmt.Printf(“var:d=%s,e=%g,f=%d\n”,d,e,f)執行結果var:d=dstring.,e=1.12,f=234除了var宣告方式不同外,也可以注意Printf中的指定格式,格式前面加’%’.例如:%s—列印字串;%d—列印整數;%g—列印浮點數;%t—列印布林值…等.詳細請至官方文件查閱Printf說明.變數值的交換過往傳統數值交換的寫法通常會借用暫存變數以交換,以Java為例inta,b,tmp;a=1;b=2;tmp=a;a=b;b=tmp;但Go可以這樣寫:g:=127.3h:=231.1fmt.Printf(“Beforeexchangeg,h—g:%g,h=%g\n”,g,h)g,h=h,gfmt.Printf(“Afterexchangeg,h—g:%g,h=%g\n”,g,h)執行結果Beforeexchangeg,h—g:127.3,h=231.1Afterexchangeg,h—g:231.1,h=127.3直接交換.其中”g:=127.3”這種寫法在Go中稱作短宣告,等同於”floatg=127.3”.常數(const)const也跟var一樣用兩種宣告方式:constcon_ifloat32=3.1416const(con_juint8=253con_kstring=“Thisisaconstantstring.”//con_l:=18.7;con_l=18.7con_m=-140con_n=787)//fmt.Printf(“con_j+con_l=%g\n”,(con_j+con_l))fmt.Printf(“con_n+con_l=%g\n”,(con_n+con_l))fmt.Printf(“con_n+con_m=%d\n”,(con_n+con_m))fmt.Printf(“con_m+con_l=%g\n”,(con_m+con_l))執行結果con_n+con_l=805.7con_n+con_m=647con_m+con_l=-121.3請注意註解,const不接受短宣告;2.Go在做數字運算時需要注意:a.指定型態時,不能與其他變數運算(eg.con_j+con_l);b.未指定型態下,以最後一個數字的型態為主,前面的數字將會被轉成該型態(eg.con_n+con_m);constiota(常數列舉)const(iota_o=3iota_p=iotaiota_q=iotaiota_r)fmt.Printf(“constantiota(常數列舉)iota_o=%d,”+“iota_p=%d,iota_q=%d,iota_r=%d.\n”,iota_o,iota_p,iota_q,iota_r)執行結果constantiota(常數列舉)iota_o=3,iota_p=1,iota_q=2,iota_r=3.當在常數宣告中使用iota,則會從1依序開始指定值給變數,iota之後未給值的變數也會依序被指定.另外此例的Printf也示範換行寫法,Printf程式換行除了用`這個符號外,用+號與雙引號串接,並加+必須放於字串尾端.字串str1:=“str1”str2:=“str2”varstr3stringfmt.Printf(“concatstr1andstr2:%s\n”,(str1+““+str2))fmt.Printf(“emptystring3:%q\n”,str3)執行結果concatstr1andstr2:str1str2emptystring3:””為指定值的str3,預設是空字串,而不是nil.示範三種計算字串長度的方式:varstr4string=“Go語言”fmt.Printf(“countstr4lengthbylen:%d\n”,len(str4))fmt.Printf(“countstr4lengthbylenandrune:%d\n”,len([]rune(str4)))fmt.Printf(“countstr4lengthbypackageunicodeutf-8:%d\n”,utf8.RuneCountInString(str4))執行結果countstr4lengthbylen:8countstr4lengthbylenandrune:4countstr4lengthbypackageunicodeutf-8:4len(…)—依據檔案的編碼計算字串位元組,目前這個go檔是utf-8編碼;len([]rune(…))—計算字串長度,也是一般應用在算字串長度時的寫法;utf8.RuneCountInString(str4)—以unicode套件的函式計算字串長度,也是依據go檔的編碼不同會有不同結果;複製字串:str4byte:=[]byte(str4)str4byte[0]=103str5:=string(str4byte)fmt.Printf(“Thestr4bytecopiesstr4(%s”+“),thestr5ismodifiedfromthestr4copy:%s\n”,str4,str5)fmt.Printf(“Theword‘語’indexis%dinstr5.”,strings.Index(str5,“語”))執行結果Thestr4bytecopiesstr4(Go語言),thestr5ismodifiedfromthestr4copy:go語言Theword‘語’indexis2instr5.這個範例有4個技巧—字串複製:str4byte:=[]byte(str4),用byte陣列儲存字串位元;修改字串:str4byte[0]=103,修改第一個字元,將大寫’G’改成小寫’g’;字串位元儲存為字串:str5:=string(str4byte);取得字元在字串中的位置:strings.Index(str5,“語”);所以字串複製是透過byte陣列與string函數達成,並且在修改複製的字串(str4byte[])不會影響原來的字串,所以str5的內容為”go語言”.透過string.Index取得字元位置,起始值為0.範例—字串走訪:str6:=“中華民國song”fori,char:=rangestr6{fmt.Printf(“迴圈走訪str6,字元%#U在位置%d.\n”,char,i)}執行結果迴圈走訪str6,字元U+4E2D‘中’在位置0.迴圈走訪str6,字元U+83EF‘華’在位置3.迴圈走訪str6,字元U+6C11‘民’在位置6.迴圈走訪str6,字元U+570B‘國’在位置9.迴圈走訪str6,字元U+0073‘s’在位置12.迴圈走訪str6,字元U+006F‘o’在位置13.迴圈走訪str6,字元U+006E’n’在位置14.迴圈走訪str6,字元U+0067‘g’在位置15.走訪字串是以位元方式執行,每個字根據語言的不同會佔用不一樣的位元大小,意思就是i並非以1的固定大小遞增.在此例中,中文字佔3bytes’華’位置會在3(起始);‘民’在6而英文字’s’則在12,所以在比對字串需要特別注意.字串比對:stra:=“Iamstra.\n”strb:=“\r\nIamstrb.”stra2:=“Iamstra.”/*strings.TrimXXX是去除前後的跳脫字元*/fmt.Printf(“TrimRightstra—%s,stra2—%s,straequalsstra2:%t\n”,strings.TrimRight(stra,“\n”),stra2,(strings.TrimRight(stra,“\n”)==stra2))fmt.Printf(“TrimLeftstrb—%s,stra2—%s,strbequalsstra2:%t\n”,strings.TrimLeft(strb,“\r\n”),stra2,(strings.TrimLeft(strb,“\r\n”)==stra2))執行結果TrimRightstra—Iamstra.,stra2—Iamstra.,straequalsstra2:trueTrimLeftstrb—Iamstrb.,stra2—Iamstra.,strbequalsstra2:false陣列宣告陣列的幾個方式vararr1[10]intarr1[0]=20arr1[1]=33//arr1index2~9未指定者補0arr2:=[10]int{1,2,3,4,5,6}//後面不足者補0//WrongSyntax//vararr4[…]int//arr4[0]=11//arr4[1]=29arr3:=[…]int{9,8,10,600}//動態陣列大小宣告方式arr4:=arr3//複製arr3給arr4fmt.Printf(“arr1=%v\n”,arr1)//印出值fmt.Printf(“arr2=%#v\n”,arr2)//印出詳細內容fmt.Printf(“arr3=%+v\n”,arr3)//適合用於struct,interface.fmt.Printf(“arr4copiedfromarr3=%#v\n”,arr4)執行結果arr1=[203300000000]arr2=[10]int{1,2,3,4,5,6,0,0,0,0}arr3=[9810600]arr4copiedfromarr3=[4]int{9,8,10,600}在這個例子中有兩個需要注意的地方,動態陣列無法先宣告再給值,arr4的宣告範例是錯的,要像arr3這樣;在格式化列印Printf中的%v有幾種列印格式,‘%v’—純印陣列值;’%#v’—印出陣列詳細內容;‘%+v’—適合用來印struct或interface的格式.二維陣列arr5:=[3][2]int{[2]int{3,4},[2]int{17,87},[2]int{0,0}}//3個size為2的陣列//arr6:=[2][…]int{[…]int{4,5,6,7},[…]int{1,20}}WrongSyntaxarr6:=[…][4]int{[4]int{4,5,6,7},[4]int{1,20,8,9}}//n個size為4的陣列fmt.Printf(“arr5=%#v\n”,arr5)fmt.Printf(“arr6=%#v\n”,arr6)fori,e:=rangearr1{fmt.Printf(“arr1[%d]=%d\n”,i,e)}forj:=0;jcap(sl3)=6.Slice與指標Slice是一種指標形式的陣列,也就是所謂的傳址(陣列是傳值),所以當變數參考同一組值時,操作任何一變數都會改到值.承上例:arr1[0]=60sl3[0]=33fmt.Printf(“modifiedarr1=%#v\n”,arr1)fmt.Printf(“modifiedsl3:%#v\n”,sl3)執行結果modifiedarr1=[7]int{60,33,40,3,8,90,21}modifiedsl3:[]int{33,40}其他建立Slice的範例varsl4[]int//空slicesl5:=make([]int,3,10)//len:3,cap:10sl6:=arr1[0:4:4]//擷取arr1從第0取4個,cap:4sl7:=arr1[:3]//擷取arr1從第0取3個,cap:7sl8:=sl6[:2]//參考sl6,從0後的2個,cap=sl6:4fmt.Printf(“sl4:%#v\n”,sl4)fmt.Printf(“sl5:%#v\n”,sl5)fmt.Printf(“sl6:%#v,length—%d,cap—%d\n”,sl6,len(sl6),cap(sl6))fmt.Printf(“sl7:%#v,length—%d,cap—%d\n”,sl7,len(sl7),cap(sl7))fmt.Printf(“sl8:%#v,length—%d,cap—%d\n”,sl8,len(sl8),cap(sl8))執行結果sl4:[]int(nil)sl5:[]int{0,0,0}sl6:[]int{60,33,40,3},length—4,cap—4sl7:[]int{60,33,40},length—3,cap—7sl8:[]int{60,33},length—2,cap—4Sliceappendarr2:=[3]int{2,3,4}sl9:=arr2[0:1]sl10:=append(sl9,7,8)sl11:=append(sl9,34,5,1)fmt.Printf(“sl9=%#v,cap?%d\n”,sl9,cap(sl9))fmt.Printf(“sl10=%#v,cap?%d\n”,sl10,cap(sl10))fmt.Printf(“sl11=%#v,cap?%d\n”,sl11,cap(sl11))執行結果sl9=[]int{2},cap?3sl10=[]int{2,7,8},cap?3sl11=[]int{2,34,5,1},cap?6這個例子有幾個技巧:sl10和sl11都是從sl9append元素建立的Slice—sl9和sl10的容量(cap)都是3;sl11的容量(cap)為6,這是因為sl10所append的總數在sl9的容量內,所以s10的容量維持與來源sl9相同.sl11則超過sl9的容量,所以會擴充容量以放多出來的元素,而擴充數是以2倍計.前面提過slice是一種陣列的指標,所以底層仍為陣列,在此例中sl9和sl10的陣列之參考為同一個arr2.sl11因為做了擴充進而建立另一組陣列,所以sl11底層陣列已不再是arr2.複製Slice—copysl12:=make([]int,len(sl11),cap(sl11))copy(sl12,sl11)//copy(目地,來源)sl12[1]=10fmt.Printf(“sl12=%#v,cap?%d\n”,sl12,cap(sl12))執行結果sl12=[]int{2,10,5,1},cap?6copy有兩個要注意的點:目地(dest)slice的容量(cap)要足夠放來源(src)slice的元素,否則會發生capoutofrange錯誤.dest與src的參考是完全獨立的,所以變動內容不會相互影響,如此例sl12[1]變為10,sl11[1]仍為34.資料集—MapType建立map方式score:=make(map[string]int)//map[(keytype)](valuetype)fmt.Printf(“scoremap:%#v\n”,score)fmt.Printf(“scoremaplength:%d\n”,len(score))score[“John”]=87score[“Mary”]=100fmt.Printf(“setupscoremap:%#v\n”,score)emap1:=map[string]int{}//相當於sorce的宣告varemap2map[string]intfmt.Printf(“emptymap1:%v\n”,emap1)fmt.Printf(“emptymap2:%v\n”,emap2)score2:=map[string]int{“Bob”:77,“Joe”:16,}fmt.Printf(“score2map:%#v\n”,score2)執行結果scoremap:map[string]int{}scoremaplength:0setupscoremap:map[string]int{“John”:87,“Mary”:100}emptymap1:map[]emptymap2:map[]score2map:map[string]int{“Bob”:77,“Joe”:16}注意:keytype只能是Go中comparabletype,如-bool;數字;字串;指標;channel;interface;struct以及這些的陣列.slice,map和func均不能當key.承上例:fmt.Printf(“score2[‘David’]:%d\n”,score2[“David”])sc2_value,exist:=score2[“David”]fmt.Printf(“score2[‘David’]returnsvalue:%d,exists:%t\n”,sc2_value,exist)_,exist2:=score2[“Kim”]ifexist2{fmt.Printf(“score2[‘Kim’]value:%d\n”,score2[“Kim”])}if_,exist3:=score2[“Cherry”];exist3{fmt.Printf(“score2[‘Cherry’]value:%d\n”,score2[“Cherry”])}執行結果score2[‘David’]:0score2[‘David’]returnsvalue:0,exists:false這裡示範幾個技巧:當map中沒有此key時,則回傳初始值(int初始為0);在Go中函數允許回傳多值,在func會提更多(mapreturns:value,exists)參考exist,exist2和exist3…;接收多回傳值時如果要忽略某個值,可使用’_’,如exist2,exist3…;map—deletedelete(score,“John”)fmt.Printf(“Delete‘John’fromscore:%v\n”,score)執行結果Delete‘John’fromscore:map[Mary:100]走訪mapscore3:=map[string]int{“John”:60,“Monica”:88,“Cherry”:80,“Bob”:100,“Mary”:30,}fork,v:=rangescore3{fmt.Printf(“score3key:%s,value:%d\n”,k,v)}fork:=rangescore3{fmt.Printf(“score3key:%s\n”,k)}for_,v:=rangescore3{fmt.Printf(“score3value:%d\n”,v)}執行結果score3key:Bob,value:100score3key:Mary,value:30score3key:John,value:60score3key:Monica,value:88score3key:Cherry,value:80score3key:Monicascore3key:Cherryscore3key:Bobscore3key:Maryscore3key:Johnscore3value:60score3value:88score3value:80score3value:100score3value:30按key順序走訪mapvarsc3Count=len(score3)varkeys=make([]string,sc3Count,sc3Count)variint=0fork:=rangescore3{keys[i]=ki++}sort.Strings(keys)//透過packagesort進行字串排序//slice回傳值為index,valuefor_,k:=rangekeys{fmt.Printf(“score3[‘%s’]=:%d\n”,k,score3[k])}執行結果score3[‘Bob’]=:100score3[‘Cherry’]=:80score3[‘John’]=:60score3[‘Mary’]=:30score3[‘Monica’]=:88常用運算子+,—,*,/跟其他語言一樣注意運算順序,用於整數,浮點數與複數;%是取餘數,僅用於整數;+也可用於字串串接;&,|,^,&^僅用於整數;<>僅用於整數;底下簡單示範+,-,*,/,%:fmt.Printf(“先乘除後加減:1+2*3=%d\n”,(1+2*3))fmt.Printf(“括號先做:(2+2+8)/4=%d\n”,((2+2+8)/4))fmt.Printf(“取餘數:10%%3=%d\n”,10%3)//印%號就用兩個%%執行結果先乘除後加減:1+2*3=7括號先做:(2+2+8)/4=3取餘數:10%3=1++與——variint16=0//varjint16=++i;WrongSyntax.//varjint16=i++;WrongSyntax.//++i;WrongSyntax.i++varjint16=i//fmt.Printf(“i=%d\n”,i++);WrongSyntax.fmt.Printf(“i=%d,j=%d\n”,i,j)執行結果i=1,j=1++和—只能用在變數後方:i++ori—,不能寫++ior——i;不能在指定運算子(=)或函式(例如:Print)上使用;比較運算子比較運算子→,=,<=,==,!=;運算結果為trueorfalse;被視為comparable(可比較)的資料型態才能使用這些運算子—布林,整數,浮點數,字串,指標,Channel,Interface,非Interface之值與Interface可比較Struct,陣列等.Slice,Map,func均不能比較;Go沒有三元運算子(eg.a=1?1:0);底下示範‘==’運用.varaint16=1varbint16=2varcstring=“iamastring”vardstring=“iamastring”vareint16=200varf*int16f=&efmt.Printf(“a=b?%t\n”,(a==b))fmt.Printf(“c=d?%t\n”,(c==d))//fmt.Printf(“e=f?%t\n”,(e==f))WrongSyntax,型態不同不能比較,e是int16,f為指標型態.fmt.Printf(“&e=f?%t\n”,(&e==f))執行結果i=1,j=1a=b?falsec=d?true&e=f?true==和!=直接拿來比變數內容值,因為不是像Java的equals方法,Java除基本型態多數以傳址處理,所以c,d兩個就可以比較內容.而&e,f值為記憶體位置,所以是比較是否為相同記憶體位置.更多指標用法會在後續介紹.if…else/elseif必須要有{};if,elseif的判斷式不需();若有運算式可像for的寫法,以分號(;)隔開,第一塊為運算,第二塊為判斷;//i為區域變數,僅在該if區塊中有用ifi:=2–1;i==1{fmt.Printf(“iin‘if’block=%d\n”,i)}//注意運算式的地方,是i,j:=k/2,k%2形式,而不能寫成i:=k/2,j:=k%2.k:=100ifi,j:=k/2,k%2;i==50&&j==0{fmt.Printf(“test‘if’,i=%d;j=%d\n”,i,j)}執行結果iin‘if’block=1test‘if’,i=50;j=0switch不像其他corjava語言,不需靠break跳開case,本身不會往下執行;如果要像corjava往下一個case走可用fallthrough.switch可同時寫宣告式與運算式.case後面不一定是常數,也可以做運算.結果與switch相同就會進到該case.switcha:=100;a/2{case10:fmt.Println(“switchcase10.”)case20:fmt.Println(“switchcase20.”)case30:fmt.Println(“switchcase30.”)case50:fmt.Println(“switchcase50.”)fallthroughdefault:fmt.Println(“switchnotmatched.”)}score:=60switch{casescore>=90:fmt.Println(“ScoreLevel=A”)casescore>=70&&score<90:fmt.Println(“ScoreLevel=B”)casescore>=50&&score<70:fmt.Println(“ScoreLevel=C”)default:fmt.Println(“ScoreLevel=D”)}執行結果switchcase50.switchnotmatched.ScoreLevel=CDorrLinJava,AndroidDeveloperFollowGoGolangMorefromDorrLinFollowJava,AndroidDeveloper



請為這篇文章評分?