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

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

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

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

Google

Googleは、アメリカ合衆国に位置する、インターネット関連のサービスや製品を提供している企業です。検索エンジンからアプリケーションの提供まで、多岐にわたるサービスを提供しています。

Q&A

2回答

443閲覧

Goの関数の書き方について

退会済みユーザー

退会済みユーザー

総合スコア0

Go

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

Google

Googleは、アメリカ合衆国に位置する、インターネット関連のサービスや製品を提供している企業です。検索エンジンからアプリケーションの提供まで、多岐にわたるサービスを提供しています。

0グッド

0クリップ

投稿2017/11/12 09:18

編集2022/01/12 10:55

Goで以下のような関数を見かけましたが、どのように動作しているのかがわかりません。
また初心者で無名関数など覚え始めたくらいです。

func findByString(name string) func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB { if name == "" { return db } return db.Where("name = ?", name) } }

それはどのような関数なのでしょうか?
動作や、なぜこのような書き方をするのかなど解説していただけると嬉しいです。

よろしくおねがいます。

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

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

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

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

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

guest

回答2

0

go

1func findByString(name string) func(db *gorm.DB) *gorm.DB { 2 return func(db *gorm.DB) *gorm.DB { 3 if name == "" { 4 return db 5 } 6 return db.Where("name = ?", name) 7 } 8}

この関数は、引数が空であれば gorm の DB をそのまま返す関数を、引数が空で無ければ where 句を設定した DB を返す関数を、返す関数です。なぜこの様な関数を作っているのかというと、gorm.Scopes という関数に渡す為です。

https://godoc.org/github.com/jinzhu/gorm#DB.Scopes

この Scope は、gorm の各種フック関数 Scopes や BeforeSave/BeforeCreate に関数を渡す事で、動的に条件などを付与する仕組みです。

ドキュメントから引用します。

go

1func AmountGreaterThan1000(db *gorm.DB) *gorm.DB { 2 return db.Where("amount > ?", 1000) 3} 4 5func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB { 6 return func (db *gorm.DB) *gorm.DB { 7 return db.Scopes(AmountGreaterThan1000).Where("status in (?)", status) 8 } 9} 10 11db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)

この OrderStatus は AmountGreaterThan1000 を使っていますが、もしこの Scopes の仕組みが無かった場合、同じ db.Where というコードを OrderStatus に入れないといけなくなりますよね。

また色々な所で AmountGreaterThan1000 や OrderStatus といった条件付けを行う場合、出来れば再利用したいですよね。そういった場合にこの Scopes が使われます。

gorm 側からすると、スコープを絞る為にコードを呼び出してあげるから、ユーザの特定の処理を関数として渡しなさいという作りになるはずです。gorm はメソッドチェインで書くスタイルなので、findByString であれば

go

1db.Scopes(findByString("something"))

と書くだけで Where 句が指定できるという仕組みなのです。

投稿2017/11/12 14:41

編集2017/11/12 14:42
mattn

総合スコア5030

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

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

退会済みユーザー

退会済みユーザー

2017/11/12 15:06

実際のコードまで丁寧にありがとうございます。 もう一つ質問したいのが、 func hogehoge() func hoge{ return func() string { } } などといったような書き方は、どのようなものなのでしょうか?
退会済みユーザー

退会済みユーザー

2017/11/12 15:08

質問のコードでいう「関数の戻り値が関数」ならば、 func(db *gorm.DB) *gorm.DB のfunc(db *gorm.DM)が戻り値で、*gorm.DBが型なのでしょうか?
mattn

2017/11/12 15:14 編集

一般的な関数は計算結果を文字列やbool、intなどで返すと思います。これは「関数を返す関数」です。呼び出し側は関数を受け取りますので、それを呼び出す事で本体のコードを変更せずに動的な呼び出しを行う事が出来ます。すこし例とははずれますが、以下の様なコードも参考になります。 https://play.golang.org/p/62QkPWKwD0 この counter という関数は、「count という変数を +1 して返す関数」を返す関数です。main はこの counter という関数を呼び出して「呼び出すごとに +1 される関数」を貰います。実際に↑の URL で run を実行するとどの様に実行されるのか分かると思います。
mattn

2017/11/12 15:17

もうすこし分かりやすく説明します。Go の関数宣言は func 関数名(引数宣言...) 戻り値の型 { } となります。 https://play.golang.org/p/62QkPWKwD0 この例ですと counter が関数名 戻り値の型が func() int となります。func() int は「int を返す関数」ですよね。なので counter は「int を返す関数」を返す関数となります。
退会済みユーザー

退会済みユーザー

2017/11/12 15:19

非常にわかりやすく、理解することができました。 またmattnさんが一部執筆されている書籍の方も参照されていただきました。非常に面白かったです! ありがとうございました!
guest

0

はじめに言っておきます。僕は素人です。
その上で素人意見を言います。
多分ですが、この関数は、「戻り値として関数を返す」という動作をするものです。
もっと言うと、findByStringの引数の値によって、returnする関数の戻り値を変えているようです。

gormと言うのはGolangのORMライブラリです。
なので、もっと単純なものに置き換えれば、動作がわかりやすくなるんじゃないですかね。

適当にそれっぽいの作りました

go

1func main() { 2 i := conv("322") //引数を""にするとi関数の動作が変わる 3 fmt.Println(i("787")) 4} 5 6func conv(f string) func(str string) int { 7 return func(str string) int { 8 if f == "" { 9 k, _ := strconv.Atoi(str) 10 return k 11 } 12 return 1 13 } 14}

convの引数に文字を入れると、戻り値のi関数は必ず1を返します
convの引数を""にすると、i関数は文字列型をint型にして返してくれます

なにか間違っていたらすみません

投稿2017/11/12 14:29

編集2017/11/12 14:33
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

退会済みユーザー

退会済みユーザー

2017/11/12 15:12 編集

ご返答ありがとうございます! 質問のコードでいう「関数の戻り値が関数」ならば、 func(db *gorm.DB) *gorm.DB のfunc(db *gorm.DM)が戻り値で、*gorm.DBが型なのでしょうか?
退会済みユーザー

退会済みユーザー

2017/11/12 15:12

DDxlkさんの例でいうと、戻り値がfunc(str string)のint型であり、これがi('"787")で実行される!というわけですね?i("322")はあくまでも関数をスイッチングするような感覚ですね? 間違っていたらすみません!!
退会済みユーザー

退会済みユーザー

2017/11/12 15:15

いいえ、型ではありません func(db *gorm.DM) *gorm.DB 全体が戻り値です。 まず、 func(db *gorm.DB) *gorm.DB を単体で見ると、 「*gorm.DBを戻り値としている関数」 ですよね? その関数自体が戻り値となっています。 つまり findByString関数は 「*gorm.DBを戻り値としている関数、func(db *gorm.DM)」 を返すということです。
退会済みユーザー

退会済みユーザー

2017/11/12 15:18 編集

int型の関数などと言うのはないですよ 「int型を返す関数」が戻り値ということです 関数をスイッチングしているという表現はわかりませんが、conv関数の引数によって、i関数の処理を変えてるということです
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問