在函数式编程链的中间获取中间值

时间:2017-10-23 21:20:21

标签: javascript functional-programming

我想知道是否有一种简洁或特定的方式来访问JavaScript中FP链中间的值。示例:

const somestuff = [true, true, false];
let filteredCount = 0;
somestuff.filter((val) => val)
  .forEach((val) => console.log(val));

上面,我想将filteredCount设置为filter函数返回的数组的长度。最直接的方式是:

const somestuff = [true, true, false];
const filteredStuff = somestuff.filter((val) => val);
let filteredCount = filteredStuff.length;
filteredStuff.forEach((val) => console.log(val));

这当然有效,但它打破了我们的FP链并引入了一个额外的保持变量。我想知道是否存在访问链中间值的约定。像.once()这样运行一次并隐式返回传入的值,但不存在类似的东西。

3 个答案:

答案 0 :(得分:1)

没有像这样的Array.prototype方法的原因是它有副作用。这是函数式编程中特别避免的。 但是,如果您不关心编写“纯函数”,甚至是函数范例,您可以将副作用放在回调函数中,或者在Array原型中编写函数。
即。

Array.prototype.once = function(callback) {
    callback(this)
    return this
}

您还有其他hacky选项,如其他答案

答案 1 :(得分:1)

为了进行调试,我经常使用名为tap的函数临时将一个副作用(如console.log添加到函数中:

const tap = f => x => (f(x), x);

此函数返回传递的任何内容,但不会在使用该值调用另一个函数之前返回。例如:



const tap = f => x => (f(x), x);
const tapLog = tap(console.log);

const x = tapLog(10);

console.log("x is", x);




您的代码段基本上是这样做的:

  • 过滤列表
  • (记录清单)
  • 从数组中检索length属性

如果使用pipecompose构建此功能,则可以"注入"中间的console.log不会中断数据流:

const countTrues = pipe(
  filter(isTrue),
  prop("length")
);

const countTruesWithLog = pipe(
  filter(isTrue),
  tap(console.log),
  prop("length")
);

在一个片段中:



// Utils
const isTrue = x => x === true;
const prop = k => obj => obj[k];
const tap = f => x => (f(x), x);
const filter = f => xs => xs.filter(f);
const pipe = (...fns) => x => fns.reduce((res, f) => f(res), x);

// Logic:
// Filter an array using the isTrue function
// and return the length of the result
const countTrues = pipe(
  filter(isTrue),
  prop("length")
);

// Create a filter with a console.log side-effect
// and return the length of the result
const countTruesWithLog = pipe(
  filter(isTrue),
  tap(console.log),
  prop("length")
);

// App:
const somestuff = [true, true, false];

console.log("pure:");
const countA = countTrues(somestuff)
console.log(countA);

console.log("with log:")
const countB = countTruesWithLog(somestuff);
console.log(countB);




答案 2 :(得分:0)

默认情况下,我认为没有类似的东西。你可以做的是扩展Array,但我并不是真的喜欢扩展框架类(例如与其他once实现的冲突)。在这种情况下,你最终得到:

Array.prototype.once = function once(func) {
    func(this);
    return this;
}

被称为:

var filteredStuff = somestuff
    .filter((val) => val)
    .once(function(array) {
        console.log(array.length);
    })
    .forEach((val) => console.log(val));

另一方面,您可以尝试使用默认功能。其中一个可以一次访问所有项目的功能是reduce。定义一个函数once,它将调用它的第一个参数一次(:)),你最终得到的结果如下:

function once(func) {
    return function(accumulator, currentValue, currentIndex, array) {
        if(currentIndex === 1) {
            func(array);
        }

        return array;
    }
}

你可以这样打电话:

var filteredStuff = somestuff
    .filter((val) => val)
    .reduce(once(function(array) {
        console.log(array.length);
    }), [0])
    .forEach((val) => console.log(val));

注意丑陋的[0]以确保once至少调用一次传递的函数(包括空数组)。

这两种解决方案都不是很整洁,但鉴于标准,这是我能想到的最好的解决方案。