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

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

ただいまの
回答率

90.49%

  • Google

    606questions

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

  • Go

    511questions

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

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

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 212

yumiyumi220

score 44

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)
    }
}

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

よろしくおねがいます。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

0

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

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

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

func main()  {
    i := conv("322")  //引数を""にするとi関数の動作が変わる
    fmt.Println(i("787"))
}

func conv(f string) func(str string) int {
    return func(str string) int {
        if f == "" {
            k, _ := strconv.Atoi(str)
            return k
        }
        return 1
    }
}

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/13 00:08 編集

    ご返答ありがとうございます!
    質問のコードでいう「関数の戻り値が関数」ならば、

    func(db *gorm.DB) *gorm.DB
    のfunc(db *gorm.DM)が戻り値で、*gorm.DBが型なのでしょうか?

    キャンセル

  • 2017/11/13 00:12

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

    キャンセル

  • 2017/11/13 00:15

    いいえ、型ではありません
    func(db *gorm.DM) *gorm.DB
    全体が戻り値です。

    まず、
    func(db *gorm.DB) *gorm.DB
    を単体で見ると、
    「*gorm.DBを戻り値としている関数」
    ですよね?
    その関数自体が戻り値となっています。

    つまり
    findByString関数は
    「*gorm.DBを戻り値としている関数、func(db *gorm.DM)」
    を返すということです。

    キャンセル

  • 2017/11/13 00:17 編集

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

    キャンセル

0

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)
    }
}


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

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

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

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

func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
    return db.Where("amount > ?", 1000)
}

func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB {
    return func (db *gorm.DB) *gorm.DB {
        return db.Scopes(AmountGreaterThan1000).Where("status in (?)", status)
    }
}

db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)


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

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

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

db.Scopes(findByString("something"))

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/13 00:06

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

    キャンセル

  • 2017/11/13 00:08

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

    キャンセル

  • 2017/11/13 00:12 編集

    一般的な関数は計算結果を文字列やbool、intなどで返すと思います。これは「関数を返す関数」です。呼び出し側は関数を受け取りますので、それを呼び出す事で本体のコードを変更せずに動的な呼び出しを行う事が出来ます。すこし例とははずれますが、以下の様なコードも参考になります。

    https://play.golang.org/p/62QkPWKwD0

    この counter という関数は、「count という変数を +1 して返す関数」を返す関数です。main はこの counter という関数を呼び出して「呼び出すごとに +1 される関数」を貰います。実際に↑の URL で run を実行するとどの様に実行されるのか分かると思います。

    キャンセル

  • 2017/11/13 00:17

    もうすこし分かりやすく説明します。Go の関数宣言は

    func 関数名(引数宣言...) 戻り値の型 {
    }

    となります。

    https://play.golang.org/p/62QkPWKwD0

    この例ですと

    counter が関数名
    戻り値の型が func() int

    となります。func() int は「int を返す関数」ですよね。なので counter は「int を返す関数」を返す関数となります。

    キャンセル

  • 2017/11/13 00:19

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

    キャンセル

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

  • ただいまの回答率 90.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Google

    606questions

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

  • Go

    511questions

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