演習a1

タイピングゲームの演習だと,Go言語の特徴的な機能であるインターフェイスの話が出てこないので,別の例としてAKB48の第3回選抜総選挙の結果を使って,インターフェイスについて演習します.

演習(インターフェース)

(1)

名前,ふりがな,得票数,チームをフィールドとしてもつ型を3つ宣言し(それぞれAKB48,SKE48,NMB48とする),すべての型に,SayHello(),GetNumOfVote()というメソッドを定義せよ.また,AKB48にSayTeam(),SKE48にSayGroup()というメソッドを定義せよ.ただし,各メソッドは次のような振る舞いをするとする.

AKB48のメソッド

SayHello() "こんにちは,○○です."という文字列を返す.○○は名前.

SayTeam() "AKB48のチーム△所属です."という文字列を返す.△はチーム名.

GetNumOfVote() 第3回総選挙の得票数を返す.

※SayGroup()というメソッドは持たない.

SKE48のメソッド

SayHello() "こんにちは,○○です."という文字列を返す.○○は名前.

SayGroup() "SKE所属です."という文字列を返す.

GetNumOfVote() 第3回総選挙の得票数を返す.

※ SayTeam()というメソッドは持たない.

NMB48のメソッド

SayHello() "こんにちは,NMB48の○○です."という文字列を返す.AKB48のSayHelloとは違う動きをする.

GetNumOfVote() 第3回総選挙の得票数を返す.

※ SayTeam,SayGroup()というメソッドは持たない.

(2)

AKB48第3回総選挙の1位から40位までのメンバーの情報(名前,得票数,チーム)を,管理するスライスを定義せよ.

(3)

組み込みのsortパッケージを用いて,スライスを得票数の多い順にソートせよ.

(4)

得票数の多い順に,"得票数:こんにちは,○○です...."と表示せよ.

更に,AKB48のメンバーの場合は,"AKB48チームA所属です."のようにチーム名を表示せよ.

また,SKE48のメンバーの場合は,"SKE48所属です."と表示せよ.

解答例

  1. package main
  2. import (
  3. "fmt"
  4. "sort"
  5. )
  6. // ---------------------------- AKB48 ----------------------------
  7. type AKB48 struct{
  8. name string // 名前
  9. furigana string // ふりがな
  10. numOfVote int // 得票数
  11. team string // チーム
  12. }
  13. func (a AKB48) SayHello() string {
  14. return "こんにちは," + a.name + "です."
  15. }
  16. func (a AKB48) SayTeam() string {
  17. return "AKB48のチーム" + a.team + "所属です."
  18. }
  19. func (a AKB48) GetNumOfVote() int {
  20. return a.numOfVote
  21. }
  22. // ---------------------------- SKE48 ----------------------------
  23. type SKE48 struct {
  24. name string // 名前
  25. furigana string // ふりがな
  26. numOfVote int // 得票数
  27. team string // チーム
  28. }
  29. func (s SKE48) SayHello() string {
  30. return "こんにちは," + s.name + "です."
  31. }
  32. func (SKE48) SayGroup() string {
  33. return "SKE48所属です."
  34. }
  35. func (s SKE48) GetNumOfVote() int {
  36. return s.numOfVote
  37. }
  38. // ---------------------------- NMB48 ----------------------------
  39. type NMB48 struct {
  40. name string // 名前
  41. furigana string // ふりがな
  42. numOfVote int // 得票数
  43. team string // チーム
  44. }
  45. func (n NMB48) SayHello() string {
  46. return "こんにちは,NMB48の" + n.name + "です."
  47. }
  48. func (n NMB48) GetNumOfVote() int {
  49. return n.numOfVote
  50. }
  51. // ---------------------- メンバー,総選挙 ----------------------
  52. type Member interface {
  53. SayHello() string
  54. GetNumOfVote() int
  55. }
  56. type Election []Member // 総選挙の結果を管理するスライス
  57. func (s Election) Len() int {
  58. return len(s)
  59. }
  60. func (s Election) Less(i, j int) bool {
  61. return s[i].GetNumOfVote() > s[j].GetNumOfVote()
  62. }
  63. func (s Election) Swap(i, j int) {
  64. s[i], s[j] = s[j], s[i]
  65. }
  66. // ---------------------------- main ----------------------------
  67. func main() {
  68. the3rd := Election{
  69. AKB48{"前田敦子","まえだあつこ",139892,"A"},
  70. AKB48{"篠田麻里子","しのだまりこ",60539,"A"},
  71. AKB48{"小嶋陽菜","こじまはるな",52920,"A"},
  72. AKB48{"高橋みなみ","たかはしみなみ",52790,"A"},
  73. AKB48{"指原莉乃","さしはらりの",45227,"A"},
  74. AKB48{"高城亜樹","たかじょうあき",31009,"A"},
  75. AKB48{"倉持明日香","くらもちあすか",12387,"A"},
  76. AKB48{"仲川遥香","なかがわはるか",10854,"A"},
  77. AKB48{"多田愛佳","おおたあいか",9910,"A"},
  78. AKB48{"大家志津香","おおやしづか",7264,"A"},
  79. AKB48{"前田亜美","まえだあみ",5220,"A"},
  80. AKB48{"大島優子","おおしまゆうこ",122843,"K"},
  81. AKB48{"板野友美","いたのともみ",50403,"K"},
  82. AKB48{"宮澤佐江","みやざわさえ",33500,"K"},
  83. AKB48{"峯岸みなみ","みねぎしみなみ",26070,"K"},
  84. AKB48{"秋元才加","あきもとさやか",17154,"K"},
  85. AKB48{"横山由依","よこやまゆい",16455,"K"},
  86. AKB48{"梅田彩佳","うめだあやか",11860,"K"},
  87. AKB48{"仁藤萌乃","にとうもえの",6288,"K"},
  88. AKB48{"松井咲子","まついさきこ",5020,"K"},
  89. AKB48{"藤江れいな","ふじえれいな",4698,"K"},
  90. AKB48{"柏木由紀","かしわぎゆき",74252,"B"},
  91. AKB48{"渡辺麻友","わたなべまゆ",59118,"B"},
  92. AKB48{"北原里英","きたはらりえ",27957,"B"},
  93. AKB48{"河西智美","かさいともみ",22857,"B"},
  94. AKB48{"佐藤亜美菜","さとうあみな",16574,"B"},
  95. AKB48{"増田有華","ますだゆか",14137,"B"},
  96. AKB48{"平嶋夏海","ひらじまなつみ",9742,"B"},
  97. AKB48{"宮崎美穂","みやざきみほ",9271,"B"},
  98. AKB48{"小森美果","こもりみか",6120,"B"},
  99. AKB48{"佐藤すみれ","さとうすみれ",5438,"B"},
  100. AKB48{"大場美奈","おおばみな",5411,"4"},
  101. AKB48{"市川美織","いちかわみおり",4928,"4"},
  102. SKE48{"松井玲奈","まついれな",36929,"S"},
  103. SKE48{"松井珠理奈","まついじゅりな",27804,"S"},
  104. SKE48{"大矢真那","おおやまさな",6660,"S"},
  105. SKE48{"須田亜香里","すだあかり",5343,"S"},
  106. SKE48{"高柳明音","たかやなぎあかね",11674,"KII"},
  107. SKE48{"秦佐和子","はたさわこ",6117,"KII"},
  108. NMB48{"山本彩","やまもとさやか",8697,"N"},
  109. }
  110. sort.Sort(the3rd)
  111. for _,m := range the3rd {
  112. fmt.Printf("得票数 %6d: %s",m.GetNumOfVote(),m.SayHello())
  113. switch val := m.(type) {
  114. case AKB48: fmt.Printf("%s",val.SayTeam())
  115. case SKE48: fmt.Printf("%s",val.SayGroup())
  116. }
  117. fmt.Printf("\n")
  118. }
  119. }

(1)

  • 8行目~23行目でAKB48型を定義.
  • 25行目~40行目でSKE48型を定義.
  • 42行目~54行目でNMB48型を定義.

(2)

  • 61行目でMemberインターフェースを要素とするスライスとして総選挙の結果を管理する型Electionを定義.
  • 74行目~12オ行目で,Election型の変数the3rd(第3回総選挙の結果を管理するスライス)を宣言し,複合リテラルで初期化.

(3)

  • 62行目~70行目で,sortパッケージの関数Sort()を利用するために必要となる3つのメソッドLen(),Less(),Swap()を定義.
  • 122行目で関数Sort()を呼び出してソート.Less()の定義により,得票数の多い順にソートされる.

(4)

  • 123行目~130行目のループで順に(得票数の多い順に)処理.
  • 124行目で "得票数:こんにちは,○○です...."と表示.
  • 126行目で,AKB48のメンバーの場合は,"AKB48チームA所属です."のようにチーム名を表示.
  • 127行目で,SKE48のメンバーの場合は,"SKE48所属です."と表示.

解説

AKB48とSKE48とNMB48はどれも同じフィールドをもつので,匿名フィールドを使って書くことができますが,ここでは,別々に定義されたクラスとインターフェースの関係を見ていくために,あえて別々に定義しています.

インターフェイス

  • インターフェイスは,メソッド群を規定します.
  • インターフェイス型の変数には,そのインターフェイスのメソッド群を持っている型の値を格納することができます.その型は,インターフェイスが規定する以外のメソッド群を持っていても構いません.そのような型は「インターフェイスを実装している」と言います.
  • 例えば,プログラムのAKB48,SKE48,NMB48はどれもSayHello()メソッドとGetNumOfVote()メソッドを持っているので,Memberインターフェイスを実装しています.
  • Javaのインターフェイスと同様に,型TがインターフェイスIを実装しているとき,型Iの変数に型Tの値を代入できます.これによりポリモルフィズムが実現されます.このとき,Tを動的な型,Iを静的な型と呼びます.
  • 例えば,75行目の AKB48{"前田敦子","まえだあつこ",139892,"A"} という複合リテラルはAKB48型,112行目の SKE48{"松井玲奈","まついれな",36929,"S"} はSKE48型,119行目の NMB48{"山本彩","やまもとさやか",8697,"N"} はNMB48型です.これらを,Memberというインターフェイス型を要素とするElectionというスライスの要素として代入しています.このとき,各要素の動的な型はそれぞれ,AKB48,SKE48,NMB48であり,静的な型はどれもMemberです.
  • 124行目でthe3rdというElection型の変数(スライス)の要素 m(Member型)のメソッドGetNumOfVote()とSayHello()が呼ばれると,その要素の動的な型(AKB48,SKE48,NMB48のいずれか)のメソッドが呼ばれます(ポリモルフィズム).
  • Javaのインターフェイスと異なり,型TがインターフェイスIを実装する場合でも,TとIの関係を明示的に指定する必要はありません.これによりダックタイピングが実現されます.
  • 例えば,AKB48型がMemberインターフェイスを実装していることは,どこにも明示されていません.AKB48がSayHello()とGetNumOfVote()というメソッドを持っていることから,コンパイラによってAKB48型がMemberインターフェイスを実装していると判定されます.
  • インターフェースに関する詳細は,言語仕様書のインタフェース型型の値と特性,実践Go言語のインタフェースとそれ以外の型などを参照してください.

標準パッケージ sort でのインターフェイスの応用

  • Go言語にはsortという標準パッケージがあります.その中でSort()という関数が定義されていて,Sort()から,Len(),Less(),Swap()というメソッドが呼ばれています.
  1. func Sort(data Interface) {
  2. for i := 1; i < data.Len(); i++ {
  3. for j := i; j > 0 && data.Less(j, j-1); j-- {
  4. data.Swap(j, j-1)
  5. }
  6. }
  7. }
  • 引数がInterfaceというインターフェイス型となっています.この方は,次のように定義されています.
  1. type Interface interface {
  2. Len() int // コレクション内の要素数
  3. Less(i, j int) bool // i番目の要素がj番目要素の前に並び替えるべきか
  4. Swap(i, j int) // インデックスi,jの要素を入れ替える
  5. }
  • 従って,Sort()関数には,このLen(),Less(),Swap()の3つのメソッドをさえ実装していればどんな型でも渡すことができます.
  • 例題のプログラムでは,62行目~70行目で3つのメソッドを実装しているので,Election型をSort()関数の引数として渡すことができます.
  • ソートについては,パッケージドキュメントを参照してください.Go言語チュートリアルにも解説があります.

型switch

  • 型switchは式switchとほぼ同じですが,値の代わりに型を比較します.
  • 125行目のように switch val := m.(type) と書くことで,変数 m の型が調べられます.その型がAKB48型であれば,126行目の case AKB48: 以降が実行されます.SKE48型であれば,127行目の case SKE48: 以降が実行されます.
  • 型switchの詳細は,言語仕様を参照してください.

型アサーション

  • x.(T)は,「xにはT型の値が格納されている」というアサーションです.Tがインタフェース型でないときx.(T)は,「xの動的な型とT型が同一の型である」とアサートします.Tがインタフェース型のときx.(T)は,「xがインタフェースTを実装している動的な型である」とアサートします.
  • v, ok := x.(T) と書くと,アサーションの結果として(T, bool)型の2つの値が返されます.アサーションに成功したときは,T型のxの値がvに,trueにokが代入されます.失敗したときはT型のゼロ値とfalseが代入されます.
  • プログラムの125行目~128行目は次のように書き換えることができます.
  • if val,ok := m.(AKB48); ok {fmt.Printf("%s",val.SayTeam())}
  • if val,ok = m.(SKE48); ok {fmt.Printf("%s",val.SayGroup())}
  • この例では,1行目でmがAKB48型であるというアサーションが成功すれば(mの動的な型がAKB48であれば){}の中を実行します.2行目も同様です.
  • 型アサーションの詳細は,言語仕様を参照してください.

組み合せ代入

  • 組み合せ代入は,多値を返す操作の各要素を変数リストに個別に代入します.
  • 例えば,プログラム69行目の s[i], s[j] = s[j], s[i] では,s[j]をs[i]に,s[i]をs[j]に代入することによって,s[i]とs[j]を入れ替えています.
  • Go言語では複数の値を返す関数を定義できますが,その場合も x,y = f() のように組み合せ代入を使って,その戻り値を得ることができます.
  • x,_ = f()のようにブランク識別子'_'を使うことで,多値となる式から返された値を無視することができます.
  • 組み合せ代入についての詳細は,言語仕様を参照してください.

プログラムの実行結果

プログラムの実行結果は以下のようになります.

得票数 139892: こんにちは,前田敦子です.AKB48のチームA所属です.

得票数 122843: こんにちは,大島優子です.AKB48のチームK所属です.

得票数 74252: こんにちは,柏木由紀です.AKB48のチームB所属です.

得票数 60539: こんにちは,篠田麻里子です.AKB48のチームA所属です.

得票数 59118: こんにちは,渡辺麻友です.AKB48のチームB所属です.

得票数 52920: こんにちは,小嶋陽菜です.AKB48のチームA所属です.

得票数 52790: こんにちは,高橋みなみです.AKB48のチームA所属です.

得票数 50403: こんにちは,板野友美です.AKB48のチームK所属です.

得票数 45227: こんにちは,指原莉乃です.AKB48のチームA所属です.

得票数 36929: こんにちは,松井玲奈です.SKE48所属です.

得票数 33500: こんにちは,宮澤佐江です.AKB48のチームK所属です.

得票数 31009: こんにちは,高城亜樹です.AKB48のチームA所属です.

得票数 27957: こんにちは,北原里英です.AKB48のチームB所属です.

得票数 27804: こんにちは,松井珠理奈です.SKE48所属です.

得票数 26070: こんにちは,峯岸みなみです.AKB48のチームK所属です.

得票数 22857: こんにちは,河西智美です.AKB48のチームB所属です.

得票数 17154: こんにちは,秋元才加です.AKB48のチームK所属です.

得票数 16574: こんにちは,佐藤亜美菜です.AKB48のチームB所属です.

得票数 16455: こんにちは,横山由依です.AKB48のチームK所属です.

得票数 14137: こんにちは,増田有華です.AKB48のチームB所属です.

得票数 12387: こんにちは,倉持明日香です.AKB48のチームA所属です.

得票数 11860: こんにちは,梅田彩佳です.AKB48のチームK所属です.

得票数 11674: こんにちは,高柳明音です.SKE48所属です.

得票数 10854: こんにちは,仲川遥香です.AKB48のチームA所属です.

得票数 9910: こんにちは,多田愛佳です.AKB48のチームA所属です.

得票数 9742: こんにちは,平嶋夏海です.AKB48のチームB所属です.

得票数 9271: こんにちは,宮崎美穂です.AKB48のチームB所属です.

得票数 8697: こんにちは,NMB48の山本彩です.

得票数 7264: こんにちは,大家志津香です.AKB48のチームA所属です.

得票数 6660: こんにちは,大矢真那です.SKE48所属です.

得票数 6288: こんにちは,仁藤萌乃です.AKB48のチームK所属です.

得票数 6120: こんにちは,小森美果です.AKB48のチームB所属です.

得票数 6117: こんにちは,秦佐和子です.SKE48所属です.

得票数 5438: こんにちは,佐藤すみれです.AKB48のチームB所属です.

得票数 5411: こんにちは,大場美奈です.AKB48のチーム4所属です.

得票数 5343: こんにちは,須田亜香里です.SKE48所属です.

得票数 5220: こんにちは,前田亜美です.AKB48のチームA所属です.

得票数 5020: こんにちは,松井咲子です.AKB48のチームK所属です.

得票数 4928: こんにちは,市川美織です.AKB48のチーム4所属です.

得票数 4698: こんにちは,藤江れいなです.AKB48のチームK所属です.