teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

2

微修正

2019/06/11 11:34

投稿

tamoto
tamoto

スコア4333

answer CHANGED
@@ -62,9 +62,10 @@
62
62
 
63
63
  型シグネチャとして見ると、標準の射影関数と同等となっていることが分かると思います。つまり、Array に対してそれぞれが射影を行っているため、実行される度に全要素が処理された中間データが生成されています。
64
64
  Transducers の理解 3. の内容自体は正しいですが、コードでは全くそうなっていないのです。
65
- 質問のコードを展開すると、動いているのはただの `arr.map(add1).filter(even)` となっているのです。
65
+ 質問のコードを展開すると、動いているのはただの `arr.map(add1).filter(even)` となっていす。
66
- transducer を用いて生成された reducer は、ただ1回の reduce 際に、1要素毎に合成した各関数を実行する (質問に引用している画像の通りです) からこそ、中間データを生成しないのです。
67
66
 
67
+ transducer を用いて生成される reducer は、ただ1回の reduce を実行する際に、1要素毎に合成した各関数を実行する (質問に引用している画像の通りです) からこそ、中間データを生成しないのです。
68
+
68
69
  ---
69
70
 
70
71
  まとめると、

1

内容の更新

2019/06/11 11:34

投稿

tamoto
tamoto

スコア4333

answer CHANGED
@@ -1,20 +1,29 @@
1
+ (2019/06/11 追記: 全体修正しました)
2
+
1
3
  こんにちは。
2
4
 
3
- 回答がななので書きます、そ課題でもんでい質問に対して答えまと、あなたの理解は一ミリ合っていせん
5
+ 回答は「Transducers という概念ようなものか」と質問に対すのとし
4
6
 
5
- Transducers については今さっき質問に記載のリンク先を読んで理解した程度ですが、
6
- の記事が言 Transducers の本質は、
7
+ まず簡単に説明すると、質問内参照の記事が言っている Transducers の概念とは、
7
8
 
8
9
  「reduce が使う関数 `(acc, a) => acc` を composable にする」
9
10
 
10
11
  というものです。
11
- まずは上から2番目のリンクの記事をちゃんと読むのがいです。
12
+ 上から2番目のリンクの記事のコード最も理解しやすと思うの、書かれているコードを順番に一つずつ追いかけてみると理解につながると思います。
12
13
 
14
+ 以下、Transducders の概念の解説です。
15
+
13
16
  ---
14
17
 
18
+ 関数型言語における中心的な概念を表した一つに、map/reduce というものがあるのは質問者さんも知っていると思います。
19
+ この map というのは射影変換、特定のデータ構造 (例えば集合、Array) に対して、要素を変換する関数を Array から Array への変換関数に格上げするものです。既に使っている Array.map はまさに配列の要素に対して一括変換するものですね。
20
+ そして reduce というのは、Array などのデータ構造を「処理」する関数によって「集計」「押し潰し」を行う関数と言えます。
15
- 暇なで丁寧解説ると、
21
+ Transducers はこ reduce 着目した概念で
22
+
16
- まずは前提として、reduce に渡す関数 `(acc, a) => acc` に `reducer` という別名を付けておきます。
23
+ まずは前提として、reduce に渡す関数 `(acc, a) => acc` に `reducer` という別名を付けておきます。つまり、Array.reduce は reducer を使ってデータ構造を集計する関数、という意味になります。
24
+
17
25
  ここで、任意の reducer を渡すとそれを用いて新たな reducer を生成する関数 `reducer => reducer` を想定します。これに `transducer` という名前を付けます。
26
+
18
27
  このとき、`compose` の型は、
19
28
 
20
29
  ```
@@ -24,18 +33,21 @@
24
33
 
25
34
  となります。
26
35
  複数の transducer を合成して一つの transducer にする関数であることを意味します。
27
- となると、`map` や `filter` が返すべきものは `transducer`、つまり `reducer => reducer` である必要があります。
36
+ となると、Transducers における `map` や `filter` が返すべきものは `transducer`、つまり `reducer => reducer` である必要があります。
28
37
 
29
38
  ```
30
39
  map : (a => b) => transducer
31
40
  map : (a => b) => reducer => reducer
41
+ map : (a => b) => ((acc, b) => acc) => ((acc, a) => acc)
42
+
32
43
  filter : (a => Bool) => transducer
33
44
  filter : (a => Bool) => reducer => reducer
45
+ filter : (a => Bool) => ((acc, a) => acc) => ((acc, a) => acc)
34
46
  ```
35
47
 
36
- このるわけです。
48
+ として表すとこうります。
37
49
 
38
- 任意数の transducer を合成し、その transducer に最終結果を得るための `reducer : (acc, a) => acc` を渡すことで `(acc, a) => acc` が得られます。これを reduce で利用するのが Transducers の仕組みです。
50
+ これらの transducer を compose で合成し、出来上がった transducer に最終結果を得るための `reducer : (acc, a) => acc` を渡すことで `(acc, a) => acc` が得られます。これを reduce で利用するのが Transducers の仕組みです。
39
51
 
40
52
  あなたのコードでは、
41
53
 
@@ -46,13 +58,12 @@
46
58
  transducer : Array => Array
47
59
  ```
48
60
 
49
- となっており、根本的にものが違います。
61
+ となっており、これでは根本的に別物となっています。
50
62
 
51
- transducer を用いて生成された reducer は、reduce の1ステップ毎に合成した各関数を実行する (質問に引用している画像の通りです) のに対し、
52
- あなたコードでは Array に対してそれぞれが射影を行っているため、射影が実行される度に全要素処理た中間データが生成されています。
63
+ 型シグネチャとして見ると、標準射影関数と同等となっていることが分かると思います。つまり、Array に対してそれぞれが射影を行っているため、実行される度に全要素処理された中間データが生成されています。
53
- つまり、Transducers の説明 3. の内容自体は正しいですが、コード全くそうなっていないのです。
64
+ Transducers の理解 3. の内容自体は正しいですが、コードでは全くそうなっていないのです。
54
- なにやら難しいことやろうした結果実際に動いているのはただの `arr.map(add1).filter(even)` です。
65
+ 質問のコード展開すると、動いているのはただの `arr.map(add1).filter(even)` となっているのです。
55
- なん意味もあません
66
+ transducer を用いて生成さた reducer 、ただ1回 reduce 際に、1要素毎に合成した各関数を実行する (質問に引用している画像の通です) からこそ、中間データを生成しないのです
56
67
 
57
68
  ---
58
69
 
@@ -60,4 +71,21 @@
60
71
 
61
72
  * Transducers は、reduce に渡す関数 `(acc, a) => acc` を合成可能にする概念
62
73
  * map や filter、その他 Transducers の関数群は、それぞれ任意の関数や値を引数に取って transducer を返す関数である
63
- * 合成した transducer に任意の reducer を渡すことで reducer が得られ、それを `arr.reduce()` で使用することで動作する
74
+ * 合成した transducer に任意の reducer を渡すことで reducer が得られ、それを `arr.reduce()` で使用することで動作する
75
+
76
+ ---
77
+
78
+ 以上の説明の元で、質問にある定義について注釈を入れると、
79
+
80
+ > 1. compose()して返却された関数構成群がtransducerである。
81
+
82
+ compose に「渡すもの」こそが transducer で、だからこそ合成されたものも transducer なのです。compose はその名の通り「合成」だけを意味しており、transducer を生成するような意味はありません。
83
+
84
+ > 2. transducerを実行するには、❶transduce()に渡す以外にも、❷transducer()に配列を渡す事でも実行できる。
85
+
86
+ transducer は配列を取る関数ではないです。reducer を受け取って reducer を返す関数なので、transducer に (acc, a) => acc を渡して、返ってきた (acc, a) => acc を Array.reduce に渡すことで初めて実行されます。
87
+ transduce は、ソースと transducer と reducer と initValue をまとめて取るだけの単なる糖衣関数です。
88
+
89
+ > 3. ❷の内部処理において、中間値が生成されるような処理ではなく、下画像のような効率的な処理がなされる。
90
+
91
+ これは正しいです。transducer は全ての処理が合成された1つの reducer を生成するため、reduce 処理の実行時に各要素毎に処理され、中間データは生成されません。