丸善出版の『プログラミング言語Go』の読書履歴です。
出てきた内容の気になったポイントのまとめや練習問題の解答(自分なりの)を記載していきます。
(ここに記載されている内容で問題が生じても責任は負えませんのであしからず…。)
練習問題は結構長くなるものが増えてきたので、本文記載のコードの修正の場合は修正部分だけ抜粋しています。
すべてのコードが見たい場合はGitHubにあるので、そちらを見てみてください。
プログラミング言語Go GitHub
さて、第3章は基本データ型についてです。
もくじ
3.1 整数
3.2 浮動小数
3.3 複素数
3.4 ブーリアン
3.5 文字列
3.6 定数
これまでの章のまとめはこちら
3.1 整数
Go言語の整数型には符号付きと符号なしがあります。
符号付き整数型は int8型、int16型、int32型、int64型があり、符号なし整数型には uint8型、uint16型、uint32型、uint64型があります。
また、ビット数が32ビットか64ビットになる int型と uint型もあります。
これらのビット数はハードウェア依存です。
他に rune型というのがあって、int32のシノニムで、慣習的にUncodeのコードポイントとして使われます。
byte型というのもあって、これはuint8のシノニムで、生データを扱うときに使われるようです。
それから、uintptr型というのもあって、これはポインタを格納するときに使うようです。
通常はint、uintを使えば良いようです。
ただ、intとint32と言ったデータ型は暗黙的に変換してくれないので、int32(x)
といったように明示的に変換する必要があります。
算術、論理、比較演算子は次のようなものがあり、CやJavaなどと同じですね。
*, /, %, +, -, <<, >>, &, &^, |, ^, ==, !=, <, <=, >, >=, &&, ||
3.2 浮動小数点
Goでは float32 と float64 があって、それぞれ精度が6桁と約15桁になります。
小数点より前の桁を省略(.707)、もしくは後の桁を省略(1.)することができます。
非常に小さな値や大きな値は e または E を使った科学的記数法で書くこともできます。
練習問題3.1
不正な値があったらポリゴンをスキップする、という問題なので、単純にIsNaN() で値を調べるif文を追加しています。
(文中にあるソースコードの修正なので、修正部分の関数だけ抜粋します)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
func main() { fmt.Printf("<svg xmlns='http://www.w3.org/2000/svg' " + "style='stroke: grey; fill; white; stroke-width: 0.7'" + "width='%d' height='%d'>", width, height) for i := 0; i < cells; i++ { for j := 0; j < cells; j++ { ax, ay := corner(i+1, j) bx, by := corner(i, j) cx, cy := corner(i, j+1) dx, dy := corner(i+1, j+1) if !(math.IsNaN(ax) && math.IsNaN(ay) && math.IsNaN(by) && math.IsNaN(by) && math.IsNaN(cy) && math.IsNaN(cy) && math.IsNaN(dy) && math.IsNaN(dy)) { fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n", ax, ay, bx, by, cx, cy, dx, dy) } } } fmt.Println("</svg>") } |
練習問題3.2
mathパッケージの別の関数を使って違う図形を作りなさい、という課題なのですが、数学が苦手ゆえ解答が出ず。。。
悩みすぎて先に進めなさそうなので、時々考えることにしてちょっとパスします(汗)
練習問題3.3
高さに基づいて色を変えよ、ということなのですが、こちらもイマイチ解答が浮かばずでして。。。
色はstyleを設定することで変更可能です。
1 |
fmt.Printf("<polygon style='stroke: #ff0000;' points='%g,%g %g,%g %g,%g %g,%g'/>\n", ax, ay, bx, by, cx, cy, dx, dy) |
練習問題3.4
SVGデータをクライアントに書き出すWebサーバを作成する、という問題なので、1章で作ったWebサーバを参考にしてこんな感じにすればブラウザで図形を見れるようになります。
HTTPリクエストでパラメータを受け取って高さなどを指定できるように、ともなるのですが、この辺は練習1.12でもやっているのでそちらを参考にしてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
func main() { handler := func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "image/svg+xml") polygon(w) } http.HandleFunc("/", handler) log.Fatal(http.ListenAndServe("localhost:8000", nil)) } func polygon(out io.Writer) { fmt.Fprintf(out, "<svg xmlns='http://www.w3.org/2000/svg' "+ "style='stroke: grey; fill: white; stroke-width: 0.7;' "+ "width='%d' height='%d'>\n", width, height) for i := 0; i < cells; i++ { for j := 0; j < cells; j++ { ax, ay := corner(i+1, j) bx, by := corner(i, j) cx, cy := corner(i, j+1) dx, dy := corner(i+1, j+1) if !(math.IsNaN(ax) && math.IsNaN(ay) && math.IsNaN(bx) && math.IsNaN(by) && math.IsNaN(cx) && math.IsNaN(cy) && math.IsNaN(dx) && math.IsNaN(dy)) { fmt.Fprintf(out, "<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n", ax, ay, bx, by, cx, cy, dx, dy) } } } fmt.Fprintf(out, "</svg>") } |
3.3 複素数
複素数ってなによ、と思うのは私だけ?(汗)
数学における複素数(ふくそすう、英: complex number)は、実数の対 a, b と 1 と線型独立な(実数ではない)要素 i の線型結合 a + bi の形に表される数(二元数: 実数体上の二次拡大環の元)で、基底元 i はその平方が −1 になるという特別な性質を持ち虚数単位と呼ばれる。
全然わかんない…(@_@;)
ま、ま、ま、とりあえず読み進めます。はい。
複素数の生成にはcomplex関数が使えて、次のように書きます。
1 |
var x complex128 = complex(1, 2) |
もっと単純に書くこともできて、次のようになります。
1 |
x := 1 + 2i |
real関数、imag関数で実部と虚部を取り出すことができます。
1 2 |
fmt.Println(real(x)) // "1" fmt.Println(imag(x)) // "2" |
練習問題3.5〜3.8
えぇと、、、数学が苦手な私にはだいぶ難しく、ちょっと解けていません。。。
ここで時間使いすぎても先に進めないので、この説の練習問題は飛ばしてしまいました。すみませんm(_ _)m
練習問題3.9
この問題は画像データをクライアントに書き出すWebサーバを作る、というものなので、問題1.12を参考にすれば出来ると思います。
パラメータを取得するところは作りこんでいませんが、クライアントに書き出すところまで作ったものがGitHubにアップロードしてあるので参考にしてみてください。
GitHub – 練習問題3.9
3.4 ブーリアン
ブーリアンは bool 型を使います。取り得る値は true と false の2つだけです。
&&演算子と || 演算子と組み合わせる事ができて、次のように書くことも可能です。
1 2 |
s := "hoge" println( s != "" && s[0] == 'w' ) |
こうすると出力結果は false になります。
s が空文字の場合は s[0]
でエラーになってしまうので、あらかじめ s != ""
で空文字かどうかのチェックをしてから、1文字目のチェックをしているわけですね。
空文字の場合は最初の条件の時点でfalseになるので、エラーにならず処理はきちんと終わります。
それから、int と boolは暗黙の変換はないので、ifを使って変換することになります。
こんな感じに変換関数を用意しておくといいかもね、というお話でした。
1 2 3 4 5 6 7 8 9 10 |
// bool -> int func btoi(b bool) int { if b { return 1 } return 0 } // int -> bool func itob(i int) bool { return 1 != 0 } |
3.5 文字列
テキスト文字列はUTF-8エンコードされたのUnicodeです。
文字数は組み込み関数の len()
を使って取得でき、 s[0]
とすると0番目の文字を取り出すこともできます。
部分文字列も取り出せて、 s[0:5]
と言ったように書けばOKです。この場合、0から5未満の文字列を取り出すことになります。
1 2 3 |
s := "hello world" fmt.Println(len(s)) // 11 fmt.Println(s[0], s[6]) // 104 119 (文字コードが返ってきます) |
+ 演算子を使って文字列を結合して新しい文字列を生成することができます。
1 2 3 4 5 |
s := "left foot" t := s s += ", right foot" fmt.Println(s) // left foot, right foot fmt.Println(t) // left foot |
あくまでも新しい文字列が生成されるので、s を代入しておいた t は元のままの文字列になります。
文字列は不変なので、文字列のデータを直接変換することはできません。
例えば以下のようにするとコンパイルエラーになります。
1 |
s[0] = 'f' |
3.5.1 文字列リテラル
文字列リテラルは ""
(ダブルクォート)で囲んで書きます。
1 |
"Hello World" |
\
(バックスラッシュ)で始まるエスケープシーケンスも使えます。
1 2 3 4 5 6 7 8 9 10 |
\a アラート \b バックスペース \f フォームフィード \n 改行 \r キャリッジリターン \t タブ \v 垂直タブ \' シングルクォート \" ダブルクォート \\ バックスラッシュ |
生文字列リテラル(raw string literal)はダブルクオートの代わりにバッククォート `
で囲みます。
生文字列リテラルはエスケープシーケンスが使えません。
ま文字列はプログラムソース内で複数行に広げることができたり、バックスラッシュを含むことが多い正規表現を書くときに便利です。
3.5.2 Unicode
昔はASCIIだったけど、いろいろな言語に対応するためにUnicodeができたよ、といった話です。
Go言語でも文字列はUnicodeで扱われます。
文字コードはGo言語ではルーン(rune)と呼ばれる規格番号を割り当てています。
3.5.3 UTF-8
UTF-8はUnicodeコードポイントのバイトしての可変長エンコーディングです。
最近はUTF-8が使われることが非常に多いですね。
Go言語ではutf-8パッケージが用意されていて、いろいろな機能があるようです。
ルーンとUTF-8の変換と言った関数も用意されています。
3.5.4 文字列とバイトスライス
文字列を操作するには、bytes、strings、strconv、unicode の標準パッケージを使うことができます。
strings パッケージは文字列の検索、置換、比較、トリミング、分割、連結などの関数を提供しています。
bytes パッケージは文字列をバイトスライスとして操作することができて、stringsと同じような機能も持っています。
strconv パッケージは整数値、ブール値、浮動小数点値から文字列へ変換やその逆変換などの機能を提供します。
unicode パッケージはルーンを分類するための関数などを提供します。
この節ではこれらのパッケージを使わないで文字列をバイトスライスとして扱う方法や、stringsやbytesを使った方法などを紹介しています。
この節では練習問題があるので、文中に出てくる commma プログラムから修正した部分だけ抜粋しています。
練習問題 3.10
文字列連結の代わりに bytes.Buffer を使って行う、というものです。ちょっと冗長な気もしますがとりあえず…。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
func comma(s string) string { n := len(s) var b bytes.Buffer for n > 0 { l := n % 3 if l == 0 { l = 3 } b.WriteString(s[:l]) if n > 3 { b.WriteString(",") } s = s[l:] n = len(s) } return b.String() } |
練習問題 3.11
comma を機能拡張して、符号記号を持つ浮動小数点を扱えるようにする、という問題です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
func comma(s string) string { var b bytes.Buffer if strings.HasPrefix(s, "+") || strings.HasPrefix(s, "-") { b.WriteByte(s[0]) s = s[1:] } f := "" num := strings.Index(s, ".") if num > 0 { f = s[num:] s = s[:num] } n := len(s) for n > 0 { l := n % 3 if l == 0 { l = 3 } b.WriteString(s[:l]) if n > 3 { b.WriteString(",") } s = s[l:] n = len(s) } b.WriteString(f) return b.String() } |
練習問題 3.12
この問題はちょっと飛ばしてしまいました。すみません…m(_ _)m
3.5.5 文字列と数値の変換
文字列と数値の変換はよくやるのですが、Go言語の場合は strconv パッケージか fmt.Sprintf を使うと簡単にできます。
1 2 3 |
x := 123 y := fmt.Sprintf("%d", x) fmt.Println(y, strconv.Itoa(x)) |
3.6 定数
定数は実行時ではなくコンパイル時に評価が行われることが保障されている式です。
定数にはブーリアン、文字列、数値の基本型を使うことが可能です。
定数を定義するには const
を使います。
1 |
const pi = 3.14 |
定数もひとつの宣言内に書くことができます。
1 2 3 4 |
const ( e = 2.71 pi = 3.14 ) |
3.6.1 定数生成器 iota
const宣言で定数生成器 iota を使うことができます。
次のように書くと、Sundayはint型なので 0 が設定されます。
そのあとのMonday以降の定数には値を指定していませんが、1ずつインクリメントされた値が設定されます。
1 2 3 4 5 6 7 8 9 10 |
type Weekday int const ( Sunday Weekday = iota Monday Tuesday Wednesday Thursday Friday Saturday ) |
多言語によくある Enumのような型にすることができます。
練習問題 3.13
iotaはビット演算はできますが、塁上演算子がないので、1000の累乗を生成するにはどうしたらいいか、という問題です。
単純に1000を掛けて行くようにしただけですが、これでいいのかしら…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package main import "fmt" const ( KB = 1000 MB = KB * KB GB = MB * KB TB = GB * KB PB = TB * KB EB = PB * KB ZB = EB * KB YB = ZB * KB ) func main() { fmt.Println(KB, MB, GB, TB, PB, EB) //, ZB, YB) } |
3.6.2 型付けなし定数
const宣言では型に結びついていない定数を定義することができて、その場合は基本型の値より遥かに高い数値精度を表現することができます。
前節で Zib や YiB は定数にそのまま代入しようとしたり、fmt.Printlnで表示しようとするとオーバーフローを起こしてエラーになっていましたが、次のように式に使うことが可能です。
1 |
fmt.Println(YiB / ZiB) |
また変数や定数に代入するときには明示的な変換を必要しません。
ただし、変数に代入したあとに精度の違う変数に使いたい場合は型変換が必要となります。
1 2 |
const Pi64 float64 = math.Pi var x float32 = float32(Pi64) |
さて、、、ようやく基本データ型の章が終わりました。
この投稿も結構なボリュームになりましたが、書籍もかなりボリュームがあります。
もちろん基本中の基本な章なのでしっかり抑えておくことが大事だと思います。