一時期、あの言語もこの言語もオブジェクト指向、いえ「クラス原理主義」にハマっていたと思う私メでございます。Go言語にはアカラサマなクラスというものは有りませんが、クラス的な型structと型に引っ付いたmethodがあり。このくらいが私メには丁度いい感じがします。折角なので練習してみました。題材は移動平均とな。
※「GoにいればGoに従え」Go関連記事の総Index
StructとMethod
Struct(構造体)は多くの言語に存在し、Goにおいても似たようなもんです。そしてC++でのClassを眺めれば、ClassとStructの実体はほぼほぼ同じようなもんだ、という大雑把な理解もあり?ホントか?
よってというか何と言うか、struct(正確に言えばstructでなくても良い、ユーザ型)を定義すればGoでも「クラスメソッド」的な奴らを定義できるのでありました。Methodとな。普通の functionとの違いはレシーバーなるものを指定するか否かだと
-
- func 関数名(仮引数並び) 戻り値 { …、Function
- func (レシーバー)メソッド名(仮引数並び) 戻り値 { …、Method
レシーバーはPythonでお馴染みの self 見たいな奴で、ユーザ定義型(通常は構造体)の型をもつです。
Method定義のサンプルに移動平均
前回、CDSセンサの値を3回測って平均とかしていたので、その部分を移動平均にしておくのもありだな~と思いました。移動平均は簡単な処理ですが、原理的にはFIR(Finite Impulse Response、有限インパルス応答)フィルタの入口であります。)移動平均が計算できれば後は何でもできる。ちょっと言い過ぎでないかい?
さて今回作成した移動平均計算用のバッファ構造体(クラスといいたいところだけれど我慢)が以下に。
package main const Maxbuffer = 5 type MovingAVGbuf struct { buf [Maxbuffer]uint16 ptr uint } func (ma *MovingAVGbuf) Clear() { for i := 0; i < Maxbuffer; i++ { ma.buf[i] = 0 } ma.ptr = 0 } func (ma *MovingAVGbuf) Put(val uint16) { ma.buf[ma.ptr] = val ma.ptr++ if ma.ptr >= Maxbuffer { ma.ptr = 0 } } func (ma *MovingAVGbuf) Average() int { var sum uint16 = 0 for _, d := range ma.buf { sum += d } return int(sum / Maxbuffer) }
簡単な構造体を1個定義して、それに引っ付く専用メソッド3つを定義して1つのファイルにまとめてみましたデス。
実機実験用のコード
前回使用したファイル群のディレクトリに上記のファイル1個をmavg.goなるお名前で追加(main.goと並列に)しました。main.goファイル内のcdsセンサから値を読み取る部分を以下のように変更してみました。
//~途中略~ func main() { //~途中略~ var mabuf MovingAVGbuf mabuf.Clear() //~途中略~ mabuf.Put(ReadCDS()) cds = mabuf.Average() fmt.Printf("CDS: %d\r\n", cds)
ビルドしてflash書き込んで実行はいつものとおり以下です(当然 micro:bit v2機をホストのUSBに接続しておく必要がありです。通常は勝手にポートを見つけてくれる筈。)
$ tinygo flash -target=microbit-v2
今回のコードでは5秒に1回、CDSセンサの値を読み取り、その5回分のデータの移動平均をとっています。かなり緩やかな値の変化です。こんな感じ。
これで「クラスみたいなもの」もバッチリ? お調子もの。