はじめに
Go 言語基礎文法最速マスターにて出題されている演習課題「Exercise: Slices(スライス)」の答えに辿り着くまでの手順と解答例の解説を記事にしてみました。pic.Show
関数、NewNRGBA
関数などについても簡単に説明しています。
課題
課題の目的は main
関数の pic.Show
関数で画像が出力されるように Pic
関数を実装することです。
package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
}
func main() {
pic.Show(Pic)
}
解答例
package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
s := make([][]uint8, dy)
for x := range s {
s[x] = make([]uint8, dx)
for y := range s[x] {
s[x][y] = uint8((x + y) / 2)
}
}
return s
}
func main() {
pic.Show(Pic)
}
解説
初めに、課題のソースコードを読み解きます。
3 行目、golang.org/x/tour/pic
パッケージをインポートしています。この中で pic.Show
関数が定義されています。
import "golang.org/x/tour/pic"
5 〜 6 行目、Pic
関数が定義されています。ここに pic.Show
関数で画像が出力されるように処理を実装します。
func Pic(dx, dy int) [][]uint8 {
}
8 〜 10 行目、main
関数で pic.Show
関数が呼び出されています。
func main() {
pic.Show(Pic)
}
さて、実装を進める前に pic.Show
関数の振る舞いについて理解する必要があります。
pic.Show
関数は実行すると引数 f
の値を基に画像を出力します。引数 f
は Pic
関数の Function value(関数値)ですね。
func Show(f func(dx、dy int)[] [] uint8)
Pic
関数の引数 dx
, dy
は何を受け取るのでしょうか。
答えは Show
関数の ソースコード にあります。
func Show(f func(dx, dy int) [][]uint8) {
const (
dx = 256
dy = 256
)
data := f(dx, dy)
m := image.NewNRGBA(image.Rect(0, 0, dx, dy))
for y := 0; y < dy; y++ {
for x := 0; x < dx; x++ {
v := data[y][x]
i := y*m.Stride + x*4
m.Pix[i] = v
m.Pix[i+1] = v
m.Pix[i+2] = 255
m.Pix[i+3] = 255
}
}
ShowImage(m)
}
Pic
関数の実引数には int
型の 256 で初期化した定数 dx
, dy
が渡されます。この値は 0 で完全な青、255 で完全な白を意味しています。
その後、image
パッケージの NewNRGBA
関数と Pic
関数の戻り値を組み合わせた画像を ShowImage
関数で出力しています。
もうちょい掘り下げて説明すると、変数 m
には NRGBA 構造体のポインタが格納されていて、フィールドの m.Pix
には R(Red), G(Green), B(Blue), A(Alpha)の順序で画像のピクセルが保持されています。つまり、画像の R, G に Pic
関数の戻り値を格納して出力している仕掛けになっています。
では、pic.Show
関数の正体がわかったところで実装を進めます。
やることは、長さ dy
のスライスに各要素が uint8(8bit の unsigned int)型で長さ dx
のスライスを割り当てたものを返すように実装することです。
まずは、make
関数で長さ dy
の [][]uint8
型スライスを作成します。
func Pic(dx, dy int) [][]uint8 {
s := make([][]uint8, dy)
return s
}
変数 s
の各要素に長さ dx
の []uint8
型スライスを割り当てたいので for-range
で各要素を取り出します。
func Pic(dx, dy int) [][]uint8 {
s := make([][]uint8, dy)
for x := range s {
}
return s
}
各要素に長さ dx
の []uint8
型スライスを割り当てまして。
func Pic(dx, dy int) [][]uint8 {
s := make([][]uint8, dy)
for x := range s {
s[x] = make([]uint8, dx)
}
return s
}
さらに for-range
で各要素に対して (x + y) / 2
, x * y
, x ^ y
を算出した値を格納して実装は完了です。^
は冪乗のことです。
func Pic(dx, dy int) [][]uint8 {
s := make([][]uint8, dy)
for x := range s {
s[x] = make([]uint8, dx)
for y := range s[x] {
s[x][y] = uint8((x + y) / 2)
}
}
return s
}
はい、できあがり!
package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
s := make([][]uint8, dy)
for x := range s {
s[x] = make([]uint8, dx)
for y := range s[x] {
s[x][y] = uint8((x + y) / 2)
}
}
return s
}
func main() {
pic.Show(Pic)
}
(x + y) / 2
の実行結果は下記のようになります。
x * y
の実行結果は下記のようになります。
x ^ y
の実行結果は下記のようになります。
以上です。
おわりに
個人的には x ^ y
の画像が好きです。