Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
第一級関数と作用的プログラミング
JavaScript*で学ぶ関数型プログラミング*2*章
YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 1
内容
• 第一級要素としての関数
• JavaScript+のプログラミングパラダイムについて
• 作用的プログラミング・コレクション中心プログラミング
• データ思考プログラミング
• テーブルのようなデータの取り扱い
YOSHIKAWA)R...
第一級要素としての関数
YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 3
第一級関数
• JavaScript+の第一級要素+:+数値・文字列+...+関数
• つまり関数と数値や文字列は同じ性質を持つ
• 変数に代入可能
• 任意のタイミングで生成可能
• 関数の引数・戻り値になる+3
3
"ここでは""関数型""...
// 変数・配列・オブジェクトのフィールドに代入可能
var f = function() { return 42 },
a = [42, function() { return 42 }],
o = {n: 42, f: function()...
閑話休題:"JavaScript"は関数型言語か?"1
• そもそも""関数型言語""の基準は?
• 関数が第一級要素?
• 参照透過性?
• 副作用の隠 ?
• 型推論?
1
"h$p://www.slideshare.net/ksknac/...
そもそも関数型とは?!2
• 以下のようなプログラミング"パラダイム"に対する形容"3
• プログラムのほとんどが関数であるような書き方
• 非関数や副作用の影響を可能な限り小さくする
• 使用する言語に関係なく実現が可能
3
"ここでは""関...
改めて:"JavaScript"は関数型言語か?
• 関数型(パラダイムのプログラミングをしやすい)言語か?
• JavaScript-では関数が第一級要素となっている
• 関数型スタイルでのプログラミングはしやすい
• その他の関数型の機能は...
JavaScript*の*
*プログラミングパラダイム
YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 9
・!命令型プログラミング
・!関数型プログラミング
・!プロトタイプベースオブジェクト指向
・!メタプログラミング
5
"よく見られるものを示した。ES6"からクラスベースオブジェクト指向も見られるようになるかも。
YOSHIKAWA)Ryot...
命令型プログラミング
• 実行フローをほぼそのまま書き下す
• プログラムの実行状態を直接操作・参照する
• 対となるパラダイムは"宣言型プログラミング
• 関数型や論理型は宣言型プログラミングに属する
• C"言語・BASIC・PASCAL"...
例)"99"本のビール
• 99#から開始し、1#ずつ引いていき繰り返す
• 現在の数を#X#として、次のように歌う
• X#本のビールが残ってる#=>#X#本のビール
• ひとつ取って、隣に回せ#=>#X(1#本のビールが残ってる
• X=1...
// 命令型プログラミング
var lyrics = [];
for (var bottles = 99; bottles > 0; bottles--) {
lyrics.push(bottles + " 本のビールが残ってる");
lyri...
// 関数型スタイルへのパラダイムシフト
// "n 本目のビールの歌詞を生成する" 機能を関数へと分割
function lyricSegment(n) {
return _.chain([]) // メソッドチェイン記法
.push(n +...
// 関数の繋ぎあわせ
function song(start, end, lyricGen) {
return _.reduce(_.range(start,end,-1), // 値を返す
function(acc,n) {
return ...
命令型と関数型
• 前述の例のように、ルールを守って処理を細かく関数に分ける
• 命令型では"99"本・同じ歌詞でしか動かせなかった
• 関数型では関数として定義することにより抽象的に
• 99"本のビール,"30"本のビール..."は"son...
何が良いと感じたか
• 機能別に分けることにより
• 変更に強いプログラムが作れる
• 再利用性が高く同じようなコードを何度も書かなくて良い
• 機能毎のテストが書きやすくなる
• 参照等価にするとここが∼みたいな話は色々あるけど...#7
7...
プロトタイプベースオブジェクト指向
• JavaScript+のオブジェクト指向はプロトタイプベース
• 既存のオブジェクトをプロトタイプとして使用する+8
• クラス指向のオブジェクト指向に対してローレベルで動作する
• 柔軟性はあるが罠も多...
罠
// オブジェクトのフィールド値に関数が存在できる
var a = {name: "a", f: function(){return this}};
// this は a 自身を指す。計画通り。
a.f() // => {name: "a...
メタプログラミング
• ソースコードを生成するようなプログラムを書くスタイル
• 本書では、以下のような例が紹介されている"9
function Point2D(x, y) { this._x = x; this._y = y; }
funct...
作用的プログラミング!
!コレクション中心プログラミング
YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 21
作用的プログラミング
• 関数"f: A()"と"f: B()"を次のように使用するプログラミング
• B"を呼び出すことで"A"を実行させる"10
• B(element, A())
• "作用的"という言葉はコンテキストによって異なる意味を...
作用的プログラミングの例
var nums = [1,2,3,4,5];
function doubleAll(array) {
return _.map(array, function(n) { return n*2 });
}
double...
コレクション中心プログラミング
• 関数型プログラミング
• コレクションに入った多数の値に同じ処理をしやすい
• [1, 2, 3, 4, 5]"や"{a: 1, b: 2}"...
• 本書はこの考え方に則りデータ構造を有効活用する方法を推...
コレクション中心プログラミングの例
_.map({a: 1, b: 2}, _.identity); //=> [1,2]
// ---------------------
_.map({a: 1, b: 2}, function(v,k) {...
作用的プログラミングのその他の例
// ReduceRight
var nums = [100,2,25];
function div(x,y) { return x/y };
_.reduce(nums, div); //=> 2
_.red...
// ReduceRight
// allOf, anyOf の使用例
function T() { return true }
function F() { return false }
allOf(); //=> true
allOf(T,...
// find, reject, filter, all, any
// find: コレクションとプレディケートを取り真となる要素を返す
_.find(['a', 'b', 3, 'd'], _.isNumber); //=> 3
// fi...
// sortBy, countBy, groupBy
var people = [{name: "Rick", age: 30}, {name: "Jaka", age: 24}];
// コレクションと関数を受け取り関数の基準に応じて並べ替...
作用的関数の定義
// 作用的ではない関数達
// 幾つかの配列を結合する関数
function cat() {
var head = _.first(arguments);
if (existy(head))
return head.conc...
// 作用的な関数 mapcat
// _.map を使用し fun を受け取り渡されたコレクションの要素全てに対して fun を実行する
// その後 _.map から返された全ての要素を結合する
function mapcat(fun, c...
function butLast(coll) {
return _.toArray(coll).slice(0, -1);
}
function interpose (inter, coll) {
return butLast(mapcat(f...
作用的関数
• 低レベルの関数によって組み立てられた個別の機能を組合わせ
• 複数の関数が作用的に呼び出される
• それぞれの関数がデータを処理し最後の解に至る
• 関数型プログラミングの重要な切り口
• 今後本書ではこのようなパターンが多数登...
データ思考
• JavaScript+におけるオブジェクト型は非常に強力
• ポリモーフィックディスパッチ/単純な連想データストア
• データストアとして扱う関数は+JavaScript+自体にはほぼ無い
• Underscore+が有用なツー...
// keys/values は key/value を返す(当然)
var zombie = {name: "Bub", film: "Day of the Dead"};
_.keys(zombie); //=> ["name", "fil...
// シーケンシャルな処理を行い、_.object でオブジェクトを再構成する例
_.object(_.map(_.pairs(zombie), function(pair) {
return [pair[0].toUpperCase(), p...
// _.defaults は指定したキーが存在しない場合デフォルト値として追加する
_.pluck(_.map([{title: "Chthon", author: "Anthony"},
{title: "Grendel", author:...
// findWhere, where は 2 番目の引数の条件にマッチするオブジェクトを
// 1 番目の引数から探してくる
var library = [{title: "SICP", isbn: "0262010771", ed: 1},...
テーブルのようなデータ
• 前項で示した"library"のようなデータの取り扱い
• JavaScript"のオブジェクトの配列をデータテーブルとして見る
• 行が"JavaScript"オブジェクト/セルがオブジェクトのキー値ペア
YOSH...
テーブルのようなデータ!2
• タイトルを抜き出したテーブル
SELECT title FROM library;
_.pluck(library, 'title'); // ここまでで習った JavaScript での同等の表現
// 但し、...
// _.pick を使用しテーブルデータの抽象型を維持する
function project(table, keys) {
return _.map(table, function(obj) {
return _.pick.apply(nul...
データの抽出
• 上記で見たようなデータ構造を破壊して必要な値を得る操作
• あるモジュールから別のモジュールへデータを「引渡す」操作
• 関数型スタイルではデータの内容/変換/フォーマットを考える
YOSHIKAWA)Ryota)(2015/...
抽象テーブル型
• SQL%では%AS%句を使ってカラムに別名を与えることが可能
SELECT ed AS edition FROM library; /* 下のテーブルのような出力 */
• 次に、JavaScript+で+as+や+wher...
// マップデータを指定した通りにリネームする関数
function rename(obj, newNames) {
return _.reduce(newNames, function(o, nu, old) {
if (_.has(obj,...
// rename 関数を使用し実装した as 関数
function as(table, newNames) {
return _.map(table, function(obj) {
return rename(obj, newNames)...
// プレディケートを受け取りそれぞれのオブジェクトに対して適用する関数
function restrict(table, pred) {
return _.reduce(table, function(newTable, obj) {
if ...
SELECT title, isbn, edition FROM (
SELECT ed AS edition FROM library
) EDS
WHERE edition > 1;
/* SQL での例 */
// 上記の SQL と同等...
まとめ
• JavaScript+の関数型(スタイルで書ける)言語としての側面
• 第一級関数/手続き型から関数型スタイルへの書き換え
• JavaScript+の様々なプログラミングパラダイムについて
• 作用的プログラミングについて
• コ...
Upcoming SlideShare
Loading in …5
×

【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 2 章

7,423 views

Published on

JavaScript で学ぶ関数型プログラミングという本を読んでいる時の発表資料です

Published in: Internet
  • Be the first to comment

【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 2 章

  1. 1. 第一級関数と作用的プログラミング JavaScript*で学ぶ関数型プログラミング*2*章 YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 1
  2. 2. 内容 • 第一級要素としての関数 • JavaScript+のプログラミングパラダイムについて • 作用的プログラミング・コレクション中心プログラミング • データ思考プログラミング • テーブルのようなデータの取り扱い YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 2
  3. 3. 第一級要素としての関数 YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 3
  4. 4. 第一級関数 • JavaScript+の第一級要素+:+数値・文字列+...+関数 • つまり関数と数値や文字列は同じ性質を持つ • 変数に代入可能 • 任意のタイミングで生成可能 • 関数の引数・戻り値になる+3 3 "ここでは""関数型""を言語に対する形容詞として使っていない YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 4
  5. 5. // 変数・配列・オブジェクトのフィールドに代入可能 var f = function() { return 42 }, a = [42, function() { return 42 }], o = {n: 42, f: function() { return 42 }}; // 任意のタイミングで生成可能 42 + (function(){ return 42 })(); //=> 84 // 引数・返り値に取ることが可能 (恒等関数での例) function identity(x) { return x }; identity(function() { return 42 }); //=> function() YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 5
  6. 6. 閑話休題:"JavaScript"は関数型言語か?"1 • そもそも""関数型言語""の基準は? • 関数が第一級要素? • 参照透過性? • 副作用の隠 ? • 型推論? 1 "h$p://www.slideshare.net/ksknac/120901fp9key"(24"ページ) YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 6
  7. 7. そもそも関数型とは?!2 • 以下のようなプログラミング"パラダイム"に対する形容"3 • プログラムのほとんどが関数であるような書き方 • 非関数や副作用の影響を可能な限り小さくする • 使用する言語に関係なく実現が可能 3 "ここでは""関数型""を言語に対する形容詞として使っていない 2 "h$p://www.slideshare.net/ksknac/120901fp9key"(25"ページ) YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 7
  8. 8. 改めて:"JavaScript"は関数型言語か? • 関数型(パラダイムのプログラミングをしやすい)言語か? • JavaScript-では関数が第一級要素となっている • 関数型スタイルでのプログラミングはしやすい • その他の関数型の機能は言語としてサポートはされていない →書き手の配慮により関数型スタイルで書くことが可能な言語- 4 ""変数再代入をしない""とか""関数は必ず値を返し、副作用がないようにする""とか。 YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 8
  9. 9. JavaScript*の* *プログラミングパラダイム YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 9
  10. 10. ・!命令型プログラミング ・!関数型プログラミング ・!プロトタイプベースオブジェクト指向 ・!メタプログラミング 5 "よく見られるものを示した。ES6"からクラスベースオブジェクト指向も見られるようになるかも。 YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 10
  11. 11. 命令型プログラミング • 実行フローをほぼそのまま書き下す • プログラムの実行状態を直接操作・参照する • 対となるパラダイムは"宣言型プログラミング • 関数型や論理型は宣言型プログラミングに属する • C"言語・BASIC・PASCAL"等はこのパラダイムを取る事が多い YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 11
  12. 12. 例)"99"本のビール • 99#から開始し、1#ずつ引いていき繰り返す • 現在の数を#X#として、次のように歌う • X#本のビールが残ってる#=>#X#本のビール • ひとつ取って、隣に回せ#=>#X(1#本のビールが残ってる • X=1#の時は最後の部分で#もうビールは残ってない#と歌う。 YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 12
  13. 13. // 命令型プログラミング var lyrics = []; for (var bottles = 99; bottles > 0; bottles--) { lyrics.push(bottles + " 本のビールが残ってる"); lyrics.push(bottles + " 本のビール"); lyrics.push("ひとつ取って、隣に回せ"); if (bottles > 1) { lyrics.push((bottles - 1) + "本のビールが残ってる"); } else { lyrics.push("もうビールは残ってない"); } } // この後に 80∼50 本のビールの歌を歌わせたかったら...? // この部分のテストが書きたくなったら...? YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 13
  14. 14. // 関数型スタイルへのパラダイムシフト // "n 本目のビールの歌詞を生成する" 機能を関数へと分割 function lyricSegment(n) { return _.chain([]) // メソッドチェイン記法 .push(n + " 本のビールが残ってる") .push(n + " 本のビール") .push("ひとつ取って、隣に回せ") .tap(function(lyrics) { // 渡ってきたオブジェクトに関数を適用しオブジェクトを返す if (n > 1) lyrics.push((n - 1) + " 本のビールが残ってる"); else lyrics.push("もうビールは残ってない"); }) .value(); // 値を返す } YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 14
  15. 15. // 関数の繋ぎあわせ function song(start, end, lyricGen) { return _.reduce(_.range(start,end,-1), // 値を返す function(acc,n) { return acc.concat(lyricGen(n)); // 値を返す }, []); } song(99, 0, lyricSegment) // => ["99 本のビールが残ってる", "99 本のビール", "ひとつ取って、隣に回せ", ... ] // song(80, 50, lyricSegment) で 80∼50 本のビールの歌が歌える // 正しく値が返って来ているかのテストも書きやすそう // 歌詞生成部だけのテストも書けそう YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 15
  16. 16. 命令型と関数型 • 前述の例のように、ルールを守って処理を細かく関数に分ける • 命令型では"99"本・同じ歌詞でしか動かせなかった • 関数型では関数として定義することにより抽象的に • 99"本のビール,"30"本のビール..."は"song"関数ですぐ作れる 6 "英語で歌わせたければ"enLyricSegment"のような関数を用意し"song(99,"0,"enlyricsegment)"のようにすればよい YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 16
  17. 17. 何が良いと感じたか • 機能別に分けることにより • 変更に強いプログラムが作れる • 再利用性が高く同じようなコードを何度も書かなくて良い • 機能毎のテストが書きやすくなる • 参照等価にするとここが∼みたいな話は色々あるけど...#7 7 "とりあえずここまでの例で読み取れることのみの感想です YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 17
  18. 18. プロトタイプベースオブジェクト指向 • JavaScript+のオブジェクト指向はプロトタイプベース • 既存のオブジェクトをプロトタイプとして使用する+8 • クラス指向のオブジェクト指向に対してローレベルで動作する • 柔軟性はあるが罠も多い 8 "以降「関数」は単独で存在する関数、「メソッド」はオブジェクトのコンテキストに生成された関数を指す YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 18
  19. 19. 罠 // オブジェクトのフィールド値に関数が存在できる var a = {name: "a", f: function(){return this}}; // this は a 自身を指す。計画通り。 a.f() // => {name: "a", f: ...} var bObj = {name: "b", f: function(){return this}}, var bFun = bObj.f; // !! this は bObj を指さない !! bFun から見た this を指す !! bFun() // => グローバルオブジェクト (window など) 8 "以降「関数」は単独で存在する関数、「メソッド」はオブジェクトのコンテキストに生成された関数を指す YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 19
  20. 20. メタプログラミング • ソースコードを生成するようなプログラムを書くスタイル • 本書では、以下のような例が紹介されている"9 function Point2D(x, y) { this._x = x; this._y = y; } function Point3D(x, y, z) { Point2D.call(this, x, y); this._z = z; } new Point3D(10, -1, 100); //=> {_x: 10, _y: -1, _z: 100} • 本書ではほぼ取り扱わないので詳細は省略する 9 "この例があまりメタプログラミングっぽく見えないのは"JavaScript"のデータ構造とプログラム構造が近いから? YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 20
  21. 21. 作用的プログラミング! !コレクション中心プログラミング YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 21
  22. 22. 作用的プログラミング • 関数"f: A()"と"f: B()"を次のように使用するプログラミング • B"を呼び出すことで"A"を実行させる"10 • B(element, A()) • "作用的"という言葉はコンテキストによって異なる意味を持つ • もう使わないけど意味だけは知っておいてね 10 #と、本書では定義してあるのでこのスライドでもそう定義します。 YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 22
  23. 23. 作用的プログラミングの例 var nums = [1,2,3,4,5]; function doubleAll(array) { return _.map(array, function(n) { return n*2 }); } doubleAll(nums); //=> [2, 4, 6, 8, 10] // --------------------- function average(array) { var sum = _.reduce(array, function(a, b) { return a+b }); return sum / _.size(array); } average(nums); //=> 3 // --------------------- function onlyEven(array) { return _.filter(array, function(n) { return (n%2) === 0; }); } onlyEven(nums); //=> [2, 4] YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 23
  24. 24. コレクション中心プログラミング • 関数型プログラミング • コレクションに入った多数の値に同じ処理をしやすい • [1, 2, 3, 4, 5]"や"{a: 1, b: 2}"... • 本書はこの考え方に則りデータ構造を有効活用する方法を推進 • It#is#be(er#to#have#100#func4ons#operate#on#one#data#structure#than# 10#func4ons#on#10#data#structures.#88#Alan#Perlis YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 24
  25. 25. コレクション中心プログラミングの例 _.map({a: 1, b: 2}, _.identity); //=> [1,2] // --------------------- _.map({a: 1, b: 2}, function(v,k) { return [k,v]; }); //=> [['a', 1], ['b', 2]] // --------------------- _.map({a: 1, b: 2}, function(v,k,coll) { return [k, v, _.keys(coll)]; }); //=> [['a', 1, ['a', 'b']], ['b', 2, ['a', 'b']]] YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 25
  26. 26. 作用的プログラミングのその他の例 // ReduceRight var nums = [100,2,25]; function div(x,y) { return x/y }; _.reduce(nums, div); //=> 2 _.reduceRight(nums, div); //=> 0.125 // allOf, anyOf; reduce でも同じように記述できる function allOf(/* funs */) { return _.reduceRight(arguments, function(truth, f) { return truth && f(); }, true); } function anyOf(/* funs */) { return _.reduceRight(arguments, function(truth, f) { return truth || f(); }, false); } YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 26
  27. 27. // ReduceRight // allOf, anyOf の使用例 function T() { return true } function F() { return false } allOf(); //=> true allOf(T, T); //=> true allOf(T, T, T , T , F); //=> false anyOf(T, T, F); //=> true anyOf(F, F, F, F); //=> false anyOf(); //=> false • reduce/reduceRight-は演算が結合的でない時に差が発生 • 遅延評価の言語ではもっと大きな違いがある YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 27
  28. 28. // find, reject, filter, all, any // find: コレクションとプレディケートを取り真となる要素を返す _.find(['a', 'b', 3, 'd'], _.isNumber); //=> 3 // find の逆の動作; underscore.js は _.isNumber のようなプレディケート関数を多数用意している _.reject(['a', 'b', 3, 'd'], _.isNumber); //=> ['a', 'b', 'd'] // 真偽を判定させる関数 complement function complement(pred) { return function() { return !pred.apply(null, _.toArray(arguments)); }; } // _.reject と同じ動作 _.filter(['a', 'b', 3, 'd'], complement(_.isNumber)); //=> ['a', 'b', 'd'] // all: 要素のすべてが true の時 true; any: 要素の1つでも true なら true _.all([1, 2, 3, 4], _.isNumber); //=> true _.any([1, 2, 'c', 4], _.isString); //=> true YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 28
  29. 29. // sortBy, countBy, groupBy var people = [{name: "Rick", age: 30}, {name: "Jaka", age: 24}]; // コレクションと関数を受け取り関数の基準に応じて並べ替えたコレクションを返す _.sortBy(people, function(p) { return p.age }); //=> [{name: "Jaka", age: 24}, {name: "Rick", age: 30}] var albums = [{title: "Sabbath Bloody Sabbath", genre: "Metal"}, {title: "Scientist", genre: "Dub"}, {title: "Undertow", genre: "Metal"}]; // 受け取った関数が返す値をキーとして、その値が等しいオブジェクトを配列に入れて返す _.groupBy(albums, function(a) { return a.genre }); //=> {Metal:[{title:"Sabbath Bloody Sabbath", genre:"Metal"}, // {title:"Undertow", genre:"Metal"}], // Dub: [{title:"Scientist", genre:"Dub"}]} // _.groupBy と同じ動作をするが、キーに対応する値が配列の長さになっている _.countBy(albums, function(a) { return a.genre }); //=> {Metal: 2, Dub: 1} YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 29
  30. 30. 作用的関数の定義 // 作用的ではない関数達 // 幾つかの配列を結合する関数 function cat() { var head = _.first(arguments); if (existy(head)) return head.concat.apply(head, _.rest(arguments)); else return []; } cat([1,2,3], [4,5], [6,7,8]); //=> [1, 2, 3, 4, 5, 6, 7, 8] // 要素と配列を引数に取り配列の前に要素を挿入する関数 // これも作用的な関数ではない function construct(head, tail) { return cat([head], _.toArray(tail)); } construct(42, [1,2,3]); //=> [42, 1, 2, 3] YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 30
  31. 31. // 作用的な関数 mapcat // _.map を使用し fun を受け取り渡されたコレクションの要素全てに対して fun を実行する // その後 _.map から返された全ての要素を結合する function mapcat(fun, coll) { return cat.apply(null, _.map(coll, fun)); } mapcat(function(e) { return construct(e, [","]); }, [1,2,3]); // => [1, ",", 2, ",", 3, ","] // construct は要素を配列の先頭に挿入する関数 // 内部的には: cat.apply(null, [[1, ","], [2, ","], [3, ","]]) YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 31
  32. 32. function butLast(coll) { return _.toArray(coll).slice(0, -1); } function interpose (inter, coll) { return butLast(mapcat(function(e) { return construct(e, [inter]); }, coll)); } interpose(",", [1,2,3]); //=> [1, ",", 2, ",", 3] // ([1, 2, 3] => [1, ",", 2, ",", 3, ","] => [1, "," 2, ",", 3]) YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 32
  33. 33. 作用的関数 • 低レベルの関数によって組み立てられた個別の機能を組合わせ • 複数の関数が作用的に呼び出される • それぞれの関数がデータを処理し最後の解に至る • 関数型プログラミングの重要な切り口 • 今後本書ではこのようなパターンが多数登場 YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 33
  34. 34. データ思考 • JavaScript+におけるオブジェクト型は非常に強力 • ポリモーフィックディスパッチ/単純な連想データストア • データストアとして扱う関数は+JavaScript+自体にはほぼ無い • Underscore+が有用なツールを提供し、オブジェクト等に特化 しない一意な定義が可能+11 11 "「情報の断片ごとに定義された、マイクロ言語のようなもの」(Hickey"2011)"から逃れられる YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 34
  35. 35. // keys/values は key/value を返す(当然) var zombie = {name: "Bub", film: "Day of the Dead"}; _.keys(zombie); //=> ["name", "film"] _.values(zombie); //=> ["Bub", "Day of the Dead"] // pluck は 指定したキーの要素を全て返す(無い時は undefined) _.pluck([{title: "Chthon", author: "Anthony"}, {title: "Grendel", author: "Gardner"}, {title: "After Dark"}], 'author'); //=> ["Anthony", "Gardner", undefined] // pairs は {a: b, c: d} を [[a, b], [c, d]] とする _.pairs(zombie); //=> [["name", "Bub"], ["film", "Day of the Dead"]] YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 35
  36. 36. // シーケンシャルな処理を行い、_.object でオブジェクトを再構成する例 _.object(_.map(_.pairs(zombie), function(pair) { return [pair[0].toUpperCase(), pair[1]]; })); //=> {FILM: "Day of the Dead", NAME: "Bub"}; // _.invert はキーと値を入れ替える _.invert(zombie); //=> {"Bub": "name", "Day of the Dead": "film"} // JavaScript のキーは必ず文字列になる _.keys(_.invert({a: 138, b: 9})); //=> ['9', '138'] YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 36
  37. 37. // _.defaults は指定したキーが存在しない場合デフォルト値として追加する _.pluck(_.map([{title: "Chthon", author: "Anthony"}, {title: "Grendel", author: "Gardner"}, {title: "After Dark"}], function(obj) { return _.defaults(obj, {author: "Unknown"}) }), 'author'); //=> ["Anthony", "Gardner", "Unknown"] // _.pick, _.omit は引数によりオブジェクトの内容をフィルタする (非破壊的操作) var person = {name: "Romy", token: "j3983ij", password: "tigress"}; var info = _.omit(person, 'token', 'password'); info; //=> {name: "Romy"} var creds = _.pick(person, 'token', 'password'); //=> {password: "tigress", token: "j3983ij"}; YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 37
  38. 38. // findWhere, where は 2 番目の引数の条件にマッチするオブジェクトを // 1 番目の引数から探してくる var library = [{title: "SICP", isbn: "0262010771", ed: 1}, {title: "SICP", isbn: "0262510871", ed: 2}, {title: "Joy of Clojure", isbn: "1935182641", ed: 1}]; // findWhere は最初にマッチしたものだけが返る _.findWhere(library, {title: "SICP", ed: 2}); //=> {title: "SICP", isbn: "0262510871", ed: 2} // where はマッチした全ての要素が配列で返る _.where(library, {title: "SICP"}); //=> [{title: "SICP", isbn: "0262010771", ed: 1}, // {title: "SICP", isbn: "0262510871", ed: 2}] YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 38
  39. 39. テーブルのようなデータ • 前項で示した"library"のようなデータの取り扱い • JavaScript"のオブジェクトの配列をデータテーブルとして見る • 行が"JavaScript"オブジェクト/セルがオブジェクトのキー値ペア YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 39
  40. 40. テーブルのようなデータ!2 • タイトルを抜き出したテーブル SELECT title FROM library; _.pluck(library, 'title'); // ここまでで習った JavaScript での同等の表現 // 但し、オブジェクトから配列に変換されており、型が壊れている ... YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 40
  41. 41. // _.pick を使用しテーブルデータの抽象型を維持する function project(table, keys) { return _.map(table, function(obj) { return _.pick.apply(null, construct(obj, keys)); }); }; var editionResults = project(library, ['title', 'isbn']); // => [{isbn: "0262010771", title: "SICP"}, // {isbn: "0262510871", title: "SICP"}, // {isbn: "1935182641", title: "Joy of Clojure"}]; // (_.pick.apply(null, [{...}, 'title', 'isbn']) が要素ごとに実行される) // データ構造を維持したまま値を返すため、更に project を適用可能 var isbnResults = project(editionResults, ['isbn']); //=> [{isbn: "0262010771"}, {isbn: "0262510871"}, {isbn: "1935182641"}] _.pluck(isbnResults, 'isbn'); // 欲しいデータだけを取得する //=> ["0262010771", "0262510871", "1935182641"] YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 41
  42. 42. データの抽出 • 上記で見たようなデータ構造を破壊して必要な値を得る操作 • あるモジュールから別のモジュールへデータを「引渡す」操作 • 関数型スタイルではデータの内容/変換/フォーマットを考える YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 42
  43. 43. 抽象テーブル型 • SQL%では%AS%句を使ってカラムに別名を与えることが可能 SELECT ed AS edition FROM library; /* 下のテーブルのような出力 */ • 次に、JavaScript+で+as+や+where+を実装してみる YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 43
  44. 44. // マップデータを指定した通りにリネームする関数 function rename(obj, newNames) { return _.reduce(newNames, function(o, nu, old) { if (_.has(obj, old)) { o[nu] = obj[old]; return o; } else return o; }, _.omit.apply(null, construct(obj, _.keys(newNames)))); }; rename({a: 1, b: 2}, {'a': 'AAA'}); //=> {AAA: 1, b: 2} // オブジェクトの再構成に _.reduce を使用している // これによってマップデータらしさを維持しつつキーの名前を変換している YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 44
  45. 45. // rename 関数を使用し実装した as 関数 function as(table, newNames) { return _.map(table, function(obj) { return rename(obj, newNames); }); }; as(library, {ed: 'edition'}); //=> [{title: "SICP", isbn: "0262010771", edition: 1}, // {title: "SICP", isbn: "0262510871", edition: 2}, // {title: "Joy of Clojure", isbn: "1935182641", edition: 1}] // as はテーブルデータ型を返すので project, as を同時に適用可能 project(as(library, {ed: 'edition'}), ['edition']); //=> [{edition: 1}, {edition: 2}, {edition: 1}]; YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 45
  46. 46. // プレディケートを受け取りそれぞれのオブジェクトに対して適用する関数 function restrict(table, pred) { return _.reduce(table, function(newTable, obj) { if (truthy(pred(obj))) return newTable; else return _.without(newTable, obj); }, table); }; restrict(library, function(book) { return book.ed > 1; }); //=> [{title: "SICP", isbn: "0262510871", ed: 2}] YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 46
  47. 47. SELECT title, isbn, edition FROM ( SELECT ed AS edition FROM library ) EDS WHERE edition > 1; /* SQL での例 */ // 上記の SQL と同等な JavaScript を使った例 restrict( project( as(library, {ed: 'edition'}), ['title', 'isbn', 'edition']), function(book) { return book.edition > 1; }); //=> [{title: "SICP", isbn: "0262510871", edition: 2},] // restrict, as, project は同じテーブル抽象型(オブジェクトの配列)に対して動作する // これがデータ思考プログラミング YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 47
  48. 48. まとめ • JavaScript+の関数型(スタイルで書ける)言語としての側面 • 第一級関数/手続き型から関数型スタイルへの書き換え • JavaScript+の様々なプログラミングパラダイムについて • 作用的プログラミングについて • コレクション中心プログラミング/テーブル様データの取扱い YOSHIKAWA)Ryota)(2015/1/22|2015/2/11))第一回/第二回)Topotal)輪読会 48

×