Javascript 如何以正确的顺序链接映射和筛选函数
我非常喜欢链接Javascript 如何以正确的顺序链接映射和筛选函数,javascript,functional-programming,Javascript,Functional Programming,我非常喜欢链接Array.prototype.map、过滤器和reduce来定义数据转换。不幸的是,在最近的一个涉及大型日志文件的项目中,我再也不能通过多次循环我的数据来逃脱 我的目标是: 我想创建一个函数来链接.filter和.map方法,而不是立即在数组上映射,组成一个函数,在数据上循环一次。即: 我的尝试: 受此启发,我开始编写一个Transduce函数 const filterer = pred => reducer => (acc, x) => pred(x)
Array.prototype.map
、过滤器
和reduce
来定义数据转换。不幸的是,在最近的一个涉及大型日志文件的项目中,我再也不能通过多次循环我的数据来逃脱
我的目标是:
我想创建一个函数来链接.filter
和.map
方法,而不是立即在数组上映射,组成一个函数,在数据上循环一次。即:
我的尝试:
受此启发,我开始编写一个Transduce
函数
const filterer = pred => reducer => (acc, x) =>
pred(x) ? reducer(acc, x) : acc;
const mapper = map => reducer => (acc, x) =>
reducer(acc, map(x));
const Transduce = (reducer = (acc, x) => (acc.push(x), acc)) => ({
map: map => Transduce(mapper(map)(reducer)),
filter: pred => Transduce(filterer(pred)(reducer)),
run: arr => arr.reduce(reducer, [])
});
问题是:
上面的Transduce
代码段的问题是它“向后”运行。。。最后一个方法I链是第一个要执行的方法:
const someTransformation = Transduce()
.map(x => x + 1)
.filter(x => x > 3)
.map(x => x / 2);
// Instead of [ 2, 2.5 ] this returns []
// starts with (x / 2) -> [0.5, 1, 1.5, 2]
// then filters (x < 3) -> []
const myData = someTransformation.run([ 1, 2, 3, 4]);
致:
Transducer(concat).map(f).map(g) == (acc, x) => concat(acc, g(f(x)))
这类似于:
mapper(f) (mapper(g) (concat))
我想我理解它为什么会发生,但我不知道如何在不改变函数“接口”的情况下修复它
const filterer = pred => reducer => (acc, x) =>
pred(x) ? reducer(acc, x) : acc;
const mapper = map => reducer => (acc, x) =>
reducer(acc, map(x));
const Transduce = (reducer = (acc, x) => (acc.push(x), acc)) => ({
map: map => Transduce(mapper(map)(reducer)),
filter: pred => Transduce(filterer(pred)(reducer)),
run: arr => arr.reduce(reducer, [])
});
问题是:
我如何才能使我的转换
方法链过滤器
和映射
操作按正确顺序进行
笔记:
- 我只是在学习一些我想做的事情的命名。请告知我是否错误使用了
术语,或者是否有更好的方法来描述问题变速器
- 我知道我可以使用嵌套的
循环执行相同的操作:for
const push=(acc,x)=>(acc.push(x,acc);
const ActionChain=(actions=[])=>{
const run=arr=>
arr.reduce((acc,x)=>{
for(设i=0,动作;ifn=>
ActionChain(push(actions,{type,fn}));
返回{
映射:addAction(“映射”),
过滤器:添加操作(“过滤器”),
跑
};
};
//与常规链条进行比较,检查是否
//性能有所提高
//诚然,在这个例子中,它非常小。。。
常数法={
运行:arr=>
啊
.map(x=>x+3)
.filter(x=>x%3==0)
.map(x=>x/3)
.filter(x=>x<40)
};
const actionChain=actionChain()
.map(x=>x+3)
.filter(x=>x%3==0)
.map(x=>x/3)
.filter(x=>x<40)
const testData=Array.from(数组(100000),(x,i)=>i);
时间(“天真”);
const result1=naiveApproach.run(testData);
console.timeEnd(“天真”);
控制台。时间(“链”);
constresult2=actionChain.run(testData);
控制台。时间结束(“链”);
log(“equal:,JSON.stringify(result1)==JSON.stringify(result2))代码>我认为您需要更改实现的顺序:
const filterer = pred => reducer => (x) =>pred((a=reducer(x) )?x: undefined;
const mapper = map => reducer => (x) => map(reducer(x));
然后,您需要将run命令更改为:
run: arr => arr.reduce((a,b)=>a.concat([reducer(b)]), []);
默认的减速机必须是
x=>x
但是,这种方式过滤器无法工作。您可以在filter函数中抛出undefined,在run函数中抛出catch:
run: arr => arr.reduce((a,b)=>{
try{
a.push(reducer(b));
}catch(e){}
return a;
}, []);
const filterer = pred => reducer => (x) =>{
if(!pred((a=reducer(x))){
throw undefined;
}
return x;
};
然而,总的来说,我认为for循环在这种情况下要优雅得多…在我们了解更多之前
我真的很喜欢锁链
我明白了,我会安抚你,但你会明白,强迫你的程序通过链接API是不自然的,而且在大多数情况下比它的价值更麻烦
我想我理解它为什么会发生,但我不知道如何在不改变函数“接口”的情况下修复它
const filterer = pred => reducer => (acc, x) =>
pred(x) ? reducer(acc, x) : acc;
const mapper = map => reducer => (acc, x) =>
reducer(acc, map(x));
const Transduce = (reducer = (acc, x) => (acc.push(x), acc)) => ({
map: map => Transduce(mapper(map)(reducer)),
filter: pred => Transduce(filterer(pred)(reducer)),
run: arr => arr.reduce(reducer, [])
});
问题确实出在Transduce
构造函数上。您的map
和filter
方法将map
和pred
堆叠在传感器链的外部,而不是嵌套在内部
下面,我已经实现了您的Transduce
API,它以正确的顺序评估映射和过滤器。我还添加了一个log
方法,以便查看Transduce
的行为
const-Transduce=(f=k=>k)=>({
地图:g=>
转换(k=>
f((acc,x)=>k(acc,g(x)),
过滤器:g=>
转换(k=>
f((acc,x)=>g(x)?k(acc,x):acc)),
日志:s=>
转换(k=>
f((acc,x)=>(console.log(s,x),k(acc,x)),
运行:xs=>
x.reduce(f((acc,x)=>acc.concat(x)),[])
})
常量foo=nums=>{
返回转换器()
.log('大于2?')
.filter(x=>x>2)
.log('\tsquare:')
.map(x=>x*x)
.log(“\t\t小于30”)
.filter(x=>x<30)
.log('\t\t\tpass')
.run(nums)
}
//对于所有n个NUM,保持正方形(n)
//其中n>2
//其中平方(n)<30
日志(foo([1,2,3,4,5,6,7]))
//=>[9,16,25]
map:map=>Transduce(映射器(映射器)(还原器)),意思是:返回一个先映射的函数,然后执行原始还原。转换器构建堆栈。尝试run:arr=>arr.reduceRight(reducer,[])
@ft或者我想让run
中的reducer
成为之前传递的所有map
和filter
方法的组合。我认为问题不在于我用数据减少我的arr
的顺序,而在于我无法正确返回新的reducer
方法。例如:mapper(f)(mapper(g)(concat))
返回一个类似于concat(acc,x)=>concat(acc,g(f(x))
,而myTransduce(concat).map(f).map(g)
返回(acc,x)=>concat(acc,f(x))
我认为这不起作用,因为mypred
是x->bool
的函数,map
是x->y
。这使得reducer首先运行,返回一个数组(至少在我当前的示例中是这样)。还是我遗漏了什么?@user3297291你是对的。您还需要更改run和默认的reducer。问题的关键在于支持过滤器。我同意
const Transduce = (reducer = (acc, x) => (acc.push(x), acc)) => ({
map: map => Transduce(mapper(map)(reducer)),
filter: pred => Transduce(filterer(pred)(reducer)),
run: arr => arr.reduce(reducer, [])
});