typeof Diary

VimとかJSとか。やったことのメモ。自分のため。

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-101e-60.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'であることに気付いた。
学び。

otiai10.hatenablog.com

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言語」を購入。

このお盆休みの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]ってなるので、他とか区別できているので)

その他も

mapStatemapGetteresmapState(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 + fVimのスクロールとバッティングするので避けました・・・。

あとは

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のscrollToscrollMaxB * 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で付与しています。