在高阶函数中传递附加参数 [英] Passing additional parameters in higher-order functions

查看:114
本文介绍了在高阶函数中传递附加参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这个例子:



  const samples = [foo bar]; const excludeFoos = function(item){return item!==foo;} const foos = samples.filter(excludeFoos);  



如何在 excludeFoos 中传递其他参数?例如:



  const samples = [foo,bar]; const exclude = function(item,str){return item!== str;} //显然不会工作,但是你得到了pointconst foos = samples。过滤器(排除( 富));执行console.log(FOOS); // [bar]  

解决方案

命名事物


如果你有精神的名字,你有权力。–你可以想到一个更好的名字你的排除功能?我知道我可以它被称为 notEqual 。简单地认识它是真正的名字,使得它在解决问题时更加多才多艺。 排除在过滤数组的上下文中是有道理的,但不知何故,如果我们想在其他地方使用 exclude 函数,这并不太有意义。

  if(exclude(a,b))
console.log(a和b不相等)

功能编程就是使功能尽可能的重用,所以我们向前走,让我们坚持使用

  const notEqual =(x,y)=> x!== y 

Function.prototype.bind



功能.prototype.bind 用于将值绑定到函数参数。这是常用的,因为它是原生的,因为ECMAScript 5–这意味着您可以在不添加任何附加依赖关系或对现有代码进行任何更改的情况下实现目标。



  const notEqual =(x,y)=> x!== yconst samples = ['foo','bar'] const foos = samples.filter(notEqual.bind(null,'foo'))console.log(foos)// [bar]  



部分应用



部分应用程序具有一个功能和一些参数,并产生另一个功能较小的 arity 是一个花哨的词,一个函数的参数数现在您已经熟悉了 Function.prototype.bind ,您已经了解了部分应用程序。唯一的区别是 bind 强制您提供绑定的上下文。上下文在大多数功能性程序中都很麻烦,所以有时候使用这个功能可以让我们在不用关注自己的情况下部分应用。



  const partial =(f,... xs)=> (... ys)=> f(... xs,... ys)const notEqual =(x,y)=> x!== yconst samples = ['foo','bar'] const foos = samples.filter(partial(notEqual,'foo'))console.log(foos)// [bar]  



Currying



Currying 虽然与部分应用程序类似,但是另一种方法可以解决您的问题。 Currying具有多个参数的功能,并将其转换为一系列一元函数–



  const notEqual =(x,y) => x!== yconst curry = f => x => y => f(x,y)const samples = ['foo','bar'] const foos = samples.filter(curry(notEqual)('foo'))console.log(foos)// [bar]  



如果您无法看到这与部分应用程序不同,注意,在功能大于两个&ndash之前,您将看不到很大的区别;另请参阅:与部分应用程序进行对比。



如你所见,可读性开始受到一点影响。而不是在飞行中,如果 notEqual 在我们的控制之下,我们可以从开始定义它的咖喱形式



< class =snippet-code-js lang-js prettyprint-override> const notEqual = x => y => x!== yconst samples = ['foo','bar'] const foos = samples.filter(notEqual('foo'))console.log(foos)// [bar]



您可能没有注意到,但 partial (以上)以curried风格定义


相关: 多个箭头功能在JavaScript中意味着什么?


Currying是一个强大的概念,在各种各样的方面是有用的。你可能会说,解决这个单独的孤立问题太过分了,你会是对的。当一个程序或语言被广泛使用时,你才会开始看到curry的好处,因为它具有一个系统效应–最终,它提供了功能本身本身的抽象。



  const apply = f => x => f(x)const notEqual = x => y => x!== yconst filter = f => xs => xs.filter(apply(f))const notFoo = filter(notEqual('foo'))const samples = ['foo','bar'] console.log(notFoo(samples)); // [bar]  



/ strong>



有很多选项可供您使用,您可能会想知道哪个是正确的选择。如果你正在寻找一个银弹,你会很难学会没有一个。与所有的一切有折衷。



我发现部分/程序性的应用程序是不可或缺的工具,因此我尝试将所有的JavaScript函数全部写在咖喱形成。这样我就可以避免在我的程序中删除 partial curry 的调用。其结果是代码最终看起来有点外国人,起初– ID生成器通用功能重复合并/展开数组自定义迭代



您的程序的所有部分都不完全在您的控制之下,对吧?当然,您可能会使用一些外部依赖项,而且它们不太可能拥有您正在寻找的完美功能界面。在这种情况下,您最终将使用 partial curry 与您不能更改。



最后,看一些功能库,如 folktalke Ramda 。我不推荐使用初学者功能性程序员,但是在您切牙之后需要考虑一下。


Consider this example:

const samples = ["foo", "bar"];

const excludeFoos = function(item) {
  return item !== "foo";
}

const foos = samples.filter(excludeFoos);

How can I pass an additional parameter in excludeFoos?

For example:

const samples = ["foo", "bar"];

const exclude = function(item, str) {
  return item !== str;
}

// obviously won't work but you get the point
const foos = samples.filter(exclude("foo"));
console.log(foos); // ["bar"]

解决方案

Naming things

"If you have the name of a spirit, you have power over it." – Gerald Jay Sussman

Can you think of a better name for your exclude function? I know I can. It's known as notEqual. Simply knowing it as its true name makes it much more versatile when it comes to problem solving. "exclude" makes sense in the context of filtering an array, but somehow it makes less sense if we wanted to use the exclude function elsewhere.

if (exclude(a,b))
  console.log("a and b are not equal")

Functional programming is all about making functions as reusable as possible, so as we move forward, let's stick with

const notEqual = (x,y) => x !== y

Function.prototype.bind

Function.prototype.bind is used to bind values to function parameters. It's commonly used because it's been native since ECMAScript 5 – meaning you can accomplish your goal without adding any additional dependencies or making any changes to your existing code.

const notEqual = (x,y) => x !== y

const samples = ['foo', 'bar']

const foos = samples.filter(notEqual.bind(null, 'foo'))

console.log(foos) // ["bar"]

Partial Application

Partial application takes a function and some arguments and produces another function of smaller arity – arity is a fancy word for "the number of arguments a function takes"

Now that you're familiar with Function.prototype.bind, you already know partial application. The only difference is bind forces you to provide the context of a binding. Contexts are a bother in most functional programs, so sometimes it's easier to have a function that lets us partially apply without concerning ourselves with context.

const partial = (f, ...xs) => (...ys) => f(...xs, ...ys)

const notEqual = (x,y) => x !== y

const samples = ['foo', 'bar']

const foos = samples.filter(partial(notEqual, 'foo'))

console.log(foos) // ["bar"]

Currying

Currying, while similar to partial application, is another way to approach your problem. Currying takes a function of multiple arguments and transforms it into a sequence of unary functions – functions that take one argument each.

const notEqual = (x,y) => x !== y

const curry = f => x => y => f(x,y)

const samples = ['foo', 'bar']

const foos = samples.filter(curry(notEqual)('foo'))

console.log(foos) // ["bar"]

If you're having trouble seeing how this is different than partial application, note you won't see much of a difference until function arity is greater than two – See also: contrast currying with partial application.

As you can see, readability is starting to suffer a little bit. Instead of currying on the fly, if notEqual is under our control, we could define it in curried form from the start

const notEqual = x => y => x !== y

const samples = ['foo', 'bar']

const foos = samples.filter(notEqual('foo'))

console.log(foos) // ["bar"]

You may not have even noticed it, but partial (above) is defined in curried style!

Related: "What do multiple arrow functions mean in JavaScript?"

Currying is a massively powerful concept and useful in a wide variety of ways. You might say it's overkill for solving this single, isolated problem, and you'd be right. You'll really only start to see the benefits of currying when it is widely used in a program or language as it has a systemic effect – and ultimately, it provides abstraction over function arity itself.

const apply = f => x => f (x)

const notEqual = x => y => x !== y

const filter = f => xs => xs.filter(apply(f))

const notFoo = filter(notEqual('foo'))

const samples = ['foo', 'bar']

console.log(notFoo(samples)); // ["bar"]

Final Remarks

There's a lot of options available to you and you might be wondering which is the "correct" one to choose. If you're looking for a silver bullet, you'll be sad to learn there isn't one. As with everything there are trade-offs.

I find partial/procedural application to be an indispensable tool, and therefore I try to write all of my JavaScript functions in fully curried form. That way I avoid dropping calls to partial and curry all over my program. The consequence of this is the code ends up looking a little foreign, at first – id generatorgeneric function repetitionmerge/flatten arraycustom iteration

Not all parts of your programs are fully under your control tho, right? Of course you're probably using some external dependencies and it's unlikely that they're going to have the perfect functional interface you're looking for. In such a case, you'll end up using partial and curry to interface with other code that you cannot change.

Lastly, look at some of the functional libraries out there like folktalke or Ramda. I don't recommend either for beginner functional programmers, but something worth looking into after you cut your teeth.

这篇关于在高阶函数中传递附加参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆