javascript运算符_让我们尝试使用JavaScript中的函数生成器和管道运算符

javascript运算符

Discover Functional JavaScript was named one of the best new Functional Programming books by BookAuthority!

“发现功能JavaScript”BookAuthority评为最佳新功能编程书籍之一

A generator is a function that returns the next value from the sequence each time it is called.

生成器是一种函数,每次调用时都会从序列中返回下一个值。

Combining functional generators with the pipeline operator and pure functions with intention revealing names, allows to write code in a more expressive manner, without creating intermediary lists:

将函数生成器与管道运算符以及具有意图显示名称的纯函数结合使用,可以以更富表现力的方式编写代码,而无需创建中介列表:

import { sequence, filter, map, take, toList } from "./sequence";

const filteredTodos =
  sequence(todos) 
  |> filter(isPriorityTodo) 
  |> map(toTodoView)
  |> take(10)  
  |> toList;

Let’s see how.

让我们看看如何。

I’ll start with a simple functional generator that gives the next integer each time is called. It starts from 0.

我将从一个简单的函数生成器开始,该函数生成器在每次调用时给出下一个整数。 从0开始。

function sequence() {
  let count = 0;
  return function() {
    const result = count;
    count += 1;
    return result;
  }
}

const nextNumber = sequence();
nextNumber(); //0
nextNumber(); //1
nextNumber(); //2

nextNumber() is an infinite generator. nextNumber() is also a closure function.

nextNumber()是一个无限生成器。 nextNumber()也是一个关闭函数。

有限发电机 (Finite generator)

Generators can be finite. Check the next example where sequence() creates a generator that returns consecutive numbers from a specific interval. At the end of the sequence it returns undefined:

生成器可以是有限的。 检查下一个示例,其中sequence()创建一个生成器,该生成器从特定间隔返回连续的数字。 在序列末尾,它返回undefined

function sequence(from, to){
 let count = from;
 return function(){
   if(count< to){
      const result = count;
      count += 1;
      return result;
    }
  }
}

const nextNumber = sequence(10, 15);
nextNumber(); //10
nextNumber(); //12
nextNumber(); //13
nextNumber(); //14
nextNumber(); //undefined

toList() (toList())

When working with generators, we may want to create a list with all the values from the sequence. For this situation, we need a new function toList() that takes a generator and returns all the values from the sequence as an array. The sequence should be finite.

当使用生成器时,我们可能想创建一个包含序列中所有值的列表。 对于这种情况,我们需要一个新函数toList() ,它使用一个生成器并将序列中的所有值作为数组返回。 顺序应该是有限的。

function toList(sequence) {
  const arr = [];
  let value = sequence();
  while (value !== undefined) {
    arr.push(value);
    value = sequence();
  }
  return arr;
}

Let’s use it with the previous generator.

让我们将其与上一个生成器一起使用。

const numbers = toList(sequence(10, 15));
//[10,11,12,13,14]

管道运营商 (The pipeline operator)

A pipeline is a series of data transformations where the output of one transformation is the input of the next one.

管道是一系列数据转换,其中一个转换的输出是下一个转换的输入。

The pipeline operator |> enables us to write data transformations in a more expressive way. The pipeline operator provides syntactic sugar over function calls with a single argument. Consider the next code:

管道运算符|>使我们能够以更具表现力的方式编写数据转换。 管道运算符通过单个参数为函数调用提供语法糖。 考虑下一个代码:

const shortText = shortenText(capitalize("this is a long text"));

function capitalize(text) {
  return text.charAt(0).toUpperCase() + text.slice(1);
}

function shortenText(text) {
  return text.substring(0, 8).trim();
}

With the pipeline operator the transformation can be written like this:

使用管道运算符,转换可以这样编写:

const shortText = "this is a long text" 
  |> capitalize 
  |> shortenText;
  //This is

At this moment the pipeline operator is experimental. You can try it using Babel:

目前,管道运营商正在试验中。 您可以使用Babel尝试一下:

  • in package.json file add the babel pipeline plugin:

    package.json文件中,添加babel管道插件:

{
  "dependencies": {
    "@babel/plugin-syntax-pipeline-operator": "7.2.0"
  }
}
  • in the .babelrc configuration file add:

    .babelrc配置文件中添加:

{
  "plugins": [["@babel/plugin-proposal-pipeline-operator", {
             "proposal": "minimal" }]]
}

发电机超过收藏 (Generators over collections)

In Make your code easier to read with Functional Programming I had an example of processing a list of todos . Here is the code:

使用函数式编程使代码更易于阅读中,我有一个处理todos列表的示例。 这是代码:

function isPriorityTodo(task) {
  return task.type === "RE" && !task.completed;
}

function toTodoView(task) {
  return Object.freeze({ id: task.id, desc: task.desc });
}

const filteredTodos = todos.filter(isPriorityTodo).map(toTodoView);

In this example, the todos list goes through two transformations. First a filtered list is created, then a second list with the mapped values is created.

在此示例中, todos列表经历了两次转换。 首先创建一个过滤列表,然后创建第二个具有映射值的列表。

With generators, we can do the two transformations and create only one list. For this, we need a generator sequence() that gives the next value from a collection.

使用生成器,我们可以执行两个转换并仅创建一个列表。 为此,我们需要一个生成器sequence() ,它提供集合中的下一个值。

function sequence(list) {
  let index = 0;
  return function() {
    if (index < list.length) {
      const result = list[index];
      index += 1;
      return result;
    }
  };
}
filter()和map() (filter() and map())

Next, we need two decorators filter() and map(), that work with functional generators.

接下来,我们需要两个装饰filter()map() ,它们与函数生成器一起工作。

filter() takes a generator and creates a new generator that only returns the values from the sequence that satisfies the predicate function.

filter()一个生成器并创建一个新的生成器,该生成器仅返回满足谓词功能的序列中的值。

map() takes a generator and creates a new generator that returns the mapped value.

map()一个生成器并创建一个新的生成器,该生成器返回映射的值。

Here are the implementations:

这里是实现:

function filter(predicate) {
  return function(sequence) {
    return function filteredSequence() {
      const value = sequence();
      if (value !== undefined) {
        if (predicate(value)) {
          return value;
        } else {
          return filteredSequence();
        }
      }
    };
  };
}

function map(mapping) {
  return function(sequence) {
    return function() {
      const value = sequence();
      if (value !== undefined) {
        return mapping(value);
      }
    };
  };
}

I would like to use these decorators with the pipeline operator. So, instead of creating filter(sequence, predicate){ } with two parameters, I created a curried version of it, that will be used like this: filter(predicate)(sequence). This way, it works nicely with the pipeline operator.

我想将这些装饰器与管道运算符一起使用。 因此,我没有使用两个参数来创建filter(sequence, predicate){ } ,而是创建了它的咖喱版本,将其使用如下: filter(predicate)(sequence) 。 这样,它可以与管道运算符很好地协同工作。

Now that we have the toolbox, made of sequence, filter, map and toList functions, for working with generators over collections, we can put all of them in a module ("./sequence"). See below for how to rewrite the previous code using this toolbox and the pipeline operator:

现在,我们有了由sequencefiltermaptoList函数组成的工具箱,用于与集合上的生成器一起使用,我们可以将所有工具箱都放在一个模块中( "./sequence" )。 有关如何使用此工具箱和管道运算符重写以前的代码的信息,请参见下文:

import { sequence, filter, map, take, toList } from "./sequence";

const filteredTodos =
  sequence(todos) 
  |> filter(isPriorityTodo) 
  |> map(toTodoView) 
  |> toList;

Here is a performance test measuring the difference between using array methods and using functional generators. It seems that the approach with functional generators is 15–20% slower.

这是一项性能测试,用于衡量使用数组方法和使用函数生成器之间的差异。 似乎使用函数发生器的方法要慢15–20%。

减少() (reduce())

Let’s take another example that computes the price of fruits from a shopping list.

让我们再举一个例子,该例子从购物清单中计算水果的价格。

function addPrice(totalPrice, line){
   return totalPrice + (line.units * line.price);
}

function areFruits(line){
   return line.type === "FRT";
}

let fruitsPrice = shoppingList.filter(areFruits).reduce(addPrice,0);

As you can see, it requires us to create a filtered list first and then it computes the total on that list. Let’s rewrite the computation with functional generators and avoid the creation of the filtered list.

如您所见,它要求我们首先创建一个过滤列表,然后再计算该列表上的总数。 让我们用函数生成器重写计算,避免创建过滤列表。

We need a new function in the toolbox: reduce(). It takes a generator and reduces the sequence to a single value.

我们需要在工具箱中添加一个新函数: reduce() 。 它需要一个生成器并将序列减少为单个值。

function reduce(accumulator, startValue) {
  return function(sequence) {
    let result = startValue;
    let value = sequence();
    while (value !== undefined) {
      result = accumulator(result, value);
      value = sequence();
    }
    return result;
  };
}

reduce() has immediate execution.

reduce()立即执行。

Here is the code rewritten with generators:

这是用生成器重写的代码:

import { sequence, filter, reduce } from "./sequence";

const fruitsPrice = sequence(shoppingList) 
  |> filter(areFruits) 
  |> reduce(addPrice, 0);
采取() (take())

Another common scenario is to take only the first n elements from a sequence. For this case we need a new decorator take(), that receives a generator and creates a new generator that returns only the first n elements from the sequence.

另一种常见情况是仅从序列中获取前n元素。 对于这种情况,我们需要一个新的装饰器take() ,它接收一个生成器并创建一个新生成器,该生成器仅返回序列中的前n元素。

function take(n) {
  return function(sequence) {
    let count = 0;
    return function() {
      if (count < n) {
        count += 1;
        return sequence();
      }
    };
  };
}

Again, this is the curried version of take() that should be called like this: take(n)(sequence).

同样,这是take()的咖喱版本,应这样调用: take(n)(sequence)

Here is how you can use take() on an infinite sequence of numbers:

这是如何在无限的数字序列上使用take()

import { sequence, toList, filter, take } from "./sequence";

function isEven(n) {
  return n % 2 === 0;
}

const first3EvenNumbers = sequence()  
  |> filter(isEven) 
  |> take(3) 
  |> toList;
  //[0, 2, 4]

I remade the previous performance test and use take() to process only the first 100 items. It turns out that the version with functional generators is a lot faster (like 170 times faster).

我重新进行了之前的性能测试,并使用take()仅处理前100个项目。 事实证明,带有函数发生器的版本要快得多(例如快170倍)。

let filteredTodos = todos
 .filter(isPriorityTodo)
 .slice(0, 100)
 .map(toTodoView);
//320 ops/sec

let filteredTodos =
const filteredTodos =
  sequence(todos) 
  |> filter(isPriorityTodo) 
  |> map(toTodoView)
  |> take(100)
  |> toList;
//54000 ops/sec

定制发电机 (Custom generators)

We can create any custom generator and use it with the toolbox and the pipeline operator. Let’s create the Fibonacci custom generator:

我们可以创建任何自定义生成器,并将其与工具箱和管道运算符一起使用。 让我们创建斐波那契自定义生成器:

function fibonacciSequence() {
  let a = 0;
  let b = 1;
  return function() {
    const aResult = a;
    a = b;
    b = aResult + b;
    return aResult;
  };
}

const fibonacci = fibonacciSequence();
fibonacci();
fibonacci();
fibonacci();
fibonacci();
fibonacci();

const firstNumbers = fibonacciSequence()  
  |> take(10) 
  |> toList;
  //[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

结论 (Conclusion)

The pipeline operator makes data transformation more expressive.

管道运算符使数据转换更具表现力。

Functional generators can be created over finite or infinite sequences of values.

可以在有限或无限的值序列上创建函数生成器。

With generators we can do list processing without creating intermediary lists at each step.

使用生成器,我们可以执行列表处理,而无需在每个步骤中创建中间列表。

You can check all the samples on codesandbox.

您可以在codeandbox上检查所有样本。

Discover Functional JavaScript was named one of the best new Functional Programming books by BookAuthority!

发现功能JavaScript被称为 BookAuthority最好的新功能编程书籍

For more on applying functional programming techniques in React take a look at Functional React.

有关在React中应用函数式编程技术的更多信息,请查看 Functional React

Learn functional React, in a project-based way, with Functional Architecture with React and Redux.

通过带有React和Redux的功能架构 ,以基于项目的方式学习功能性React

Follow on Twitter

在Twitter上关注

翻译自: https://www.freecodecamp.org/news/lets-experiment-with-functional-generators-and-the-pipeline-operator-in-javascript-520364f97448/

javascript运算符

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值