函数组合中的条件运算

时间:2016-08-30 10:39:05

标签: javascript function functional-programming function-composition

如何根据某些逻辑条件停止或分支合成?

例如。假设我有以下代码:

compose(
  operation4
  operation3,
  operation2,
  operation1
)(myStuff);

甚至像

这样的东西
myStuff
 .map(operation1)
 .map(operation2)
 .map(operation3)
 .map(operation4)

如果myStuff满足某些条件,我只希望执行操作3和4。

如何实现(特别是在JavaScript中)?

我是否必须创建两个较小的合成并具有单独的if语句,或者是否有办法在合成中包含条件?

Monads可以解决我的问题吗?如果是这样,怎么样?

4 个答案:

答案 0 :(得分:4)

一种简单但实用的方法是使when(cond, f)这样的函数只有在f返回true时才执行cond(x)(它又可以是一个合成):

_do = (...fns) => x => fns.reduce((r, f) => f(r), x);
_when = (cond, f) => x => cond(x) ? f(x) : x;


// example

add3 = x => x + 3;
add5 = x => x + 5;
add9 = x => x + 9;


pipe = _do(
    add3,
    add5,
    _when(x => x > 20, _do(add9))
)


console.log(pipe(30))
console.log(pipe(1))

答案 1 :(得分:2)

过早退出作文

这至少是可能的,但我认为这不是一个好主意。函数组合意味着表现得像单个函数。这是一个原子操作。因此,开箱即用的过早退出。

让我们退后一步。 n函数的组合是经典的缩减:



// generic functions

const foldr = f => acc => xs =>
 xs.reduceRight((acc, x, i) => f(x) (acc, i), acc);

const comp = f => g => x => f(g(x));

const I = x => x;

const inc = x => x + 1;

// derived function

const compn = foldr(comp) (I);


// run

console.log(
  compn([inc, inc, inc, inc]) (0) // 4
);




compn产生类似的计算:



const I = x => x;

const inc = x => x + 1;

const computation = x => inc(inc(inc(inc(I(x)))));

console.log(
  computation(0) // 4
);




如果我们想要提前退出作文,我们必须同时适应迭代算法(foldr)和作文(compn):



// generic functions

const foldrk = f => acc => xs => {
  const next = (i, acc) => i < 0
   ? acc
   : f(xs[i], i) (acc) (acc => next(i - 1, acc));

  return next(xs.length - 1, acc);
};

const I = x => x;

const comp = f => g => x => f(g(x));

const inc = x => x + 1;

const lt = y => x => x < y;


// derived function

compWhile = pred => foldrk(f => acc => k => k(
  x => pred(x) 
   ? comp(acc) (f) (x) 
   : x
)) (I);


// run

console.log(
  compWhile(lt(2)) ([inc, inc, inc, inc]) (0) // 2
);
&#13;
&#13;
&#13;

只要当前返回值低于2,组合就会继续。代码很难推理。我甚至不试图解释它(这不值得努力)。无论如何,compWhile产生以下计算:

&#13;
&#13;
const comp = f => g => x => f(g(x));

const I = x => x;

const inc = x => x + 1;

const lt = y => x => x < y;

const lt2 = lt(2);

const computation = x => lt2(x) 
 ? comp(x => lt2(x)
  ? comp(x => lt2(x)
   ? comp(x => lt2(x)
    ? comp(I) (inc) (x)
    : x) (inc) (x)
   : x) (inc) (x)
  : x) (inc) (x)
 : x;


console.log(
  computation(0) // 2
);
&#13;
&#13;
&#13;

分支组合物

将组合物分成较小的组合物,这些组合物代表所需的分支。如果你试图编写条件函数,你将不可避免地得到不可读的代码。

如果您只有一个简单的条件并且不想分割您的合成,那么至少要使分支尽可能明确,例如使用条件运算符:?

&#13;
&#13;
const foldr = f => acc => xs =>
 xs.reduceRight((acc, x, i) => f(x) (acc, i), acc);

const comp = f => g => x => f(g(x));

const I = x => x;

const inc = x => x + 1;

const compn = foldr(comp) (I);


console.log(
  compn([x => x >= 2 ? x : inc(x), inc, inc]) (0) // 2
);
&#13;
&#13;
&#13;

结论

函数组合是一种原子操作。不要试图过早退出或从作文中分支。

答案 2 :(得分:0)

在每个步骤后分析您的 someObject 。条件回调示例,但您可以通过不同方式实现此检查。

function compose(...fns) {
    return function (result, conditionCallback) {
        for (var i = fns.length - 1; i > -1; i--) {
            result = fns[i].call(this, result);

            // Run here condition check if provided.
            if (conditionCallback && conditionCallback(result) === false) {
                return result;
            }
        }
        return result;
    };
};

compose(...functionList)(someObject, function(obj) {
    return obj.prop !== someValue;
});
PS:我希望理解正确,你的意思是&#34;组成&#34;。

PPS:示例使用ES6功能,但将其降级为ES5非常简单。

答案 3 :(得分:0)

我可能在某种程度上误解了这个问题,但这听起来像是你可以用数组或流做的事情,例如,使用反应流或任何其他类似的库。

var myStuff = "one";
[myStuff]
  .filter(text => text.length <= 3)
  .map(text => text.split('').reverse().join(''))
  .map(text => text.toUpperCase())
  .forEach(text => console.log(text)); //prints ENO

或使用像RxJS这样的流库。

Rx.Observable.of("one","two","three")
            .filter(text => text.length <= 3)
            .map(text => text.split('').reverse().join(''))
            .map(text => text.toUpperCase())
            .forEach(text => console.log(text)); //prints ENO, OWT

除非满足过滤条件,否则上述两个示例数据都不会流向反转和大写操作,这似乎就是您所要求的。

虽然这很明显,但我觉得你的问题可能与其他事情有关。在任何一种情况下,我都希望这有助于讨论。