質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

解決済

1回答

1730閲覧

Golang で switch 文による型ごとの処理

Yhaya

総合スコア439

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

0グッド

2クリップ

投稿2020/07/23 07:35

質問したいこと

interface を引数に持つ関数の内部で、switch 文を使って引数の型ごとに処理を分けるときにどのようにしたら簡潔にコードをかけるかわからない

環境

  • Ubuntu 18.04
  • go 1.14.2

問題の詳細

挿入ソートのコードを go を使って書いています。

go

1package insertsort 2 3func insertSort(x interface{}, size int) { 4 switch value := x.(type) { 5 case *[]int: 6 for i := 0; i < size; i++ { 7 tmp := (*value)[i] 8 j := i - 1 9 10 for j >= 0 { 11 if tmp < (*value)[j] { 12 (*value)[j+1] = (*value)[j] 13 (*value)[j] = tmp 14 j-- 15 } else { 16 break 17 } 18 } 19 } 20 case *[]string: 21 for i := 0; i < size; i++ { 22 tmp := (*value)[i] 23 j := i - 1 24 25 for j >= 0 { 26 if tmp < (*value)[j] { 27 (*value)[j+1] = (*value)[j] 28 (*value)[j] = tmp 29 j-- 30 } else { 31 break 32 } 33 } 34 } 35 default: 36 37 } 38} 39

引数には整数の配列や文字列の配列などを許容したくて interface{} にしています。中の処理では、型ごとに switch を使って分岐しているのですが、 *[]int のブランチと *[]string のブランチで中身は全く同じになってしまっています。

これを解決するために分岐の部分を

go

1case *[]int, *[]string:

のように書いてみたのですが、invalid indirect of value (type interface {}) というエラーが出てしまいます。

この書き方だと上のブランチのほかに *[]float64 などどんどん分岐を増やしていくと重複だらけのコードになってしまうので困っているのですが、どのように書くのが正しいのでしょうか?

よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Goでのオススメの考え方は「必要なものだけを実装する」です。

標準のソート以外が必要になる型は実際には限られるはずです。
必要だとわかってるものだけを実装するのがGoの流儀かなと思います。

実際にやってみたことがあるけれど結局実装を複雑に膨らませただけでいいことは何もありませんでした。
使われるかわからない実装をして、そのためのテストを組むのはもったいないですしね。

それでも複数の型に対応する実装を展開する理由ができた場合、僕の場合はgo generateを活用します。

追記

現時点での僕のオススメの実装例を以下に示します。

以下のツールをインストールします。

shell

1go get github.com/cheekybits/genny

以下の2つのコードをメインパッケージにおきます
(必要ならパッケージを分けても構いません)

go

1package main 2 3import "github.com/cheekybits/genny/generic" 4 5//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "Type=string,int" 6 7// Type ... 8type Type generic.Number 9 10// SortType ... 11func SortType(s []Type) { 12 size := len(s) 13 for i := 0; i < size; i++ { 14 tmp := s[i] 15 j := i - 1 16 17 for j >= 0 { 18 if tmp < s[j] { 19 s[j+1] = s[j] 20 s[j] = tmp 21 j-- 22 } else { 23 break 24 } 25 } 26 } 27}

go

1package main 2 3import "fmt" 4 5// Sort ... 6func Sort(s interface{}) { 7 switch v := s.(type) { 8 case []int: 9 SortInt(v) 10 case []string: 11 SortString(v) 12 } 13} 14 15func main() { 16 is := []int{3, 2, 5} 17 Sort(is) 18 fmt.Println(is) 19 ss := []string{"oge", "moge", "hoge"} 20 Sort(ss) 21 fmt.Println(ss) 22}

以下のようにgo-generateを行えば、
SortInt、SortStringという関数定義が自動生成されます。

shell

1go generate .

以下のコマンドで実行したりできます

shell

1go run .

投稿2020/07/23 11:16

編集2020/07/23 23:58
nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobonobo

2020/07/23 11:20

もちろん来年以降に使えるようになるかもしれないジェネリクス 機能を待ってもいいわけですが。
Yhaya

2020/07/23 14:36

nobonoboさん、回答していただきありがとうございます。就活のコード面接対策でアルゴリズムを一通りさらい直していたところ、ふと複数の型への拡張は Go だとどうするのだろうと思って質問してみましたが、言われてみれば nobonobo さんの言うとおりですね。 go generate は初めて知りました。ありがとうございます。
nobonobo

2020/07/24 00:10

go-generateの例を追記しました。 質問に書かれたコードでひとつ指摘するとすれば、 Goのスライスの値の実体は参照型なので僕の書いた実装例のように「スライス」を渡すだけでソートを実装することができます。 (スライスを渡した引数の内容を変更したとき、渡す前のスライスにも変更が反映されます。) わざわざ「スライスへのポインタ」という概念を利用する必要はありません。 (ただし、要素数に関わる情報は値渡しなので、要素が増えたり減ったりする場合は「スライスへのポインタ」という概念が必要になる場合もあります)
Yhaya

2020/07/24 05:36

ありがとうございます。go-generate 試してみました。スライスの関数への渡し方までご指摘いただきありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問