やわらかテック

興味のあること。業務を通して得られた発見。個人的に試してみたことをアウトプットしています🍵

【サンプルコード有り】golangで三項演算子っぽいものを記述する方法について

嫌だなぁと感じる場面

業務を進める中で、以下の様なコードを書くことが多かった。

drink := ""
if orderNum {
    drink = "green tea"
} else {
    drink = "tea"
}

orderNumの様な関数の引数で指定される値であったり、http経由で指定されたqueryによって値をセットする処理なのだが、事前にdrinkという同じ型に該当する初期値となる変数を用意しておく必要があり個人的には好きではない。しかしながら、これがgolangの言語デザインでもあり、QiitaRui Ueyamaさんも言及している。

三項演算子があればなぁ...」と思い、何とかならないかなとあがいた結果が以下になる

無名関数を使った記述方法

先ほどのコードを無名関数を使った三項演算子っぽいものに書き換えてみた。

func Sample(orderNum int) string {
    // 無名関数を即時実行する
    drink := func(n int) string {
        if n == 1 {
            return "green tea"
        } else {
            return "tea"
        }
        // 即時実行のために引数を指定
    }(orderNum)

    // 無名関数の実行結果を返す
    return drink
}

やっていることは大したことはなくて、無名関数を定義して、その無名関数の戻り値を任意の変数に代入にしているだけ。わざわざ、外部に関数を定義するまでもないので無名関数で良くないかという判断に至った。nの値によって戻り値のパターンを増やしたいのならswitch文の採用も考える。無駄な変数を宣言する必要がないため、割と気に入っている。

パフォーマンスについて

気になるのは、無名関数を使った記述方法にすることで、どれだけオーバーヘッドになってしまうのかという点。普通に考えて、無名関数を使った方がパフォーマンスは悪いだろうなと思うが、考えても分からないので測定してみた。
それぞれ無名関数を使用する記述と、従来の記述とで1万回ずつ測定して、平均値を算出した。

比較のためにそれぞれを関数内に内包して、それぞれの関数の実行速度を比較するとする。

func AnonymousFunctionSample(orderNum int) string {
    // 無名関数を即時実行する
    drink := func(n int) string {
        if n == 1 {
            return "green tea"
        } else {
            return "tea"
        }
        // 即時実行のために引数を指定
    }(orderNum)

    // 無名関数の実行結果を返す
    return drink
}

func NormalSample(orderNum int) string {
    drink := ""
    if orderNum == 1 {
        drink = "green tes"
    } else {
        return "tea"
    }
    return drink
}

検証に使用したコードはこちら。
github.com

実行結果

$ bash benchmark.sh

[info] clear go test cached...
[info] start benchmark
=== RUN   TestSample
[info] average for anonymous function:  9.854730000000441e-08
[info] average for normal exec:  1.0573470000000177e-07
--- PASS: TestSample (0.01s)
PASS
ok      ternary_operator/src    0.015s

1万回平均で見ると、無名関数を使用した記述方法の方が速いようだ。
可読性は好き嫌いが分かれる所だと思うが、良ければ使ってください。

参考文献