PageRenderTime 67ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 0ms

/JSGohan/reduce.html

https://github.com/azu/slide
HTML | 262 lines | 261 code | 1 blank | 0 comment | 0 complexity | 60d4c8335a5d932194b0e6124b7444ea MD5 | raw file
  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="ja">
  4. <meta charset="UTF-8">
  5. <title>Array.prototype.reduce Dance</title>
  6. <noscript>
  7. <style>
  8. .main-content {
  9. display: none;
  10. }
  11. </style>
  12. </noscript>
  13. <link rel="stylesheet" href="https://azu.github.io/pdf-slide-html/index.css"/>
  14. <link rel="canonical" href="https://azu.github.io/slide/JSGohan/reduce.html">
  15. <link rel="author" href="https://www.hatena.ne.jp/efcl/" />
  16. </head>
  17. <body itemscope itemtype="http://schema.org/Article">
  18. <div class="main-content">
  19. <meta itemprop="name headline" content="Array.prototype.reduce Dance"/>
  20. <iframe id="main-slide"
  21. src="https://azu.github.io/slide-pdf.js/?slide=https://azu.github.io/slide/JSGohan/reduce.pdf"
  22. scrolling="no"
  23. allowtransparency="true"
  24. width="100%"
  25. height="100%"
  26. style="border:0;">
  27. </iframe>
  28. <aside class="slide-controller">
  29. <span class="slide-date-published">公開日:<time itemprop="datePublished" datetime="2014-07-09" id="datePublished">
  30. 2014-07-09
  31. </time></span>
  32. <span class="slide-date-modified">変更日:<time itemprop="dateModified" datetime="2015-12-06" id="dateModified">
  33. 2015-12-06
  34. </time></span>
  35. <a href="https://azu.github.io/slide/JSGohan/reduce.pdf" title="Array.prototype.reduce Dance">
  36. <svg xmlns="http://www.w3.org/2000/svg"
  37. version="1.1"
  38. class="svg-icon"
  39. viewBox="0 0 512 512">
  40. <path
  41. d="M 224 64 L 224 272 L 128 176 L 128 256 L 256 384 L 384 256 L 384 176 L 288 272 L 288 64 L 224 64 z M 64 320 L 64 448 L 448 448 L 448 320 L 416 320 L 416 416 L 96 416 L 96 320 L 64 320 Z"
  42. style="fill:#000000"></path>
  43. </svg>
  44. Download PDF</a>
  45. <button class="fullscreen-button" id="js-fullscreen-button">
  46. <svg xmlns="http://www.w3.org/2000/svg"
  47. version="1.1"
  48. class="svg-icon"
  49. viewBox="0 0 533 533">
  50. <g>
  51. <path d="M533.333,0v216.667L450,133.333l-100,100l-50-50l100-100L316.667,0H533.333z M233.333,350l-100,100l83.333,83.333H0
  52. V316.667L83.333,400l100-100L233.333,350z"></path>
  53. </g>
  54. </svg>
  55. Full Screen
  56. </button>
  57. </aside>
  58. </div>
  59. <article class="markdown-body" itemprop="articleBody"><h1 id="array-prototype-reduce-dance">Array.prototype.reduce Dance</h1>
  60. <hr>
  61. <h2 id="basic">Basic</h2>
  62. <blockquote>
  63. <p>var assert = require(&quot;assert&quot;);</p>
  64. </blockquote>
  65. <ul>
  66. <li>forと違いimmutableなオブジェクトを使わないで合計を出せる</li>
  67. <li><a href="http://toolness.github.io/slowmo-js/?code=%5B1%2C%202%5D.reduce(function%20(x%2C%20y)%20%7B%20return%20x%20%2B%20y%20%7D)%3B&amp;filterrange=5-5">Basic flow</a></li>
  68. </ul>
  69. <pre><code class="lang-js">var total = [1, 2, 3, 4, 5].reduce(function (a, b) {
  70. return a + b;
  71. });
  72. assert.equal(total, 15);
  73. </code></pre>
  74. <hr>
  75. <h3 id="-">初期値</h3>
  76. <ul>
  77. <li>初期値を指定することもできる</li>
  78. </ul>
  79. <pre><code class="lang-js">var initialTotal = [0, 1, 2, 3, 4].reduce(function (a, b) {
  80. return a + b;
  81. }, 10);
  82. assert.equal(initialTotal, 20);
  83. </code></pre>
  84. <hr>
  85. <h3 id="-">例外</h3>
  86. <ul>
  87. <li>空の配列の場合は例外を吐く</li>
  88. <li><a href="http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.21" title="15.4.4.21 Array.prototype.reduce">15.4.4.21 Array.prototype.reduce</a><ul>
  89. <li>Step 8.c</li>
  90. </ul>
  91. </li>
  92. </ul>
  93. <pre><code class="lang-js">assert.throws(
  94. function () {
  95. [].reduce(function (a, b) {
  96. return a + b
  97. });
  98. },
  99. TypeError,
  100. &quot;k &lt; len の場合はTypeError&quot;
  101. );
  102. </code></pre>
  103. <hr>
  104. <hr>
  105. <h2 id="every-by-reduce">every by reduce</h2>
  106. <pre><code class="lang-js">var assert = require(&quot;assert&quot;);
  107. assert.ng = function(value, message){
  108. assert.equal(false, !!value, message);
  109. };
  110. </code></pre>
  111. <ul>
  112. <li><code>reduce</code> があれば補完mapやeveryなどは実装できる</li>
  113. <li>柔軟性が高い</li>
  114. </ul>
  115. <blockquote>
  116. <p>このうちreduceが一番強力でmapやfilterやsumなど他の関数もこれをもとに定義できます</p>
  117. </blockquote>
  118. <p>via <a href="https://gist.github.com/ympbyc/5564146" title="Functional JavaScript">Functional JavaScript</a></p>
  119. <hr>
  120. <h3 id="-array-every-https-developer-mozilla-org-ja-docs-web-javascript-reference-global_objects-array-every-array-every-"><a href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/every" title="Array.every">Array.every</a></h3>
  121. <pre><code class="lang-js">function every(array, predicate) {
  122. return array.reduce(function (prev, current, index, list) {
  123. if (prev) {
  124. return predicate(current, index, list);
  125. } else {
  126. return prev;
  127. }
  128. }, true);
  129. }
  130. function isBigEnough(element, index, list) {
  131. return (element &gt;= 10);
  132. }
  133. assert.ok(every([12, 130, 44], isBigEnough));
  134. assert.ng(every([1, 100, 200], isBigEnough));
  135. </code></pre>
  136. <hr>
  137. <h2 id="map">map</h2>
  138. <blockquote>
  139. <p>var _ = require(&quot;underscore&quot;);</p>
  140. </blockquote>
  141. <ul>
  142. <li><code>map</code> は基本的に入力の個数と出力の個数が同じ</li>
  143. <li><code>map</code> だけだと <code>filter</code> (select) までは行えない</li>
  144. </ul>
  145. <pre><code class="lang-js">var filteredNumberToString = function (e) {
  146. if (typeof e === &quot;number&quot;) {
  147. return String(e);
  148. }
  149. };
  150. var mappedArray = [1, null, 3].map(filteredNumberToString);
  151. // 現実はundefinedになる
  152. assert.deepEqual(mappedArray, [&quot;1&quot;, undefined, &quot;3&quot;]);
  153. </code></pre>
  154. <hr>
  155. <h3 id="flatmap">flatMap</h3>
  156. <ul>
  157. <li><code>reduce</code> なら違う形(入力と出力の個数が異なる)ものを返せる</li>
  158. </ul>
  159. <pre><code class="lang-js">function flatMap(obj, iterator) {
  160. return _.reduce(obj, function (memo, value, index, list) {
  161. var items = iterator(value, index, list);
  162. if (items == null) {
  163. return memo;
  164. }
  165. return memo.concat(items);
  166. }, []);
  167. }
  168. var flatMappedArray = flatMap([&quot;string&quot;, 1, null, 3], filteredNumberToString);
  169. // Numberだけにfilter + NumberをStringに変換した結果を返す
  170. assert.deepEqual(flatMappedArray, [&quot;1&quot;, &quot;3&quot;]);
  171. </code></pre>
  172. <hr>
  173. <h3 id="-">高階関数</h3>
  174. <ul>
  175. <li>関数を返す関数の事</li>
  176. </ul>
  177. <pre><code class="lang-js">var ComparisonResult = {
  178. ascending: -1,// &lt;
  179. same: 0, // ==
  180. descending: 1 // &gt;
  181. };
  182. function comparator(predicate) {
  183. return function (x, y) {
  184. if (predicate(x, y)) {
  185. return ComparisonResult.ascending
  186. } else if (predicate(y, x)) {
  187. return ComparisonResult.descending;
  188. }
  189. return ComparisonResult.same;
  190. };
  191. }
  192. </code></pre>
  193. <hr>
  194. <h3 id="predicates-to-comparisonresult">Predicates to ComparisonResult</h3>
  195. <ul>
  196. <li>真偽値 -&gt; comparator を通して -&gt; <code>ComparisonResult</code> を返す関数を作る</li>
  197. <li>真偽値を返す関数を Predicates という</li>
  198. <li>真偽値から <code>&lt;</code> <code>==</code> <code>&gt;</code> の3種類の状態を返せるのでシンプル</li>
  199. </ul>
  200. <pre><code class="lang-js">function isLessOrEqual(x, y) {
  201. return x &lt;= y;
  202. }
  203. var values = [2, 3, -1, -6, 0, -108, 42, 10];
  204. var expectedSortedValues = [-108, -6, -1, 0, 2, 3, 10, 42];
  205. var results = values.sort(comparator(isLessOrEqual));
  206. assert.deepEqual(results, expectedSortedValues);
  207. </code></pre>
  208. <hr>
  209. <hr>
  210. <h2 id="null-guard">Null Guard</h2>
  211. <ul>
  212. <li>配列に <strong>falsy</strong> な値が含まれてる意図しない結果になってしまう</li>
  213. </ul>
  214. <pre><code class="lang-js">var nums = [1, 2, 3, null, 5];
  215. var multFn = function (total, n) {
  216. return total * n;
  217. };
  218. _.reduce(nums, multFn);// =&gt; 0 になってしまう…
  219. </code></pre>
  220. <hr>
  221. <h3 id="null-check-">Null Check?</h3>
  222. <ul>
  223. <li>multFnにnullチェックを入れるのは本質的じゃない</li>
  224. <li>nullチェックを加える <strong>高階関数</strong> を作る</li>
  225. </ul>
  226. <pre><code class="lang-js">function fnull(fn, defaultValue) {
  227. return function () {
  228. // falsyだった場合はdefaultValueにしたものに引数を構築し直す
  229. var args = _.map(arguments, function (e) {
  230. return e != null ? e : defaultValue;
  231. });
  232. return fn.apply(null, args);
  233. }
  234. }
  235. </code></pre>
  236. <hr>
  237. <h3 id="safemult">safeMult</h3>
  238. <ul>
  239. <li>falsyな値はdefalutValueに変更される</li>
  240. </ul>
  241. <pre><code class="lang-js">// var nums = [1, 2, 3, null, 5];
  242. // falsyな値はdefalutValue(1)に変更される
  243. var safeMultFn = fnull(multFn, 1);
  244. var totalMult = _.reduce(nums, safeMultFn);
  245. assert.equal(totalMult, 30);
  246. </code></pre>
  247. <hr>
  248. <h3 id="-">おわり</h3>
  249. <p>サンプルコード</p>
  250. <ul>
  251. <li><a href="https://github.com/azu/ReduceDance" title="azu/ReduceDance">azu/ReduceDance</a></li>
  252. </ul>
  253. <p>参考</p>
  254. <ul>
  255. <li><a href="http://www.functionaljavascript.com/" title="functional javascript">functional javascript</a></li>
  256. <li><a href="http://taiju.hatenablog.com/entry/20110331/1301535208#fn1" title="reduce関数は結構有用っていうお話 - あと味">reduce関数は結構有用っていうお話 - あと味</a></li>
  257. </ul>
  258. </article>
  259. <script async src="//azu.github.io/pdf-slide-html/index.js"></script>
  260. </body>
  261. </html>