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

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

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

Haskellは高い機能性をもった関数型プログラミング言語で、他の手続き型プログラミング言語では難しいとされている関数でも容易に行うことができます。強い静的型付け、遅延評価などに対応しています。

Q&A

解決済

2回答

4241閲覧

concatのソースコードの意味が分からない

moeii

総合スコア12

Haskell

Haskellは高い機能性をもった関数型プログラミング言語で、他の手続き型プログラミング言語では難しいとされている関数でも容易に行うことができます。強い静的型付け、遅延評価などに対応しています。

1グッド

0クリップ

投稿2016/10/22 18:58

Hoogleでconcatのソースコードを読んでいたのですがソースコードの内容がよくわからなかったので質問しました。
ここで言っている分からないは「concatの使い方が分からない」ではなく「一体このソースコードが何をしているのか分からない」と言う意味です
以下がそのソースコードです。

lang

1concat :: Foldable t => t [a] -> [a] 2concat xs = build (\c n -> foldr (\x y -> foldr c y x) n xs)

以下が入出力です。

lang

1Prelude > concat [[1,2],[3,4]] 2[1,2,3,4]

では、具体的にどの部分が分からないかですが
「\c n ->」とありますがこの「c」と「n」は一体どこから引数を取っているのでしょうか?

###考えたこと

lang

1--仮にconcatに[[1,2],[3,4]]を渡した場合 2cancat [[1,2],[3,4]] = build (\c n -> foldr (\x y -> foldr c y x) n [[1,2],[3,4]]) 3 4--buildはg:[]なので恐らく最終的にこうなるのだろうか? 5concat [[1,2],[3,4]] = build(1:2:3:4) 6 7concat [[1,2],[3,4]] = 1:2:3:4:[] 8 9--そうなるとこのラムダ式は「1:2:3:4」を返す 10--じゃあ一体cとnは何なんだろうか? 11(\c n -> foldr (\x y -> foldr c y x) n xs) 12
maisumakun👍を押しています

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

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

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

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

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

guest

回答2

0

解答の補足です。下記のような定義ですが、

haskell

1concat :: Foldable t => t [a] -> [a] 2concat xs = build (\c n -> foldr (\x y -> foldr c y x) n xs)

本来つぎのような定義でも良いはずです。

haskell

1concat :: Foldable t => t [a] -> [a] 2concat xs = foldr (\x y -> foldr (:) y x) [] xs

上のような定義になっている理由としては、このような書きかただと、コンパイラによる最適化がされるからです(RULESプラグマによる書き換えが行われる)。つぎのように最適化されます。

haskell

1foldr c n (build g) ===> g c n

関数concatの例よりも、関数mapのほうが説明しやすいので、関数mapの例を挙げます。たとえば、関数mapが、つぎのように定義されているとします。

haskell

1map f xs = build $ \c n -> foldr (c . f) n xs

このとき、つぎのようなリストを定義するとします。

haskell

1nums = map (* 3) $ map (+ 5) [2, 8, 3]

これを展開します。

haskell

1-> build $ \c1 n1 -> foldr (c1 . (* 3)) n1 2 $ build $ \c2 n2 -> foldr (c2 . (+ 5)) n2 [2, 8, 3]

ここで、foldr c n (build g)の形ができていることを確認してください。この形は、g c nのように最適化されるので、

haskell

1-> build $ \c1 n1 -> (\c2 n2 -> foldr (c2 . (+ 5)) n2 [2, 8, 3]) (c1 . (* 3)) n1 2-> build $ \c1 n1 -> foldr (c1 . (* 3) . (+ 5)) n1 [2, 8, 3]

のようになります。これを関数mapの形にもどします。上記の関数mapの定義について右の形を左のかたちにするということです。

haskell

1-> map ((* 3) . (+ 5)) [2, 8, 3]

これは結局は、つぎのような最適化をしたことになります。

haskell

1map (* 3) $ map (+ 5) [2, 8, 3] 2 -> map ((* 3) . (+ 5)) [2, 8, 3]

最適化のまえの形では、リスト[7, 13, 8]が作られますが、最適化後の形では、そのような途中のリストは作られません。これは、関数mapによる変換が、いくつ連続していても、おなじです。

このように、関数buildを利用することで、GHCでは、途中に作られるリストをなくすことができます。それにより、効率が最適化されます。

参考: モジュールGHC.Baseのソースコード

投稿2017/06/26 06:39

編集2018/09/11 01:08
YoshikuniJujo

総合スコア33

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

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

0

ベストアンサー

まず、buildのソース

Haskell

1build g = g (:) []

は、いいですね?ということでconcatのソースからbuildをなくせば

Haskell

1concat xs = (\c n -> foldr (\x y -> foldr c y x) n xs) (:) []

となります。つまり、buildの引数であるラムダ式に、:[]の引数を与えるのがbuildです。ということで、cとnに入るのは:[]です。

Haskell

1concat xs = foldr (\x y -> foldr (:) y x) [] xs

fordrが二重になっていてわかりにくいですが、次のように計算していきます。(実際は遅延評価のため、求められるまではそれぞれ計算されませんが)

Haskell

1foldr (\x y -> foldr (:) y x) [] [[1,2][3,4]] 2(\x y -> foldr (:) y x) [1,2] $ (\x y -> foldr (:) y x) [3,4] [] 3(\x y -> foldr (:) y x) [1,2] $ foldr (:) [] [3,4] 4(\x y -> foldr (:) y x) [1,2] $ 3:(4:[]) 5(\x y -> foldr (:) y x) [1,2] $ 3:[4] 6(\x y -> foldr (:) y x) [1,2] [3,4] 7foldr (:) [3,4] [1,2] 81:(2:[3,4]) 91:[2,3,4] 10[1,2,3,4]

といった感じでconcatできるということです。

投稿2016/10/22 23:07

raccy

総合スコア21735

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問