回答編集履歴

2

微修正

2019/06/11 11:34

投稿

tamoto
tamoto

スコア4228

test CHANGED
@@ -126,9 +126,11 @@
126
126
 
127
127
  Transducers の理解 3. の内容自体は正しいですが、コードでは全くそうなっていないのです。
128
128
 
129
- 質問のコードを展開すると、動いているのはただの `arr.map(add1).filter(even)` となっているのです。
129
+ 質問のコードを展開すると、動いているのはただの `arr.map(add1).filter(even)` となっていす。
130
130
 
131
+
132
+
131
- transducer を用いて生成され reducer は、ただ1回の reduce 際に、1要素毎に合成した各関数を実行する (質問に引用している画像の通りです) からこそ、中間データを生成しないのです。
133
+ transducer を用いて生成され reducer は、ただ1回の reduce を実行する際に、1要素毎に合成した各関数を実行する (質問に引用している画像の通りです) からこそ、中間データを生成しないのです。
132
134
 
133
135
 
134
136
 

1

内容の更新

2019/06/11 11:34

投稿

tamoto
tamoto

スコア4228

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