A Tour of Goを完走した
1周目はよくわからなかったけど、よく読んで2周目やってみたら意外と解けました。
ということで、一通りやってみたコードのまとめ。
もっとスマートにできたりすると思う。
Loops and Functions
関数とループを使った簡単な練習として、平方根の計算を実装してみましょう: 数値 x が与えられたときに z² が最も x に近い数値 z を求めたいと思います。
func Sqrt(x float64) float64 { z := 1.0 for i := 0; i < 10; i++ { z -= (z*z - z) / (2 * z) } return z }
次に値が変化しなくなった (もしくはごくわずかな変化しかしなくなった) 場合にループを停止させます。 それが 10 回よりも多いか少ないかを確認してください。
この文章が本当に理解できず、悩みました。
「ごくわずかな変化ってなんやねん」って感じで・・・。
下の方にニュートン法のことが触れられていて、そっちを読むとなんとなく理解し始めました。
func Sqrt(x float64) float64 { z := 1.0 var prev float64 for i := 0; i < 10; i++ { z -= (z*z - z) / (2 * z) if math.Abs(prev-z) < 1e-10 { fmt.Printf("%d回目\n" , i+1) break } prev = z } return z }
どんなもんなのかを課題解いたよ!って記事のコードいくつか読んでみて噛み砕いてからやりました。
ただ、1e-10
のところが実装者によって様々で、1e-10
、1e-6
、0.0001
とかいろいろあって、理解していないときは「なんで?」ってなりました。
ここが誤差の許容範囲みたいな感じで理解してます。
Slices
Pic 関数を実装してみましょう。 このプログラムを実行すると、生成した画像が下に表示されるはずです。
ここは問題文通り、+ヒント参考にしながらやればなんとかなりました。
func Pic(dx, dy, int) [][]uint8 { pic := make([][]uint8, dy) for y, _ := range pic { pic[y] = make([]uint8, dx) for x, _ := range pic[y] { pic[x][y] = uint8(x*y) } } return pic }
uint8(xxx)のところは、
生成する画像は、好きに選んでください。例えば、面白い関数に、 (x+y)/2 、 x*y 、 xy などがあります。
の部分なので、好きな計算式入れてあげると良いです。
画像変わって面白いですよ。なんならここに示されている計算式以外でも変な画像できます。
Maps
WordCount 関数を実装してみましょう。string s で渡される文章の、各単語の出現回数のmapを返す必要があります。 wc.Test 関数は、引数に渡した関数に対しテストスイートを実行し、成功か失敗かを結果に表示します。
一番分かりやすかった気がするのは、普段JSで大体なんでもオブジェクトでKey:Valueにしてなんかしちゃうのが原因?
func WordCount(s string) map[string]int { wordList := strings.Fields(s) m := make(map[string]int) for _, word := range wordList { if _, exist := m[word]; exist { m[word]++ } else { m[word] = 1 } } return m }
Fibonacci closure
fibonacci (フィボナッチ)関数を実装しましょう。この関数は、連続するフィボナッチ数(0, 1, 1, 2, 3, 5, ...)を返す関数(クロージャ)を返します。
クロージャの問題。
フィボナッチ数列自体は分かってたのですが、最初の0,1は固定なんですね・・・。
func fibonacci() func() int { fib := 0 result := [2]int{0,0} return func() int { if fib == 1 { result[1] = 1 } else { w := result[1] result[1] = result[0] + result[1] result[0] = w } fib++ return result[1] } }
さすがにもっとスマートに書けるんじゃないかなこれ。
Stringers
IPAddr 型を実装してみましょう IPアドレスをドットで4つに区切った( dotted quad )表現で出力するため、 fmt.Stringer インタフェースを実装してください。 例えば、 IPAddr{1, 2, 3, 4} は、 "1.2.3.4" として出力するようにします。
func (ip IPAddr) String() string { return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]) }
Errors
Sqrt 関数を 以前の演習 からコピーし、 error の値を返すように修正してみてください。 Sqrt は、複素数をサポートしていないので、負の値が与えられたとき、nil以外のエラー値を返す必要があります。
type ErrNegativeSqrt float64 func (x ErrNegativeSqrt) Error() string { return fmt.Sprintf("cannot Sqrt negative number %f", float64(x)) } func Sqrt(x float64) (float64, error) { if x < 0 { return 0, ErrNegativeSqrt(x) } z := 1.0 var prev float64 for i := 0; i < 10; i++ { z -= (z*z - z) / (2 * z) if math.Abs(prev-z) < 1e-10 { fmt.Printf("%d回目\n" , i+1) break } prev = z } return z, nil }
Readers
ASCII文字 'A' の無限ストリームを出力する Reader 型を実装してください。
無限ストリームがイマイチピンとこなくて調べました。 ほぼそのままの意味でした()
b[i] = "A"
としていてtype errorになって、ここではじめて'A'
であることに気付いた。
学び。
func (r MyReader) Read(b []byte) (int, error) { for i := range b { b[i] = 'A' } return len(b), nil }
rot13Reader
io.Reader を実装し、 io.Reader でROT13 換字式暗号( substitution cipher )をすべてのアルファベットの文字に適用して読み出すように rot13Reader を実装してみてください。
type rot13Reader struct { r io.Reader } func (r rot13Reader) Read(b []byte) (n int, err error) { n, err = r.r.Read(b) if err != nil { return } for i, c := range b[:n] { if 'A' <= c && c <= 'Z' { b[i] = (c-'A'+13)%26 + 'A' } else if 'a' <= c && c <= 'z' { b[i] = (c-'a'+13)%26 + 'a' } } return }
問題文中にあるgzip.NewReader
へのリンクがあったので、コード読んでると、r.r.Read
のヒントが得られました。
読んだら書けました。
Image
前に解いた、画像ジェネレーターを覚えていますか? 今回は、データのスライスの代わりに image.Image インタフェースの実装を返すようにしてみましょう。 自分の Image 型を定義し、 インタフェースを満たすのに必要なメソッド を実装し、 pic.ShowImage を呼び出してみてください。
ドキュメント読んだらそれっぽくできました。
とりあえず、w, hだけ持たせて、呼び出しで大きさ決めれるようにはしておきました。
import ( "golang.org/x/tour/pic" "image" "image/color" ) type Image struct { w int h int } func (i Image) ColorModel() color.Model { return color.RGBAModel } func (i Image) Bounds() image.Rectangle { return image.Rect(0, 0, i.w, i.h) } func (i Image) At(x int, y int) color.Color { return color.RGBA{0, 0, uint8(x ^ y), uint8(x ^ y)} } func main() { m := Image{180, 180} pic.ShowImage(m) }
Exercise: Equivalent Binary Trees
https://go-tour-jp.appspot.com/concurrency/7
問題文長いので↑で確認してください。。。
Tree.Left, Tree.Rightが*Treeであることに気付けたら、あとは早かったです。
package main import ( "fmt" "golang.org/x/tour/tree" ) // Walk walks the tree t sending all values // from the tree to the channel ch. func Walk(t *tree.Tree, ch chan int) { recursiveWalk(t, ch) close(ch) } func recursiveWalk(t *tree.Tree, ch chan int) { if t.Left != nil { recursiveWalk(t.Left, ch) } ch <- t.Value if t.Right != nil { recursiveWalk(t.Right, ch) } } // Same determines whether the trees // t1 and t2 contain the same values. func Same(t1, t2 *tree.Tree) bool { ch1 := make(chan int) ch2 := make(chan int) go Walk(t1, ch1) go Walk(t2, ch2) for { c1, ok1 := <-ch1 c2, ok2 := <-ch2 switch { case !ok1, !ok2: return ok1 == ok2 case c1 != c2: return false } } } func main() { ch := make(chan int) go Walk(tree.New(1), ch) for c := range ch { fmt.Println(c) } fmt.Println(Same(tree.New(1), tree.New(1))) fmt.Println(Same(tree.New(1), tree.New(2))) }
Exercise: Web Crawler
package main import ( "fmt" "sync" "time" ) type Fetcher interface { // Fetch returns the body of URL and // a slice of URLs found on that page. Fetch(url string) (body string, urls []string, err error) } // Crawl uses fetcher to recursively crawl // pages starting with url, to a maximum of depth. func Crawl(url string, depth int, fetcher Fetcher) { cacheUrl := make(map[string]int) var mux sync.Mutex var crawl func(string, int) crawl = func(url string, depth int) { if depth <= 0 { return } if _, ok := cacheUrl[url]; ok { return } mux.Lock() cacheUrl[url]++ mux.Unlock() body, urls, err := fetcher.Fetch(url) if err != nil { fmt.Println(err) return } fmt.Printf("found: %s %q\n", url, body) for _, u := range urls { go crawl(u, depth-1) } } go crawl(url, depth) time.Sleep(time.Second) fmt.Println(cacheUrl) return } func main() { Crawl("https://golang.org/", 4, fetcher) } // fakeFetcher is Fetcher that returns canned results. type fakeFetcher map[string]*fakeResult type fakeResult struct { body string urls []string } func (f fakeFetcher) Fetch(url string) (string, []string, error) { if res, ok := f[url]; ok { return res.body, res.urls, nil } return "", nil, fmt.Errorf("not found: %s", url) } // fetcher is a populated fakeFetcher. var fetcher = fakeFetcher{ "https://golang.org/": &fakeResult{ "The Go Programming Language", []string{ "https://golang.org/pkg/", "https://golang.org/cmd/", }, }, "https://golang.org/pkg/": &fakeResult{ "Packages", []string{ "https://golang.org/", "https://golang.org/cmd/", "https://golang.org/pkg/fmt/", "https://golang.org/pkg/os/", }, }, "https://golang.org/pkg/fmt/": &fakeResult{ "Package fmt", []string{ "https://golang.org/", "https://golang.org/pkg/", }, }, "https://golang.org/pkg/os/": &fakeResult{ "Package os", []string{ "https://golang.org/", "https://golang.org/pkg/", }, }, }
合ってるかわからん!
それっぽいはず・・・。
一通り終えて
コードこれで本当に合っているのかはわからないけど、1回目に書いたものということで残しておきます。
これで、みんなのGo言語を読み進めるスタート地点に立てた気がします。。。 本にもあったけど、また慣れてからやれば、もっと違う感じになると思うので、それはまた追々。
とりあえず、終わったよ報告でした。
GoでString to Intしたい
夏季課題として与えられたちょっとしたプログラミングテスト。
で慣れ親しんだJSではなく、わざとGoで挑戦。
せっかく始めたし、知識つけるために。
そんな中、StringをIntにしないといけない場面に出くわして調べた。 (Tour of Goしかやってないぐらいの知識なので)
parseInt的なことやるときはstrconv.Atoi(string)
でできることが分かった。
部分的にはこんな場面。
package main import ( "fmt" "strings" "strconv" ) func main() { const S = "20 50 33 50 60" numberList := strings.Fields(S) sum := 0 for _, number := range numberList { i, _ := strconv.Atoi(number) sum += i } fmt.Println(sum) }
parseInt的なことやるときはstrconv.Atoi(string)
。
覚えました。
別言語で当たり前にやってることだけど、こうやってやり方覚えていくのは楽しい。
Goはじめた
先日、「みんなのGo言語」を購入。
買った。 pic.twitter.com/AuT3kryGmy
— りしあ (@lisia__) August 3, 2019
このお盆休みの9連休でやってやろうと思ってやり始めてます。
進めていくと、まずは「A Tour of Go」をやろうとのことなので、ちょっとずつ進めていきました。
一通り最後まで進めたはいいものの、まだまだ理解は追いついていません。
まだ慣れていないからっていうのもあるし、書いているうちに慣れるでしょう・・・。
それはそうと、Tour of GoのExercise難しくないですか?
僕のレベルが低いだけ?
解けたの2問しかない。
最初のforのやつと、mapのやつ。
forのやつも差がうんぬんの意味がわからなくてできてないし、
できそうだったフィボナッチもできなかった。
問題読んでも何をしたらいいのかサッパリで、自分のレベルの低さに嫌気がさしました。
こういった問題が解けないんだなと、改めて苦手を意識。
trackとかの問題も解くのが苦手で、同じ感じですね。
AtCoderなんかも同じ感じ。
これも数こなして慣れるしかないんでしょうか。
まぁ、さすがに克服したいので、少しずつやっていきたいと思います。
Tour of GoのExercise、自力で解きたいなぁ・・・。
VuexのcreateNamespacedHelpers、同名どうするよ
昨日書いた「VuexのcreateNamespacedHelpersでdispatchの記述を短くしたい」なんですが。
一つ考慮が漏れてました。
namespace
があることで同じ名前のアクション名でも問題なかったのが、あれをやると死にますね。
(よくよく考えたら当たり前)
例えば、それぞれのモジュールでステートを初期化するアクションがあるとすると・・・
// foo/types.js export const namespace = 'foo' export const RESET = 'RESET' // hoge/types.js export const namespace = 'hoge' export const RESET = 'RESET'
import * as fooTypes from '@/store/modules/Foo/types' import * as hogeTypes from '@/store/modules/Hoge/types' import { createNamespacedHelpers } from 'vuex' const { mapActions: mapActionsOfFoo } = createNamespacedHelpers(fooTypes.namespace) const { mapActions: mapActionsOfHoge } = createNamespacedHelpers(hogeTypes.namespace) // ... methods: { ...mapActionsOfFoo([fooTypes.RESET]), ...mapActionsOfHoge([hogeTypes.RESET]) }
この場合、同じRESET
がマッピングされてしまうので、結果的にはHoge
側がマッピングされてしまいます。
防ぐには、
FOO_RESET
とかHOGE_RESET
とか分けた名前にする(namespaceの意味とは)mapActions({ FOO_RESET: fooTypes.RESET })
みたいにマップするときに名前を変える
かなと思いますが、namespace
で得たものを自ら捨てている感じがして、なんとも言えない感じに。
記述量は減らせますが、一長一短ですね。
うーん・・・。
VuexのcreateNamespacedHelpersでdispatchの記述を短くしたい
初めてVuexを触ったとき、とくにnamespaceも使わずやってました。
ちなみにmapActions
は使わず、this.$store.dispatch
でしたい派。
this.$store.dispatch
の方がdispatch
してるわー!今dispatchしてるわー感があるので好き。
というのは嘘で、コード見たときに、「アクション叩いてる」っていう部分が、ぱっと見分かりやすかったってのがあります。
問題
namespaced: true
にしてみると、namespace + actionName
と書く必要がでてきます。
methods: { handleClickBefore(e) { this.$store.dispatch('actionName', payload) }, handleClickAfter(e) { this.$store.dispatch('namespace/actionName', payload) } }
こう見てみると、「そんな嫌か?」ってレベルに見えるんです。
typesを分ける
実際開発しだすと、直接文字列で指定することは少なくて、types.js
みたいなのをつくって、中にconst
でミューテーションとかアクション名を書いていきます。
export const namespace = 'File/' export const SET_FILE_LIST = 'SET_FILE_LIST' export const FETCH_FILE_LIST = ' FETCH_FILE_LIST'
こうすると、dispatchの部分は
import * as fileTypes from '@/store/modules/File/types' // ... methods: { handleClick(e) { this.$dispatch(fileTypes.namespace + fileTypes.FETCH_FILE_LIST) } }
このレベルだとまだマシですが、中にはPREFIXがついて長くなるアクション名がでてきます。
this.$dispatch(fooTypes.namespace + fooTypes.PREFIX_PREFIX_ACTION_NAME_FOR_XXX)
ここまでなると、さすがにdispatch
が良いとは言い辛い状況になってきました。
解決
createNamespacedHelpers
というのがVuexに用意されていることを知りました。
Vuex/modules
import { createNamespacedHelpers } from 'vuex' import * as fileTypes from '@/store/modules/file/types' const { mapActions: mapActionsOfFile } = createNamespacedHelpers(fileTypes.namspace) //... methods: { ...mapActionsOfFile([fileTypes.FETCH_FILE_LIST]), handleClick(e) { this[fileTypes.FETCH_FILE_LIST]() } }
結局mapActions
に屈したわけですが、思っていたほど見辛いこともなく、楽に書けるので良いかなといった具合です。
(Vuex噛んでいるところはthis[types.XXXX]
ってなるので、他とか区別できているので)
その他も
mapState
やmapGetteres
でmapState(namespace, [xxxx])
としていたのも同じく解決です。
また、複数のモジュールを使うときも明確になりますね。
import * as fileTypes from '@/store/modules/File/types' import * as userTypes from '@/store/modules/User/types' const { mapState: mapStateOfFile, mapGetters: mapGettersOfFile } = createNamespacedHelpers(fileTypes.namesace) const { mapState: mapStateOfUser, mapGetters: mapGettersOfUser } = createNamespacedHelpers(userTypes.namesace) //... computed: { ...mapStateOfFile(['fileList']), ...mapStateOfUser(['userList']), ...mapGettersOfFile(['imageList', 'pdfList']) }
みたいな具合で。
一つのモジュールしか使わないなら、import * as types from '@/store/modules/xxxx'
でいいんですけどね。
参考記事
vscodevimでやってる設定
VSCode使い始めて1年ほど。
結局Vimから乗り換えたというか、両方使ってる感じではあります。
というのも、このissueにあるように、行数が多いとかなりパフォーマンスが落ちてしまって、まともにコード書けなくなるんですよね・・・。
VSCodeVIM is unusable (incredibly slow) on larger files, especially in INSERT MODE #2216
そんな中でこれだけは設定してる!というのピックアップしておきます。
設定
自分はVimでLeaderをスペースに設定しているので、それを踏襲。
<Leader> h
で文頭、<Leader> l
で文末をよく使ってます。
最低限、これを入れるだけでかなり使い勝手がよくなりました。
むしろ、手が勝手にこれを打つのでないと厳しい...。
そこの設定はこんな感じ。
{ "vim.leader": "<space>", "vim.useSystemClipboard": true, "vim.normalModeKeyBindingNonRecursive": [ { "before": [ "leader", "h" ], "after": [], "commands": [ { "command": "cursorHome", "args": [] } ] }, { "before": [ "leader", "l" ], "after": [], "commands": [ { "command": "cursorEnd", "args": [] } ] } ] }
キーボードショートカット
もうひとつ、Uniteで,f
とかでファイラ開いてたのも似たような感じにしたくて、Shift + f
でクイックオープン開くようにしました。
Shift + f
はいろいろ問題あったのでやめました。。。
まずエクスプローラーでShilf + fすると、普通にFが入力されてしまうし、そもそもShift + f
潰すとFが入力できない()
Ctrl + , f
に変えました。ちょっと面倒かな。。。
こっちは基本設定→キーボードショートカットから、workbench.action.quickOpen
の設定を変えてください。
MacのデフォがCommand + p
なんですけど、片手で打ちやすいようにした感じです。
Ctrl + f
が良いんですけど、Ctrl + f
はVimのスクロールとバッティングするので避けました・・・。
あとは
fコマンドを使うとき、自分がいかにclever-f.vimに依存してるかが分かりました。
自然とf連打してしまう・・・。VSCodeでも似たようなことしたいなーなんかちょいちょい思ってます。
スクロールを連動させる
Qiitaなんかである入力エリアとプレビューエリアのスクロールが連動するやつ。
要件は違ったんですが、スクロール同期という点では同じだったのでやってみたので書いておきます。
単純に連動する
まずは単純に同じサイズの要素を連動させてみます。
単純な連動デモ
const areaA = document.getElementById('area-a') const areaB = document.getElementById('area-b') areaA.addEventListener('scroll', e => { const scrollTop = e.target.scrollTop areaB.scrollTo(0, scrollTop) })
同じサイズのA→Bと連動させるなら、AのスクロールイベントでscrollTop
をとって、BのscrollTo()
に放り込んであげるだけです。
サイズが違う要素を連動する
次にQiitaみたいな要素の大きさが異なる場合の連動を見ていきます。
サイズ違いの連動デモ
const areaA = document.getElementById('area-a') const areaB = document.getElementById('area-b') areaA.addEventListener('scroll', e => { const scrollMaxA = e.target.scrollHeight - e.target.clientHeight const scrollMaxB = areaB.scrollHeight - areaB.clientHeight const percent = e.target.scrollTop / scrollMaxA areaB.scrollTo(0, scrollMaxB * percent) })
さっきと異なるのがサイズが異なるので、何%スクロールしたのかを求めることが必要です。
まずはscrollHeight - clientHeight
で、どれだけスクロールできるのかをそれぞれ求めます。
次にAのscrollTop / scrollMaxA
で%を計算。
最後にBのscrollTo
にscrollMaxB * percent)
でスクロール量を放り込んであげて完成です!
相互連動させる
最後に相互に連動させる必要がある場合です。
相互連動デモ
const areaA = document.getElementById('area-a') const areaB = document.getElementById('area-b') function scrollA(e) { const scrollTop = e.target.scrollTop areaB.scrollTo(0, scrollTop) } function scrollB(e) { const scrollTop = e.target.scrollTop areaA.scrollTo(0, scrollTop) } areaA.addEventListener('scroll', scrollA) areaB.addEventListener('scroll', scrollB) areaA.addEventListener('mouseenter', e => { areaB.removeEventListener('scroll', scrollB) }) areaB.addEventListener('mouseenter', e => { areaA.removeEventListener('scroll', scrollA) }) areaA.addEventListener('mouseleave', e => { areaB.addEventListener('scroll', scrollB) }) areaB.addEventListener('mouseleave', e => { areaA.addEventListener('scroll', scrollA) })
ちょっと面倒な書き方してるかもしれないですが。。。
相互連動するとA→Bをしたときに、Bのスクロールイベントがトリガーされます。
デモぐらい単純な中身なら気にはなりませんが、たまーにズレたりします。
そこで、AをスクロールさせたときはBのイベントをトリガーしない。
BをスクロールしたときはAのイベントをトリガーしない。みたいなことが必要になります。
方法は
- どこからスクロールされているのかを明確にする
- そもそもイベント自体を消してしまう
どっちかだと思いますが、今回は後者です。
各要素にmouseenter
したら別要素のイベントをremoveEventListener
で削除。
mouseleave
したら別要素にイベントをaddEventListener
で付与しています。