トラック
/
Go
Go
/
演習
/
仕分け室
仕分け室

仕分け室

学習演習

はじめに

型変換

Goでは、異なる型の間で明示的に変換する必要があります。 型の間の変換(型キャストとも呼ばれます)は、変換先の型と同じ名前の関数を使って行います。 たとえば、intをfloat64に変換するには、次のようにします:

var x int = 42 // x has type int
f := float64(x) // f has type float64 (ie. 42.0)

プリミティブ型と文字列の変換

プリミティブ型(intなど)とstringの間の変換には、strconvパッケージがあります。

import "strconv"

var intString string = "42"
var i, err = strconv.Atoi(intString)

var number int = 12
var s string = strconv.Itoa(number)

型アサーション

Goのインターフェースは、その基になっている型について曖昧さを生むことがあります。 型アサーションを使うと、interfaceVariable.(concreteType)という構文で、インターフェース値が基にしている具体的な値を取り出せます。

たとえば:

var input any = 12
number := input.(int)

注意: インターフェース変数がその具体的な型の値を持っていない場合、これはパニックを引き起こします。

インターフェース値が特定の具体的な型の値を持っているかどうかは、型アサーションの2つの戻り値、つまり基になっている値と、アサーションが成功したかどうかを報告する真偽値の両方を使うことで確認できます。 たとえば:

str, ok := input.(string) // no panic if input is not a string

inputがstringを持っていれば、strが基になっている値になり、okはtrueになります。 inputがstringを持っていなければ、strはstring型のゼロ値(つまり""、空の文字列)になり、okはfalseになります。 どちらの場合もパニックは起きません。

型スイッチ

型スイッチを使うと、複数の型アサーションを連続して実行できます。 構文は型アサーション(interfaceVariable.(concreteType))と同じですが、concreteTypeがキーワードtypeに置き換わります。 例を見てみましょう:

var i any = 12 // try: 12.3, true, int64(12), []int{}, map[string]int{}

switch v := i.(type) {
case int:
    fmt.Printf("the integer %d\n", v)
case string:
    fmt.Printf("the string %s\n", v)
default:
    fmt.Printf("type, %T, not handled explicitly: %#v\n", v, v)
}

説明

ジェンは大きな工場の仕分け室で働いています。 仕分け室では、入ってくるものすべてにラベルを付けて分類する必要があります。 ジェンは、あらかじめ数値として分類されたものを担当していて、その仕分けを手伝ってくれるプログラムを必要としています。

ほとんどのプリミティブ値には、単純なラベルを付けるだけで済みます。 数値の場合は、"This is the number 2.0"という文字列を返してほしいと考えています(数値が2の場合)。 整数でも小数でも、ジェンは同じ出力を求めています。

中身を取り出すためにアンラップする必要があるBoxインターフェースがいくつかあります。 NumberBoxの場合は、"This is a box containing the number 3.0"という文字列を返してほしいと考えています(Number()メソッドが3を返す場合)。 FancyNumberBoxの場合は、"This is a fancy box containing the number 4.0"という文字列を返してほしいと考えていますが、それは型がFancyNumberの場合だけです。

想定外のものは"Return to sender"と返すようにして、ジェンが元あった場所へ送り返せるようにします。

1. 単純な数値を説明する

ジェンは、数値が"This is the number 2.0"のような文字列を返すことを望んでいます(小数点以下1桁を含みます):

fmt.Println(DescribeNumber(-12.345))
// Output: This is the number -12.3

2. 数値ボックスを説明する

ジェンは、数値ボックスが"This is a box containing the number 2.0"のような文字列を返すことを望んでいます(こちらも小数点以下1桁を含みます):

fmt.Println(DescribeNumberBox(numberBoxContaining{12}))
// Output: This is a box containing the number 12.0

3. ファンシー数値ボックスから数値を取り出すメソッドを実装する

ジェンは、FancyNumberBoxから数値を取り出すヘルパー関数を必要としています。 FancyNumberBoxがFancyNumberであれば、その値を取り出し、stringからintに変換します。 それ以外の型のFancyNumberBoxは0を返すようにします。

fmt.Println(ExtractFancyNumber(FancyNumber{"10"}))
// Output: 10
fmt.Println(ExtractFancyNumber(AnotherFancyNumber{"4"}))
// Output: 0

4. ファンシー数値ボックスを説明する

FancyNumberBoxがFancyNumberであれば、ジェンは"This is a fancy box containing the number 4.0"という文字列を返してほしいと考えています。 それ以外の型のFancyNumberBoxは"This is a fancy box containing the number 0.0"と返すようにします。

fmt.Println(DescribeFancyNumberBox(FancyNumber{"10"}))
// Output: This is a fancy box containing the number 10.0
fmt.Println(DescribeFancyNumberBox(AnotherFancyNumber{"4"}))
// Output: This is a fancy box containing the number 0.0

注意:ExtractFancyNumber関数を使いましょう!

5. それらをすべて使うDescribeAnythingを実装する

これは、任意の入力を受け取る、ジェンが必要とするメイン関数です。 DescribeAnythingは、渡された値の型に応じて、他の関数に処理を委譲します。 具体的には次のとおりです:

  • intとfloat64はどちらもDescribeNumberに委譲します
  • NumberBoxはDescribeNumberBoxに委譲します
  • FancyNumberBoxはDescribeFancyNumberBoxに委譲します
  • それ以外は"Return to sender"を返すようにします
fmt.Println(DescribeAnything(numberBoxContaining{12.345}))
// Output: This is a box containing the number 12.3
fmt.Println(DescribeAnything("some string"))
// Output: Return to sender
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
Go Exercism

仕分け室を始める準備はできましたか?

Exercismに登録すれば、34個のコンセプト166個の演習、そして本物の人間によるメンタリングとともに、Goを学んでマスターできます。すべて無料です。