为了“结合”函数在JavaScript中的功能方式? [英] To "combine" functions in javascript in a functional way?

查看:71
本文介绍了为了“结合”函数在JavaScript中的功能方式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习函数式编程,我想知道是否有这样的组合功能:

 函数triple(x){
return x * 3;
}
函数plusOne(x){
return x + 1;
}
函数isZero(x){
return x === 0;
}
合并(1); // 1
combine(triple)(triple)(plusOne)(1); // 10
combine(plusOne)(triple)(isZero)( - 1); // true

如果para是一个函数,它将函数组合到自身中,if不会返回最终结果。
Thanks!

解决方案

这个概念来自可爱的数学。它被称为功能组合

g(y)= z 
$ b $(f(x))= z

(g•f)(x)= z

最后一行是g x的f等于z组合功能的优点是消除了。注意 g(f(x))= z 我们取一个 x 输入并得到一个 z 输出。这完全跳过了中间 y 。在这里,我们说我们删除了点



所以我们有一个组合的函数 (g•f)(x)= z ,我们将它设置为 h(x)

  h(x)=(g•f)(x)= z 

既然(g•f)已经是一个函数,我们可以移除另一个点, x

  h(x)=(g•f)(x)= z 
h =(g•f)

功能组合是创建高级函数,并保持代码的清洁。很容易明白为什么我们希望在你的Javascript中使用它。

让我们创建自己的组合函数!



好的,你听起来真的很兴奋。这里我们去...

 函数triple(x){
return x * 3;
}

函数plusOne(x){
return x + 1;
}

函数id(x){return x; }

函数compN(funcs){
return funcs.reduce(function(g,f){
return function(x){
return g(f( x));
}
},id);
}


var g = compN([triple,triple,plusOne]);
// g(x)=(triple•triple•plusOne)(x)

g(1);
// => 18

评估

  triple(triple(plusOne(1)))
triple(triple(2))
triple(6)
18







如果您对这个答案感到满意,那就很好。对于那些想探索构建更好的高阶函数的人来说,请继续!

考虑到构建高阶函数的概念,其他函数,我们可以对 compN



的定义进行重构和扩展。首先让我们来了解 compN 正在评估事物。假设我们要编写3个函数, a b c



<$ (x(x))= a(b(c(x)));
// g(x)=(a•b•c)(x)
// g =(a•b•c)
var g = compN([a,b,现在, compN 将会调用减少并使用我们的 id 函数对其进行初始化(原因明显稍后)



reduce 的工作方式是在数组中为每个项目调用一次这个内部函数



<$ p $函数(g,f){返回函数(x){g(f(x)); }; }

我有意添加空白符合下面的表格

 迭代gf返回解释
---------------- -------------------------------------------------- ----------
#1 id aλx=> g(f(x))原始返回值
λx=> id(a(x))代替`g`和`f`
a',我们称之为a prime

#2 a'bλx=> g(f(x))原始返回值
λx=> a'(b(x))替代`g`和`f`
b',我们称之为b素数

#3 b'cλx=> g(f(x))原始返回值
λx=> b'(c(x))代替`g`和`f`
c',我们称之为c prime
>> c'是最终的返回值<<

所以当我们调用 compN([a,b,c]) c'是返回给我们的函数。

<

当我们调用该函数时会发生什么? b
$ b

  alias original x return 
---------------- -------------------------------------------------- ----------
c'(5); λx=> b'(c(x))5 b'(c(5))
b'(c(5))λx=> a(b(x))c(5)a'(b(c(5)))
a'(b(c(5)))λx=> id(a(x))b(c(5))id(a(b(c(5))))< - 最终返回值

  compN([a,b ,c])(5)=== id(a(b(c(5)))); 

这真是太可爱了,但这对我的大脑来说有点难



好的,我同意,如果您和我一样,当我在3个月内返回此代码时,我我不知道该怎么办。



因此,要开始改善 compN ,让我们第一次修订

  // g(x)= a(b(c(x))); 
// g(x)=(a•b•c)(x)
// g =(a•b•c)
var g = compN([a,b, c])

如果我们看另一个例子,也许我们会得到一个提示

  [1,2,3] .reduce(function(x,y){return x + y;},0); 
// => 6

相同

<$ p $ ((0 + 1)+2)+ 3
// => 6

隐藏在中减少是这个函数

  function(x,y){return x + y; } 

看到了吗?这看起来像一个非常基本的功能,不是吗?也可以重复使用!如果你认为添加是一个很好的名字,那么你是对的!让我们看看再次减少

 函数add(x,y){return x + y; } 
[1,2,3] .reduce(add,0);
// => 6

超级也很容易遵循。我可以随时回到那个代码并知道发生了什么。



我知道你在想什么

  1 + 2 + 3 

看起来非常像

  a•b•c 

也许如果我们从compN中提取reduce迭代器,我们可以简化compN的定义...



这是我们原来的 compN

  //原始
函数compN(fs){
return fs.reduce(function(g,f){
return function(x){
返回g(f(x));
}
},id);
}

让我们取出迭代器并将它称为 comp

 函数comp(g,f){
return function(x){
return g(f(x));
}
}

var g = comp(triple)(plusOne); //λx=> triple(plusOne(x))
g(1); // triple(plusOne(1))
// => 6

好的,我们来看看修改过的 compN 现在

  //修订1 
函数compN(fs){
return fs.reduce(comp , ID);
}

粗糙的改进!所以我们都完成了现在呢?



Hahahahah,no。



c $ c>减少就在那里。这是一个非常有用的函数,我很确定我们可以在很多地方使用它。

  function reduce(f,i ){
return function(xs){
return xs.reduce(f,i);
}
}

reduce(add,0)([1,2,3]); // => 6
reduce(comp,id)([a,b,c]); // => λx=> id(a(b(c(x))))

最后一行应该是一个明显的提示对于我们的 compN 函数的下一次修订

  // revision 2 
函数compN(fs){
return reduce(comp,id)(fs);
}

这看起来并不比修订好1 ...



杜,我知道!但肯定你会在每一行的末尾看到悬挂的(fs),对吗?



你不会写这个吧?

  //无用包装
函数max(x){
return Math。 MAX(X);
}

max(3,1);
// => 3

Duh!这与

  var max = Math.max; 
max(3,1);
// => 3




所以我提出最后的陈述......




  //简述
函数reduce(f,i){
return function xs){
return xs.reduce(f,i);
};
}

函数id(x){return x; }

函数comp(g,f){
返回函数(x){
return g(f(x));
};
}

//修订版3
var compN = reduce(comp,id);

那仍然是一样的吗?



heck yes it does!

 函数triple x){
return x * 3;
}

函数plusOne(x){
return x + 1;
}

var g = compN([triple,triple,plusOne]); //λx=> id(triple(triple(plusOne(x))))
g(1); // id(triple(triple(plusOne(1))))
// => 18

但为什么这会更好? strong>



好吧,这很简单。当然我们有更多的代码,但现在我们有 4 可重用函数,而不仅仅是 1 。每个功能都有一个简单的任务,很容易立即识别。与原始函数相比,我们最终获得的代码更多是声明性 。这在下面会进一步强调......






我不会在这里潜入太深,但是 ES6 让我们惊喜不已。

  / /与上述相同的功能
let id = x => X;
let reduce =(f,i)=> xs => xs.reduce(F,I);
let comp =(g,f)=> x =>克(F(X));
让compN = reduce(comp,id);

//你的函数
let triple = x => x * 3;
let plusOne = x => x + 1;

//尝试一下!
let g = compN([triple,triple,plusOne]);
console.log(g(1));

// => 18

请将它粘贴到 Babel REPL 看看它是否有效



这就是全部,伙计们!


I'm learning functional programming and I wonder if there is a way to "combine" functions like this:

function triple(x) {
    return x * 3;
}
function plusOne(x) {
    return x + 1;
}
function isZero(x) {
    return x === 0;
}
combine(1); //1
combine(triple)(triple)(plusOne)(1); // 10
combine(plusOne)(triple)(isZero)(-1); // true

If the para is a function, it "combines" the function into itself, and if not it will return the final result. Thanks!

解决方案

This concept comes from lovely Maths. It is called function composition.

      f(x) = y
      g(y) = z

   g(f(x)) = z

  (g•f)(x) = z

That last line is read "g of f of x equals z". What's great about composed functions is the elimination of points. Notice in g(f(x)) = z we take an x input and get a z output. This completely skips of the intermediate y. Here we say we removed the point y.

So we have a composed function (g•f)(x) = z, let's set that to h(x).

h(x) = (g•f)(x) = z

Since (g•f) is already a function, we can remove another point, x

h(x) = (g•f)(x) = z
   h = (g•f)

Function composition is a great way to create higher-order functions and keep your code nice and clean. It's easy to see why we'd want this in your Javascript.

"Let's make our own composition function !"

OK, you sound really excited. Here we go...

function triple(x) {
    return x * 3;
}

function plusOne(x) {
    return x + 1;
}

function id(x) { return x; }

function compN(funcs) {
  return funcs.reduce(function(g, f) {
    return function(x) {
      return g(f(x));
    }
  }, id);
}


var g = compN([triple, triple, plusOne]);
// g(x) = (triple • triple • plusOne)(x)

g(1);
//=> 18

Evaluation

triple(triple(plusOne(1)))
triple(triple(2))
triple(6)
18


If you're happy with that answer, good. For those that want to explore building better higher-order functions, please proceed!

Taking this concept of building higher-order functions out of other functions, we can refactor and expand upon our definition of compN

First let's understand how compN is evaluating things. Let's say we want to compose 3 functions, a, b, and c

//  g(x) = a(b(c(x)));
//  g(x) = (a•b•c)(x)
//  g    = (a•b•c)
var g = compN([a,b,c])

Now, compN will call reduce on the array and initialize it with our id function (for reasons obvious a little later)

The way reduce works is it will call this inner function once per item in our array

function(   g,   f) {   return function(x) { g(f(x)); }; }

I've purposely add whitespace to have it line up with this table below

iteration   g     f     return              explanation
----------------------------------------------------------------------------
#1          id    a     λx => g(f(x))       original return value
                        λx => id(a(x))      substitute for `g` and `f`
                        a'                  we'll call this "a prime"

#2          a'    b     λx => g(f(x))       original return value
                        λx => a'(b(x))      substitute for `g` and `f`
                        b'                  we'll call this "b prime"

#3          b'    c     λx => g(f(x))       original return value
                        λx => b'(c(x))      substitute for `g` and `f`
                        c'                  we'll call this "c prime"
                                            >> c' is the final return value <<

So when we call compN([a,b,c]), c' is the function that gets returned to us.

"Well what happens when we call that function with an arguement, such as c'(5) ?"

alias           original          x         return
----------------------------------------------------------------------------
c'(5);          λx => b'(c(x))    5         b'(c(5))
b'(c(5))        λx => a'(b(x))    c(5)      a'(b(c(5)))
a'(b(c(5)))     λx => id(a(x))    b(c(5))   id(a(b(c(5))))  <-- final return value

So in other words...

compN([a,b,c])(5) === id(a(b(c(5))));

"That's fricken sweet, but that was a little hard for my brain to follow."

OK, I agree, and if you're like me, when I return to this code in 3 months, I'm going to be scratching my head wondering what the hell it does.

So to start improving compN, let's first revist

//  g(x) = a(b(c(x)));
//  g(x) = (a•b•c)(x)
//  g    = (a•b•c)
var g = compN([a,b,c])

If we look at another example, maybe we'll get a hint

[1,2,3].reduce(function(x,y) { return x + y; }, 0);
//=> 6

Is the same as

((0 + 1) + 2) + 3
//=> 6

Hiding right in the middle of that reduce is this function

function (x,y) { return x + y; }

See it? That looks like a pretty basic function, doesn't it? And reusable too! If you're thinking add is a good name for it, you'd be right! Let's look at that reduce again

function add(x,y) { return x + y; }
[1,2,3].reduce(add, 0);
//=> 6

That's super easy to follow, too. I could come back to that code at any time and know exactly what's going on.

I know what you're thinking

1 + 2 + 3

Looks an awful lot like

a • b • c

"Maybe if we extract the reduce iterator from compN, we can simplify compN's definition..."

Here's our original compN

// original
function compN(fs) {
  return fs.reduce(function(g, f) {
    return function(x) {
      return g(f(x));
    }
  }, id);
}

Let's take out the iterator and call it comp

function comp(g, f) {
  return function(x) {
    return g(f(x));
  }
}

var g = comp(triple)(plusOne);    // λx => triple(plusOne(x))
g(1);                             //       triple(plusOne(1))
//=> 6

OK, so let's see the revised compN now

// revision 1
function compN(fs) {
  return fs.reduce(comp, id);
}

"Gnarly improvement ! So we're all done now ?"

Hahahahah, no.

Look at that reduce just sitting there. That's a very useful function, and I'm pretty sure we could use that in tons of places

function reduce(f, i) {
  return function(xs) {
    return xs.reduce(f, i);
  }
}

reduce(add, 0)([1,2,3]);     //=> 6
reduce(comp, id)([a, b, c]); //=> λx => id(a(b(c(x))))

That last line should be an obvious hint for the next revision to our compN function

// revision 2
function compN(fs) {
  return reduce(comp, id)(fs);
}

"That doesn't look much better than revision 1..."

Duh, I know! But surely you see the dangling (fs) at the end of each line, right?

You wouldn't write this, right?

// useless wrapper
function max(x) {
  return Math.max(x);
}

max(3,1);
//=> 3

Duh! That's the same as

var max = Math.max;
max(3,1);
//=> 3

So I present the final reivision...

// recap
function reduce(f, i) {
  return function(xs) {
    return xs.reduce(f, i);
  };
}

function id(x) { return x; }

function comp(g, f) {
  return function(x) {
    return g(f(x));
  };
}

// revision 3
var compN = reduce(comp, id);

"And that still works the same way?"

Heck yes it does!

function triple(x) {
    return x * 3;
}

function plusOne(x) {
    return x + 1;
}

var g = compN([triple, triple, plusOne]); // λx => id(triple(triple(plusOne(x))))
g(1);                                     //       id(triple(triple(plusOne(1))))
//=> 18

"But why is this better ?"

Well it's simple. Sure we have a little more code, but we have 4 reusable functions now instead of just 1. Each function has a simple task which is easy to identify immediately. Compared to the original function, we end up with code that is a lot more declarative than imperative. This is even further emphasized below...


Now for something really, cool. I won't dive in too deep here, but ES6 makes this amazing for us.

// identical functionality as above
let id = x => x;
let reduce = (f,i) => xs => xs.reduce(f,i);
let comp = (g,f) => x => g(f(x));
let compN = reduce(comp, id);

// your functions
let triple = x => x * 3;
let plusOne = x => x + 1;

// try it out!
let g = compN([triple, triple, plusOne]);
console.log(g(1));

//=> 18

Go ahead and paste this into a Babel REPL to see it work

And that's all, folks !

这篇关于为了“结合”函数在JavaScript中的功能方式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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