【JavaScript】パイプライン処理を考える

公開日:2019-04-18
最終更新:2019-06-01

旧題: h(g(f(value)))がキモいのでなんとかしようとした

はじめに

const result = h(g(f(value))); // キモい  
  1. カッコ
    • カッコイイとでも思ってるの?
  2. 順番
    • 処理の順番に対し、読み書きする順番が逆

なんとかしてみる

まず思いついたのが以下のようにする方法。

const result = pipeline(value, f, g, h);  

再帰する

で思いついたのが再帰関数を使う方法。

const pipeline = (v, ...fs) => {  
    // 関数がなかった場合は値をそのまま戻す  
    if (fs.length < 1) return v;  

    const currentF = fs.shift();  
    const rv = currentF(v);  
    // 再帰  
    if (rv === undefined || rv === null) return pipeline(v, ...fs); // 戻り値がない場合は関数を実行するだけ  
    return pipeline(rv, ...fs);  
};  

reduceする

しかし、思いついてしまった。
別にわざわざ再帰しなくとも、reduce()使えばいいじゃんこれ。
そして、下になる。

const pipeline = (v, ...fs) => fs.reduce((acc, cur) => {  
    const rv = cur(acc);  
    if (rv === undefined || rv === null) return acc; // 戻り値がない場合は関数を実行するだけ  
    return rv;  
}, v);  

ついでにカリー化

カリー化とは…

カリー化 (currying, カリー化された=curried) とは、複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること(あるいはその関数のこと)である。
カリー化 - Wikipedia

まぁ簡単に言うと…

const func = a => b => a + b;  
const hoge = func(1); // b => 1 + b  
hoge(2) // 3  
hoge(4) // 5  

みたいな感じ。

const pipeline = v => (...fs) => fs.reduce((acc, cur) => {  
    const rv = cur(acc);  
    if (rv === undefined || rv === null) return acc; // 戻り値がない場合は関数を実行するだけ  
    return rv;  
}, v);  
const result = pipeline(value)(f, g, h);  

全部カリー化できるんじゃ?

そして思ってしまった。
もういっそのこと全部カリー化ですませられるんじゃ…?

const pipeline = v => f => {  
    const rv = f(v);  
    if (rv === undefined || rv === null) return pipeline(v); // 戻り値がない場合は関数を実行するだけ  
    return pipeline(rv);  
};  
const result = pipeline(value)(f)(g)(h);  
console.log(result); // function pipeline()  

だめだった。

そして、よく考えたら「戻り値がない場合は関数を実行するだけ」って変。
あえてundefinednullを戻したいときとかどうするの? って話。

もういっそのこと、class作る

const Pipeline = class {  
    constructor(v) {  
        this._v = v;  
    }  

    get value() {  
        return this._v;  
    }  

    exec(f, overwrite) {  
        if (overwrite === undefined || overwrite === null) overwrite = true;  

        const rv = f(this._v);  
        return overwrite ? new this.constructor(rv) : this;  
    }  
};  
const result = new Pipeline(value)  
    .exec(f)  
    .exec(g)  
    .exec(value => console.log(value), false) // 上書きしない  
    .exec(h) // ので値はそのまま  
    .value  
    ;  

おわりに

まぁパイプライン演算子が使えるようになったら必要なくなるんですけどね。

まだかES Next!!!

記事が少しでもいいなと思ったらクラップを送ってみよう!
16
+1
@okayuの大して技術的ではないブログ

よく一緒に読まれている記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる

技術ブログをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

技術ブログを開設する

Qrunchでアウトプットをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

Markdownで書ける

ログ機能でアウトプットを加速

デザインのカスタマイズが可能

技術ブログ開設

ここから先はアカウント(ブログ)開設が必要です

英数字4文字以上
.qrunch.io
英数字6文字以上
ログインする