今まで意識せずに使ってたけど、ジムでランニングしながら見てた funfunfunction で面白かったのでメモ。
高階関数 higher-order function
関数を引数に取る関数のことを、高階関数 (higer-order function) というらしい。
Javascript だと callback 関数とかで割と見かけるやつの事だった。
高階関数のメリット
- ロジックを分離して再利用可能にする
- ロジックが分離されるので、処理部分のコードが短く見やすくなる
例えば、次のリストから type
が "cool"
のアイドルだけを取り出したいような場合
const idols = [ { name: '星宮 いちご', type: 'cute' }, { name: '霧矢 あおい', type: 'cool' }, { name: '紫吹 蘭', type: 'sexy' }, { name: '有栖川 おとめ', type: 'pop' }, { name: '藤堂 ユリカ', type: 'cool' }, { name: '神谷 しおん', type: 'cool' }, { name: '一ノ瀬 かえで', type: 'pop' }, { name: '三ノ輪 ヒカリ', type: 'sexy' }, ... ];
通常の for文 の場合、要件は満たせるがロジックが分離されてないのでここ限りで再利用することは難しい
const coolIdols = []; for (var i = 0, l = idols.length; i < l; i += 1) { if ( idols[i].type === 'cool' ) { coolIdols.push(idols[i]); } } console.log(coolIdols); // => [{ name: '霧矢 あおい', type: 'cool' }, { name: '藤堂 ユリカ', type: 'cool' }, ...]
高階関数である Array.filter
を使うと次のような感じで書ける。
const coolIdols = idols.filter((idol) => { return idol.type === 'cool'; }); console.log(coolIdols); // => [{ name: '霧矢 あおい', type: 'cool' }, { name: '藤堂 ユリカ', type: 'cool' }, ...]
filter に渡している関数は idol.type === 'cool'
を判定するだけなので、配列をループさせる処理とは切り離されている。
判定しているロジックを別の関数に切り出すと、判定するだけの関数として再利用が可能になる。
// filterの引数に渡す関数を分離する const isCoolIdol = function(idol) { return idol.type === 'cool'; }; const coolIdols = idols.filter( isCoolIdol ); // => [{ name: '霧矢 あおい', type: 'cool' }, { name: '藤堂 ユリカ', type: 'cool' }, ...]
例えば配列から条件にマッチしている要素を除くfilterを作成しすると…
Array-reject.js
Array.prototype.reject = function(func) { const res = []; if ( typeof(func) !== 'function' ) { return this; } this.forEach(function(v) { if ( !func(v) ) { res.push(v); } }); return res; }
👇 同じ判定ロジックで、別の条件のデータを作成できたりする
require('./Array-reject'); // cool type かどうかを判定するロジック const isCoolIdol = function(idol) { return idol.type === 'cool'; }; // type "cool" の idols リスト const coolIdols = idols.filter( isCoolIdol ); // => [{ name: '霧矢 あおい', type: 'cool' }, { name: '藤堂 ユリカ', type: 'cool' }, ...] // type "cool" 以外の idol リスト const notCoolIdols = idols.reject( isCoolIdol ); // => [{ name: '星宮 いちご', type: 'cute' }, { name: '紫吹 蘭', type: 'sexy' },...]
ロジックを使い回すことができ、処理をする部分のコードも短くなって何をしているかの見通しが良くなる!
ってのが高階関数のメリットってことみたい。
カリー化 currying
名称は知ってたけどどういうものか知らず、なんで?🍛って思ってたやつ。
複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること(あるいはその関数のこと)である。
cf. https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%BC%E5%8C%96
という事らしい。
雑に言えば、「引数を取って、その引数を使った関数を返す関数」ということっぽい
e.g.
function over(standard_value) { return function(x) { return x > standard_value; } }
パラメーターに応じて判定の変わる関数を返す関数
const over5 = over(5); console.log( over5(10) ); // => true console.log( over(5)(10) ); // => true
返される関数の内部に最初に渡された引数を持っているので、動的に関数を作り出せる (= カリー化)
console.log( over(2)(5) ); // => true const over10 = over(10); console.log( over10(5) ); // => false console.log( over10(11) ); // => true
ということっぽい。
高階関数とカリー化を組み合わせる
高階関数に渡す関数をカリー化すると更に便利になる
e.g.
function over(standard_value) { console.log('over init!'); return function(x) { return x > standard_value; } } const over5Arr = [1,2,3,4,5,6,7,8,9,10].filter( over(5) ); // => over init! console.log( over5Arr ); // => [ 6, 7, 8, 9, 10 ]
over()
は1度だけ呼び出されて、その後はover(5)
で返された関数が実行される。( filter のループ内で都度 over()
が呼ばれる訳ではない)
高階関数でロジックを分離しているので、条件を変えた処理を短いコードで表現できるようになる!!
require('./Array-reject'); // data const idols = [ { name: '星宮 いちご', type: 'cute' }, { name: '霧矢 あおい', type: 'cool' }, { name: '紫吹 蘭', type: 'sexy' }, { name: '有栖川 おとめ', type: 'pop' }, { name: '藤堂 ユリカ', type: 'cool' }, { name: '神谷 しおん', type: 'cool' }, { name: '一ノ瀬 かえで', type: 'pop' }, { name: '三ノ輪 ヒカリ', type: 'sexy' }, { name: '神崎 美月', type: 'sexy' }, { name: '夏樹 みくる', type: 'pop' }, { name: '北大路 さくら', type: 'cute' }, { name: '大空 あかり', type: 'cute' }, { name: '服部 ユウ', type: 'cool' }, { name: '氷上 スミレ', type: 'cool' }, { name: '新条 ひなき', type: 'pop' }, { name: '紅林 珠璃', type: 'sexy' }, { name: '黒沢 凛', type: 'cool' }, { name: '天羽 まどか', type: 'cute' }, ]; // currying function idolType(type) { return function(idol) { return idol.type === type }; } // type "cool" のリスト const coolIdols = idols.filter( idolType('cool') ); // type "cool" 以外のリスト const notCoolIdols = idols.reject( idolType('cool') ); // type "cute" のリスト const cuteIdols = idols.filter( idolType('cute') ); console.log( coolIdols ); /* [ { name: '霧矢 あおい', type: 'cool' }, { name: '藤堂 ユリカ', type: 'cool' }, { name: '神谷 しおん', type: 'cool' }, { name: '服部 ユウ', type: 'cool' }, { name: '氷上 スミレ', type: 'cool' }, { name: '黒沢 凛', type: 'cool' } ] */ console.log( notCoolIdols ); /* [ { name: '星宮 いちご', type: 'cute' }, { name: '紫吹 蘭', type: 'sexy' }, { name: '有栖川 おとめ', type: 'pop' }, { name: '一ノ瀬 かえで', type: 'pop' }, { name: '三ノ輪 ヒカリ', type: 'sexy' }, { name: '神崎 美月', type: 'sexy' }, { name: '夏樹 みくる', type: 'pop' }, { name: '北大路 さくら', type: 'cute' }, { name: '大空 あかり', type: 'cute' }, { name: '新条 ひなき', type: 'pop' }, { name: '紅林 珠璃', type: 'sexy' }, { name: '天羽 まどか', type: 'cute' } ] */ console.log( cuteIdols ); /* [ { name: '星宮 いちご', type: 'cute' }, { name: '北大路 さくら', type: 'cute' }, { name: '大空 あかり', type: 'cute' }, { name: '天羽 まどか', type: 'cute' } ] */
文章みたいな構造でリスト操作ができるようになりました!
A W E S O M E !
完 全
理 解
![[Amazonブランド]Happy Belly ビーフカレー 中辛 180g×15個 [Amazonブランド]Happy Belly ビーフカレー 中辛 180g×15個](https://images-fe.ssl-images-amazon.com/images/I/51bsKv-ZU4L._SL160_.jpg)
[Amazonブランド]Happy Belly ビーフカレー 中辛 180g×15個
- 出版社/メーカー: ヤマモリ
- メディア: 食品&飲料
- この商品を含むブログを見る

【Amazon.co.jp限定】TVアニメ/データカードダス『アイカツオンパレード!』OP/EDテーマ 「君のEntrance/アイドル活動!オンパレードver.」 (デカジャケット付)
- アーティスト: V.A.
- 出版社/メーカー: ランティス
- 発売日: 2019/10/23
- メディア: CD
- この商品を含むブログを見る