Curried 函数:如何优化它们
Curried functions: how to optimize them
我对函数式编程和库(例如 ramda.js 比较陌生,但我发现非常有用的一件事是柯里化函数的可能性。
使用柯里化函数,我经常写如下内容
const myFun = R.curry(
(arg1, arg2) => {
let calculated = anotherFun(arg1)
//do something with calculated and arg2
return calculated * 5 + arg2
}
)
const anotherFun = (arg) => {
console.log("calling anotherFun");
return arg + 1
}
var partial = myFun(1)
console.log(partial(2))
console.log(partial(3))
<script src="//cdn.jsdelivr.net/ramda/0.22.1/ramda.min.js"></script>
但显然在这种情况下,每次我调用 partial
时都会调用 anotherFun
,即使在 arg1
中也是如此,因此 calculated
始终相同。
有没有办法优化此行为并仅在其参数更改时调用 anotherFun
?
我唯一想到的就是这个
const myFun = R.curry(
(calculated, arg2) => {
return calculated * 5 + arg2
}
)
const anotherFun = (arg) => {
console.log("calling anotherFun");
return arg + 1
}
var calculated = anotherFun(1)
var partial = myFun(calculated)
console.log(partial(2))
console.log(partial(3))
<script src="//cdn.jsdelivr.net/ramda/0.22.1/ramda.min.js"></script>
但是这样我必须更改传递给 myFun 的参数,这会使外部 API
如果你像这样手动进行柯里化
const myFun = arg1 => arg2 => {
let calculated = anotherFun(arg1)
// do something with calculated and arg2
return calculated * 5 + arg2
};
你也可以这样优化:
const myFun = arg1 => {
let calculated = anotherFun(arg1);
return arg2 => {
// do something with calculated and arg2
return calculated * 5 + arg2
};
};
我认为 Ramda 不会在这里帮助您任何事情; JavaScript 编译器肯定不会进行这种优化。
@Bergi 是的,Ramda 不会为此提供任何帮助。如果你想要一个 Ramda 风格的结果,你可以用一个参数调用来获取一个函数,或者同时调用两个参数来获取结果,你可以这样做:
const myFun = function(arg1, arg2) {
let calculated = anotherFun(arg1);
const newFunc = arg2 => {
return calculated * 5 + arg2
};
return (arguments.length < 2) ? newFunc : newFunc(arg2);
};
const with3 = myFun(3);
//: calling anotherFun
with3(1); //=> 21
with3(2); //=> 22
with3(4); //=> 23
myFun(2, 7);
//: calling anotherFun
//=> 22
myFun(2, 8);
//: calling anotherFun
//=> 23
这是以无法使用 ES2015 箭头函数为代价的。但这对你来说可能是值得的。
如果提供了两个参数,您也可以稍微修改一下以不构建内部函数,如果这对您很重要的话。
Ramda 的 useWith
和 memoize
怎么样?
const myFun = R.useWith(
(a, b) => a * 5 + b,
[R.memoize(anotherFun), R.identity]
);
我对函数式编程和库(例如 ramda.js 比较陌生,但我发现非常有用的一件事是柯里化函数的可能性。
使用柯里化函数,我经常写如下内容
const myFun = R.curry(
(arg1, arg2) => {
let calculated = anotherFun(arg1)
//do something with calculated and arg2
return calculated * 5 + arg2
}
)
const anotherFun = (arg) => {
console.log("calling anotherFun");
return arg + 1
}
var partial = myFun(1)
console.log(partial(2))
console.log(partial(3))
<script src="//cdn.jsdelivr.net/ramda/0.22.1/ramda.min.js"></script>
但显然在这种情况下,每次我调用 partial
时都会调用 anotherFun
,即使在 arg1
中也是如此,因此 calculated
始终相同。
有没有办法优化此行为并仅在其参数更改时调用 anotherFun
?
我唯一想到的就是这个
const myFun = R.curry(
(calculated, arg2) => {
return calculated * 5 + arg2
}
)
const anotherFun = (arg) => {
console.log("calling anotherFun");
return arg + 1
}
var calculated = anotherFun(1)
var partial = myFun(calculated)
console.log(partial(2))
console.log(partial(3))
<script src="//cdn.jsdelivr.net/ramda/0.22.1/ramda.min.js"></script>
但是这样我必须更改传递给 myFun 的参数,这会使外部 API
如果你像这样手动进行柯里化
const myFun = arg1 => arg2 => {
let calculated = anotherFun(arg1)
// do something with calculated and arg2
return calculated * 5 + arg2
};
你也可以这样优化:
const myFun = arg1 => {
let calculated = anotherFun(arg1);
return arg2 => {
// do something with calculated and arg2
return calculated * 5 + arg2
};
};
我认为 Ramda 不会在这里帮助您任何事情; JavaScript 编译器肯定不会进行这种优化。
@Bergi 是的,Ramda 不会为此提供任何帮助。如果你想要一个 Ramda 风格的结果,你可以用一个参数调用来获取一个函数,或者同时调用两个参数来获取结果,你可以这样做:
const myFun = function(arg1, arg2) {
let calculated = anotherFun(arg1);
const newFunc = arg2 => {
return calculated * 5 + arg2
};
return (arguments.length < 2) ? newFunc : newFunc(arg2);
};
const with3 = myFun(3);
//: calling anotherFun
with3(1); //=> 21
with3(2); //=> 22
with3(4); //=> 23
myFun(2, 7);
//: calling anotherFun
//=> 22
myFun(2, 8);
//: calling anotherFun
//=> 23
这是以无法使用 ES2015 箭头函数为代价的。但这对你来说可能是值得的。
如果提供了两个参数,您也可以稍微修改一下以不构建内部函数,如果这对您很重要的话。
Ramda 的 useWith
和 memoize
怎么样?
const myFun = R.useWith(
(a, b) => a * 5 + b,
[R.memoize(anotherFun), R.identity]
);