作为函数式编程的练习,我决定通过我的一个项目并用Array.prototype
的高阶函数替换包含for循环的函数例如map
和reduce
。
我的项目中的一个函数在二维数组中平均列。它需要一个参数samples
,它是一个大小为[n][LOOKBACK]
的二维数组:
[
[0.6, 4.0, -0.5],
[1.0, -0.5, -0.8],
...
]
const LOOKBACK = 3
function averageChange(samples) {
let result = []
let count = 0,
i, j
for (i = 0; i < LOOKBACK; i++) {
let accumulator = 0
for (j = 0; j < samples.length; j++) {
accumulator += samples[j][i]
}
result.push(accumulator / samples.length)
}
return result
}
console.log(
averageChange([
[0.6, 4.0, -0.5],
[1.0, -0.5, -0.8]
])
)
&#13;
输出应该是一个大小为LOOKBACK
的数组,其元素是每列的平均值:
[0.8, 1.75, -0.65]
我花了一些时间试图找到解决方案,但我似乎无法想出一个解决方案。
这是否可以使用Javascript内置的数组函数?
来自Kirill的优雅解决方案。如果其他人有一个很好的解决方案,我很乐意看到更多。
答案 0 :(得分:2)
使用reduce
和forEach
函数尝试此示例:
let a = [
[0.6, 4.0, -0.5],
[3.0, -0.5, -0.1],
[1.0, -0.2, -0.8],
[7.0, -0.5, -0.8]
];
let b = a.reduce((acc, cur) => {
cur.forEach((e, i) => acc[i] = acc[i] ? acc[i] + e : e);
return acc;
}, []).map(e => e / a.length);
console.log(b);
&#13;
这是使用矩阵转置的更狡猾的方法:
let a = [
[0.6, 4.0, -0.5],
[3.0, -0.5, -0.1],
[1.0, -0.2, -0.8],
[7.0, -0.5, -0.8]
];
let b = a[0].map((col, i) => a.map(row => row[i]).reduce((acc, c) => acc + c, 0) / a.length);
console.log(b);
&#13;
答案 1 :(得分:2)
功能编程不只是编写单行并使用高阶函数,例如Arary#map
,Array#reduce
和{{3} }。顺便说一句,Array#filter
不起作用,因为它不是 Array#forEach
..
除高阶函数外,您还可以使用pure function,currying和function composition。
我们要做的是:
这可以在JavaScript中看起来像:
const averageChange = pipe(
rearrange ([]),
map (average)
)
pipe
是将组合多个函数组合成一个巨大函数的函数。 averageChange
现在接受一个参数,这将通过管道。
const rearrange = yss => xss =>
xss[0].length === 0
? yss
: rearrange
(concat (yss) ([ map ( getIndex (0) ) ( xss ) ]))
(map ( slice (1, xss[0].length) ) ( xss ))
这看起来很神秘。由于咖喱和功能组成,我们可以重写它:
const rearrange = yss => xss =>
matrixLength (xss) === 0
? yss
: rearrange
(concat (yss) ([ firstIndeces ( xss ) ]))
(excludeFirstIndeces ( xss ))
rearrange
是一个递归函数,用于转换矩阵
[
[0.6, 4.0, -0.5],
[3.0, -0.5, -0.1],
[1.0, -0.2, -0.8],
[7.0, -0.5, -0.8]
]
到
[
[ -0.5, -0.1, -0.8, -0.8 ],
[ 4 , -0.5, -0.2, -0.5 ],
[ 0.6, 3 , 1 , 7 ]
]
我编写了比其他解决方案更多的代码,但是我将逻辑划分为我自己的函数,这意味着我们现在可以将average
这样的函数用于代码的其他部分。另外,我已经为Array#map
等编写了curryied版本来编写它们。如果你使用一个库,这将是多余的..
// helper functions
const pipe = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)))
const getIndex = i => xs =>
xs[i]
const map = f => xs =>
xs.map(f)
const reduce = f => seel => xs =>
xs.reduce(f)
const concat = ys => xs =>
xs.concat(ys)
const slice = (start, end) => xs =>
xs.slice(start, end)
const average = xs =>
reduce ((sum, x) => sum + x) (0) (xs) / xs.length
const length = xs =>
xs.length
const matrixLength = pipe(
getIndex(0),
length
)
const firstIndex = getIndex (0)
const firstIndeces = map ( firstIndex )
const excludeFirstIndex = xss => slice (1, matrixLength (xss)) (xss)
const excludeFirstIndeces = map ( excludeFirstIndex )
// business logic
const rearrange = yss => xss =>
matrixLength (xss) === 0
? yss
: rearrange
(concat (yss) ([ firstIndeces ( xss ) ]))
(excludeFirstIndeces ( xss ))
const averageChange = pipe (
rearrange ([]),
map(average)
)
const values = [
[0.6, 4.0, -0.5],
[3.0, -0.5, -0.1],
[1.0, -0.2, -0.8],
[7.0, -0.5, -0.8]
]
console.log( averageChange (values) )
&#13;
答案 2 :(得分:0)
var a=[];var b=[];
var i = 0; j = 0;
cubes.forEach(function each(item,length) {
if (Array.isArray(item)) {
item.forEach(each);
j++;
i = 0;
} else {
if(a[i]===undefined){
a[i]=0;b[i]=0}
a[i]=(a[i]*b[i]+item)/(b[i]+1);
b[i]=b[i]+1;
i++;
}
});
console.log(a);
这甚至可以用于不同的内部数组大小。这样的东西[[1],[2,3,4]]
cubes=[[1],[1,2,3]]
var a=[];var b=[];
var i = 0; j = 0;
cubes.forEach(function each(item,length) {
if (Array.isArray(item)) {
item.forEach(each);
j++;
i = 0;
} else {
if(a[i]===undefined){
a[i]=0;b[i]=0}
a[i]=(a[i]*b[i]+item)/(b[i]+1);
b[i]=b[i]+1;
i++;
}
});
console.log(a);