如何根据某些逻辑条件停止或分支合成?
例如。假设我有以下代码:
compose(
operation4
operation3,
operation2,
operation1
)(myStuff);
甚至像
这样的东西myStuff
.map(operation1)
.map(operation2)
.map(operation3)
.map(operation4)
如果myStuff满足某些条件,我只希望执行操作3和4。
如何实现(特别是在JavaScript中)?
我是否必须创建两个较小的合成并具有单独的if语句,或者是否有办法在合成中包含条件?
Monads可以解决我的问题吗?如果是这样,怎么样?
答案 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;
只要当前返回值低于2,组合就会继续。代码很难推理。我甚至不试图解释它(这不值得努力)。无论如何,compWhile
产生以下计算:
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;
将组合物分成较小的组合物,这些组合物代表所需的分支。如果你试图编写条件函数,你将不可避免地得到不可读的代码。
如果您只有一个简单的条件并且不想分割您的合成,那么至少要使分支尽可能明确,例如使用条件运算符:?
:
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;
函数组合是一种原子操作。不要试图过早退出或从作文中分支。
答案 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
除非满足过滤条件,否则上述两个示例数据都不会流向反转和大写操作,这似乎就是您所要求的。
虽然这很明显,但我觉得你的问题可能与其他事情有关。在任何一种情况下,我都希望这有助于讨论。