我想实现一个更高阶函数(hof
),它基本上像F#style forward-pipe运算符一样工作(将值作为第一个参数传递给另一个函数myFunc
)。我能想到的唯一方法是:
function hof(val, myFunc, args_array) {...}
其中args_array
是调用myFunc
的参数数组(不包括第一个参数,因为那将是val
)
但这对我来说并不是很优雅。有更好的方法吗?
编辑:我在github上发现了这个https://gist.github.com/aaronpowell/d5ffaf78666f2b8fb033。但我真的不明白sweet.js
代码在做什么。如果您可以注释代码,那将非常有用,具体来说:
case infix { $val | _ $fn($args (,) ...) } => {
return #{
($fn.length <= [$args (,) ...].length + 1 ? $fn($args (,) ..., $val) : $fn.bind(null, $args (,) ..., $val))
}
}
case infix { $val | _ $fn } => {
return #{
($fn.length <= 1 ? $fn($val) : $fn.bind(null, $val))
}
}
答案 0 :(得分:1)
如果你想要像F#管道运营商这样的东西,我认为你最好的选择就是你在帖子中采用的方法:
hof(val, myFunc, [arg1, arg2, arg3]);
或者这个:
hof(val, myFunc, arg1, arg2, arg3);
第一个可以像这样实现:
function hof(val, func, args) {
func.apply(this, [val].concat(args || []));
}
第二个可以像这样实现:
function hof(val, func) {
func.apply(this, [val].concat(Array.prototype.slice(arguments, 2));
}
但是这一切都留下了为什么你不能以正常的方式调用函数的问题:
myFunc(val, arg1, arg2, arg3);
答案 1 :(得分:1)
这不是Currying吗?
无论如何,这是一个垃圾的例子,我确信如果你search有更好的例子:
function myCurry() {
var args = [].slice.call(arguments);
var fn = args.splice(0,1)[0];
return function(arg) {
var a = [].concat(arg, args);
return fn.apply(this, a);
};
}
// Just sums the supplied arguments, initial set are 1,2,3
var fn = myCurry(function(){
var sum = 0;
for (var i=0, iLen=arguments.length; i<iLen; i++) {
// Show args in sequence - 4, 1, 2, 3
console.log('arguments ' + i + ': ' + arguments[i]);
sum += arguments[i];
}
return sum;
}, 1,2,3);
// Provide an extra argument 4
console.log(fn(4)); // 10
答案 2 :(得分:1)
Sweet.js宏很简单,一见钟情看起来很神秘。
F#语言中的转发管道,中缀运算符|>
将右侧的函数应用于左侧表达式的结果。
新语法应如下所示(|>
宏):
var seven = 7;
var outcome = 3 |> plus(seven) |> divideBy(2); // outcome must be 5
管道宏必须将参数附加到函数,以便在扩展函数看起来像这样:plus(seven, 3)
。
sweet.js宏in the question适用于最简单的情况。
主要部分在此代码段return #{ /** macro expansion template */ }
中。
它包含在编译时执行的javascript(sweet.js)返回文本,该文本是宏扩展的代码。
编译结果:
var outcome$633 = plus.length <= [seven].length + 1 ? plus(seven, 5) : plus.bind(null, seven, 5);
The length
of a function in js is its number of expected positional arguments
一个sofisticated宏,允许使用对象中包含的函数(点表示法):
macro (|>) {
case infix { $val | _ $fn(.)... ( $args(,)... ) } => {
return #{
($fn(.)....length <= [$args(,)...].length + 1 ? $fn(.)...( $args(,)..., $val) : $fn(.)....bind(null, $args(,)..., $val))
}
}
case infix { $val | _ $fn($args ...) } => {
return #{
($fn.length <= [$args ...].length + 1 ? $fn($args ..., $val) : $fn.bind(null, $args ..., $val))
}
}
case infix { $val | _ $fn(.)... } => {
return #{
($fn(.)....length <= 1 ? $fn(.)...($val) : $fn(.)....bind(null, $val))
}
}
}
如果管道中的函数不接受参数,则此宏的一个限制/要求是省略parens ()
。
行var outcome = 5 |> plus(seven) |> obj.subObj.func;
扩展为js中的这个嵌套三元运算符表达式:
var outcome$640 = obj.subObj.func.length <= 1 ? obj.subObj.func(plus.length <= [seven].length + 1 ? plus(seven, 5) : plus.bind(null, seven, 5)) : obj.subObj.func.bind(null, plus.length <= [seven].length + 1 ? plus(seven, 5) : plus.bind(null, seven, 5));
答案 3 :(得分:0)
您可以轻松地使用闭包进行此类包装:
function callf(f, count) {
for (var i=0; i<count; i++) {
f(i);
}
}
function g(x, y) {
console.log("called g(" + x + ", " + y + ")");
}
callf(function(x) { g(x, "hey!"); }, 10);
答案 4 :(得分:0)
第一次调用myFunc会返回另一个接受单个参数的函数。然后,当您执行初始调用hof(val, myFunc(arg1, arg2, arg3));
时,将返回该函数,并且只需要一个参数val
来完成执行,这由hof
提供。
function hof(val, func){
func(val);
}
function myFunc(arg1, arg2, arg3){
return function(val){
// do something with val, arg1, arg2, arg3 here
}
}
hof(val, myFunc(arg1, arg2, arg3));
如果你说的是真的,你可以使用下面的代码,但我没有看到一点,因为你可以直接运行myFunc。
function myFunc(val, arg1, arg2, arg3){
// do something
}
function hof(val, myfunc, arg1, arg2, arg3){
return myFunc(val, arg1, arg2, arg3);
}
function customCurry(func){
var args = [];
var result;
return function(){
var l = arguments.length;
for(var i = 0; i < l; i++){
if(args.length > 3) break;
args.push(arguments[i]);
}
if(args.length > 3){
return result = func(args[3], args[0], args[1], args[2]);
}
}
}
var preloaded = customCurry(myFunc)(arg1, arg2, arg3);
hof(val, myFunc);
function hof(val, func){
func(val);
}
答案 5 :(得分:0)
ecma5添加了 Function.bind(),这也是一个问题:
myFunc2=myFunc.bind(this, val, arg1, arg2, arg3);
如果你想要一个可重复使用的泛型函数来使 bind()更容易一些:
function hof(f, args){ return f.bind.apply(f, [this].concat(args));}
返回一个新函数,参数预填充先前传递的值。传递更多参数会将它们移到右侧,您可以通过 arguments 关键字访问所有参数。
答案 6 :(得分:0)
我们来看一个例子。
假设你有一个简单的函数来总结数字:
var sum = function() {
return Array.prototype.slice.call(arguments).reduce(function(sum, value) {
return sum + value;
}, 0);
};
你不能简单地通过传递参数来调用它,然后将它传递给另一个函数,因为它将评估函数,所以我们需要在函数周围创建一个包装器来移动参数:
var sumWrapper = function() {
var args = Array.prototype.slice.call(arguments);
return function() {
var innerArgs = Array.prototype.slice.call(arguments).concat(args);
return sum.apply(null, innerArgs);
}
};
然后我们可以创建你的高阶函数(假设最后一个参数是有问题的函数):
var higherOrderSum = function() {
return arguments[arguments.length - 1].apply(null, Array.prototype.slice.call(arguments, 0, arguments.length - 1));
};
用法:
alert(higherOrderSum(3, 4, sumWrapper(1, 2, 3)));
答案 7 :(得分:0)
在ES6中,这变得非常紧凑:
var hof = (fn, ...params) => (...addtl) => fn(...parms, ...addtl);