
一時期、あの言語もこの言語もオブジェクト指向、いえ「クラス原理主義」にハマっていたと思う私メでございます。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回分のデータの移動平均をとっています。かなり緩やかな値の変化です。こんな感じ。
これで「クラスみたいなもの」もバッチリ? お調子もの。
