本文介绍了如何在JavaScript中正确地嵌入函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 我在JavaScript中编写了一个简单的 curry 函数的问题(以及大部分 咖喱 功能 people 在JavaScript中写)是它不能正确处理额外的参数。 code> curry 做 假设 f 是一个函数和 f.length 是 n 。让 curry(f)为 g 。我们用 m 参数来调用 g 。如果 m === 0 则返回 c $ c> g 。 如果 m 部分应用于 m 新参数,并返回一个新的curried函数它接受其余的 n-m 参数。 其他的 f m 参数并返回结果。 这是大多数 curry 函数,这是错误的。前两种情况是正确的,但第三种情况是错误的。相反,它应该是: 如果 m === 0 g 。 如果 m 部分应用于 m 新参数,并返回一个新的curried函数它接受其余的 n-m 参数。 如果 m === n 然后将 f 应用于 m 参数。如果结果是一个函数,那么 curry 结果。最后,返回结果。 如果 m> n 然后将 f 应用于第一个 n 参数。如果结果是一个函数,那么 curry 结果。最后,将结果应用于其余的 m -n 参数并返回新结果。 大多数 curry 函数的问题 请考虑以下代码: var countWhere = compose(compose(length),filter); countWhere(奇数,[1,2,3,4,5]); 如果我们使用不正确的 curry 函数,那么这相当于: $ b compose(compose(length),filter,odd,[1 ,2,3,4,5]); 然而, compose 只接受三个参数。最后一个参数被删除: var compose = curry(function(f,g,x) { return f(g(x)); }); 因此,上述表达式的计算结果为: compose(length)(filter(odd)); 这进一步评估为: compose(length,filter(odd)); compose 函数需要多一个参数是为什么它返回一个函数而不是返回 3 。要获得正确的输出,您需要编写: $ b countWhere(odd)([1,2 ,3,4,5]); 这就是为什么大多数 curry 函数是错误的。 解决方案使用正确的咖喱函数 请再次考虑以下代码: $ b var countWhere =撰写(撰写(长度),过滤); countWhere(奇数,[1,2,3,4,5]); 如果我们使用正确的 curry 函数,那么这相当于: $ b compose(compose(length),filter,odd)([ 1,2,3,4,5]); 计算结果为: compose(length)(filter(odd))([1,2,3,4,5]); 进一步评估(跳过一个中间步骤): compose(length,filter(odd),[1,2,3,4,5]); 结果如下: length(filter(odd,[1,2,3,4,5])); 产生正确的结果 3 p> 执行正确的咖喱函数 请注意,我没有使用 slice 将参数对象转换为数组,因为这是V8中的优化杀手: function curry(f){ var length = f.length; if(length> 0)return partial(f,length,[]); else返回f; // f已经curried } 函数partial(f,length,a){ return function(){ var count = arguments.length; var arity = length; var part = count<元数; var size = part?计数:arity; var args = new Array(size); var index = 0; while(index< size)args [index] = arguments [index ++]; if(part)return partial(f,arity - count,a.concat(args)); var result = f.apply(null,a.concat(args)); if(typeof result ===function) result = curry(result); if(arity var args = new Array(count - arity),number = 0; while(index< count)args [number ++] = arguments [index ++]; 返回result.apply(null,args); } 返回结果; }; } 我不确定咖喱的执行速度有多快是。也许有人可以让它变得更快。 使用正确的咖喱功能的含义 使用正确的 curry 函数可以让您直接将Haskell代码翻译成JavaScript。例如: $ b var id = curry(function(a){ return a ; }); var flip = curry(function(f,x,y){ return f(y,x); }); id 函数非常有用,因为它允许您可以轻松地部分应用非curried函数: $ b var add2 = id(add,2 ); 函数add(a,b){ return a + b; flip 函数是非常有用的,因为它允许您在JavaScript中轻松创建右侧部分: var sub2 = flip(sub,2); //相当于(x - 2) 函数sub(a,b){ return a - b; } 这也意味着你不需要像这样的黑客 extended compose function : 这个扩展的撰写功能有什么好名字? 你可以简单地写下: $ b var project =撰写(地图,选择); 正如问题中提到的,如果您想编写 length 和 filter 然后使用(f。)。 g pattern: 什么是(f。 )。 g的意思是在Haskell? 另一个解决方案是创建更高阶的 compose 函数: var compose2 = compose(compose,compose); var countWhere = compose2(length,fitler); 这一切都是可能的,因为 curry function。 深思熟虑 下面的 chain 函数,当我想编写一系列函数时: var chain = compose(function(a,x){ var length = a.length; while(length> 0)x = a [ - length] (x); return x; }); 这允许您编写如下代码: var inc = add(1); var foo = chain([map(inc),filter(odd),take(5)]); foo([1,2,3,4,5,6,7,8,9,10]); // [2,4,6] 它与以下Haskell代码等效: let foo = map(+1)。过滤奇数。拿5美元b $ b foo [1,2,3,4,5,6,7,8,9,10] 它还允许您编写如下代码: 链([map(inc),filter(odd),take(5)],[1,2,3,4,5,6,7,8,9,10]); // [2,4,6] 它与以下Haskell代码等效: map(+1)。过滤奇数。取5美元[1,2,3,4,5,6,7,8,9,10] 希望有所帮助。 I wrote a simple curry function in JavaScript which works correctly for most cases:var add = curry(function (a, b, c) { return a + b + c;});var add2 = add(2);var add5 = add2(3);alert(add5(5));<script>function curry(f) { var length = f.length; if (length > 0) { return partial(f, length, []); } else { return f; // f is already curried }}function partial(f, length, a) { return function () { var arity = length; var count = arguments.length; var args = new Array(count); var index = 0; while (index < count) { args[index] = arguments[index++]; } args = a.concat(args); return count < arity ? partial(f, arity - count, args) : f.apply(this, args); };}</script>However, it doesn't work for the following case:// length :: [a] -> Numberfunction length(a) { return a.length;}// filter :: (a -> Bool) -> [a] -> [a]var filter = curry(function (f, a) { return a.filter(f);});// compose :: (b -> c) -> (a -> b) -> a -> cvar compose = curry(function (f, g, x) { return f(g(x));});// countWhere :: (a -> Bool) -> [a] -> Numbervar countWhere = compose(compose(length), filter);According to the following question countWhere is defined as (length .) . filter:What does (f .) . g mean in Haskell?Hence I should be able to use countWhere as follows:countWhere(odd, [1,2,3,4,5]);function odd(n) { return n % 2 === 1;}However, instead of returning 3 (the length of the array [1,3,5]), it returns a function. What am I doing wrong? 解决方案 The problem with your curry function (and for most curry functions that people write in JavaScript) is that it doesn't handle extra arguments correctly.What curry doesSuppose f is a function and f.length is n. Let curry(f) be g. We call g with m arguments. What should happen?If m === 0 then just return g.If m < n then partially apply f to the m new arguments, and return a new curried function which accepts the remaining n - m arguments.Otherwise apply f to the m arguments and return the result.This is what most curry functions do, and this is wrong. The first two cases are right, but the third case is wrong. Instead, it should be:If m === 0 then just return g.If m < n then partially apply f to the m new arguments, and return a new curried function which accepts the remaining n - m arguments.If m === n then apply f to the m arguments. If the result is a function then curry the result. Finally, return the result.If m > n then apply f to the first n arguments. If the result is a function then curry the result. Finally, apply the result to the remaining m - n arguments and return the new result.The problem with most curry functionsConsider the following code:var countWhere = compose(compose(length), filter);countWhere(odd, [1,2,3,4,5]);If we use the incorrect curry functions, then this is equivalent to:compose(compose(length), filter, odd, [1,2,3,4,5]);However, compose only accepts three arguments. The last argument is dropped:var compose = curry(function (f, g, x) { return f(g(x));});Hence, the above expression evaluates to:compose(length)(filter(odd));This further evaluates to:compose(length, filter(odd));The compose function expects one more argument which is why it returns a function instead of returning 3. To get the correct output you need to write:countWhere(odd)([1,2,3,4,5]);This is the reason why most curry functions are wrong.The solution using the correct curry functionConsider the following code again:var countWhere = compose(compose(length), filter);countWhere(odd, [1,2,3,4,5]);If we use the correct curry function, then this is equivalent to:compose(compose(length), filter, odd)([1,2,3,4,5]);Which evaluates to:compose(length)(filter(odd))([1,2,3,4,5]);Which further evaluates to (skipping an intermediate step):compose(length, filter(odd), [1,2,3,4,5]);Which results in:length(filter(odd, [1,2,3,4,5]));Producing the correct result 3.The implementation of the correct curry functionNote that I'm not using slice to convert the arguments object into an array because that is an optimization killer in V8:function curry(f) { var length = f.length; if (length > 0) return partial(f, length, []); else return f; // f is already curried}function partial(f, length, a) { return function () { var count = arguments.length; var arity = length; var part = count < arity; var size = part ? count : arity; var args = new Array(size); var index = 0; while (index < size) args[index] = arguments[index++]; if (part) return partial(f, arity - count, a.concat(args)); var result = f.apply(null, a.concat(args)); if (typeof result === "function") result = curry(result); if (arity < count) { var args = new Array(count - arity), number = 0; while (index < count) args[number++] = arguments[index++]; return result.apply(null, args); } return result; };}I am not sure how fast this implementation of curry is. Perhaps somebody could make it faster.Implications of using the correct curry functionUsing the correct curry function allows you to directly translate Haskell code into JavaScript. For example:var id = curry(function (a) { return a;});var flip = curry(function (f, x, y) { return f(y, x);});The id function is useful because it allows you to partially apply a non-curried function easily:var add2 = id(add, 2);function add(a, b) { return a + b;}The flip function is useful because it allows you to easily create right sections in JavaScript:var sub2 = flip(sub, 2); // equivalent to (x - 2)function sub(a, b) { return a - b;}It also means that you don't need hacks like this extended compose function:What's a Good Name for this extended `compose` function?You can simply write:var project = compose(map, pick);As mentioned in the question, if you want to compose length and filter then you use the (f .) . g pattern:What does (f .) . g mean in Haskell?Another solution is to create higher order compose functions:var compose2 = compose(compose, compose);var countWhere = compose2(length, fitler);This is all possible because of the correct implementation of the curry function.Extra food for thoughtI usually use the following chain function when I want to compose a chain of functions:var chain = compose(function (a, x) { var length = a.length; while (length > 0) x = a[--length](x); return x;});This allows you to write code like:var inc = add(1);var foo = chain([map(inc), filter(odd), take(5)]);foo([1,2,3,4,5,6,7,8,9,10]); // [2,4,6]Which is equivalent to the following Haskell code:let foo = map (+1) . filter odd . take 5foo [1,2,3,4,5,6,7,8,9,10]It also allows you to write code like:chain([map(inc), filter(odd), take(5)], [1,2,3,4,5,6,7,8,9,10]); // [2,4,6]Which is equivalent to the following Haskell code:map (+1) . filter odd . take 5 $ [1,2,3,4,5,6,7,8,9,10]Hope that helps. 这篇关于如何在JavaScript中正确地嵌入函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
05-29 06:00
查看更多