【Go】関数のスライスをつくる


はじめに

Youtubeで見た、「元に戻す」機能を合成関数を使って実装・解説している動画1が大変勉強になりましたので、Goでもやってみました!

JavaScriptの場合

関数の配列は以下のように作れます。

// 1を加算する
function hoge(p) { return p+1; }
// 2で冪乗する
function fuga(p) { return p**2; }

// 関数を配列に格納
let arr = [ hoge, fuga ];

let result = 1;
// 以下の計算をします
// fuga(hoge(1)) -> fuga(2) -> 4
for ( let i = 0; i < arr.length; i++ ) { 
    result = arr[i](result);
}
console.log(`result is ${result}`);
// result is 4

動作としてはループの中で一回ずつ関数を呼び出して変数に値を格納しています。

Goの場合

参考動画1と同じように、「倉庫番」のコードに合わせてます。

mapstatus.go
type MapStatus struct {
    pX, pY int // プレイヤー座標
    bX, bY int // 荷物座標
    mX, mY int // マーク座標

}

// MoveFunc func(*MapStatus)型
type MoveFunc func(*MapStatus)

// Union 空レシーバ
func (m MoveFunc) Union() {}

// Command 抽象コマンド
type Command interface {
    Union()
}

// move プレイヤーの移動
func (m *MapStatus) move(x, y int) {

    m.pX += x
    m.pY += y

    // プレイヤーとかぶったら荷物をずらす
    if m.pX == m.bX && m.pY == m.bY {
        m.bX += x
        m.bY += y
    }
}

// Right 右へ進む
func Right(m *MapStatus) {
    m.move(1, 0)
}

// Down 下へ進む
func Down(m *MapStatus) {
    m.move(0, 1)
}
main.go
func main() {
    var cmds []Command

    m := NewMapStatus()

    fmt.Println("before:", m) // before: &{1 0 3 1 5 1}

    cmds = append(cmds, MoveFunc(Right))
    cmds = append(cmds, MoveFunc(Down))
    cmds = append(cmds, MoveFunc(Right))
    cmds = append(cmds, MoveFunc(Right))

    cmds = cmds[:len(cmds)-1] // 1つ戻る

    for _, cmd := range cmds {
        switch t := cmd.(type) {
        case MoveFunc:
            t(m)
        }
    }

    fmt.Println("after:", m) // after: &{3 1 4 1 5 1}
}

まとめ

動画でも紹介されていますが、「元に戻す」のような機能を関数の配列(スライス)で表現できるところが簡潔でいいなと思いました。

Goで実装した関数のスライスを作るのはそのまま移植した感が否めないので、他のやり方があれば是非コメントで教えていただければ幸いです!
(Goの書き方についてもツッコミありましたらお願いします)