JavaScript美食的秘密:香料

查看以下解决相同问题的代码段,并考虑最喜欢哪一个。
这是第一个:这是第二个:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(int => isEven(int)) .filter(int => isBiggerThan(3, int)) .map(int => int + 1) .map(int => toChar(int)) .filter(char => !isVowel(char)) .join('') // 'fhjl' 
 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('') // 'fhjl' 
“我敢打赌第二个选项比第一个选项具有更好的可读性,”该材料的作者说,我们今天出版了该版本的翻译。 据他介绍,重点在于filter()map()方法的参数。



今天,我们将讨论与第一个示例类似的如何回收代码,以使其看起来像第二个示例中的代码。 本文的作者承诺,在您理解了它的工作原理之后,您将以一种新的方式与您的程序相关联,并且您将无法忽略过去看起来很正常且不需要进行改进的内容。

功能简单


考虑一个简单的sum()函数,该函数将传递给它的数字相加:

 const sum = (a, b) => a + b sum(1, 2) // 3 

我们重写它,为新函数命名为csum()

 const csum = a => b => a + b csum(1)(2) // 3 

其新版本的工作方式与原始版本完全相同,唯一的区别在于此新函数的调用方式。 即, sum()函数一次获取两个参数,而csum()一次获取一个相同的参数。 实际上,当调用csum() ,将调用两个函数。 特别是,考虑调用csum()并将数字1传递给它的情况:

 csum(1) // b => 1 + b 

csum()这种调用导致以下事实:它返回一个函数,该函数可以采用在其通常的调用期间传递给csum()的第二个数字参数,并返回在此参数上加一个的结果。 调用此函数plusOne()

 const plusOne = csum(1) plusOne(2) // 3 

处理数组


在JavaScript中,您可以使用多种特殊方法来处理数组。 假设map()方法用于将传递给它的函数应用于数组的每个元素。

例如,为了使整数数组的每个元素增加1(更确切地说,要形成一个包含原始数组的元素增加1的新数组),可以使用以下构造:

 [1, 2, 3].map(x => x + 1) // [2, 3, 4] 

换句话说,发生的情况可以描述如下:函数x => x + 1接受一个整数,并以一系列整数返回其后的数字。 使用上面讨论的plusOne()函数,可以将该示例重写如下:

 [1, 2, 3].map(x => plusOne(x)) // [2, 3, 4] 

在这里值得放慢脚步,思考正在发生的事情。 如果这样做,可以看到在考虑的情况下, x => plusOne(x)plusOne (请注意,在这种情况下,函数名称后没有方括号)是等效的。 为了更好地理解这一点,请考虑otherPlusOne()函数:

 const otherPlusOne = x => plusOne(x) otherPlusOne(1) // 2 

该函数的结果将与通过简单调用我们已知的plusOne()获得的结果相同:

 plusOne(1) // 2 

出于同样的原因,我们可以讨论以下两种构造的等效性。 这是我们已经看到的第一个:

 [1, 2, 3].map(x => plusOne(x)) // [2, 3, 4] 

这是第二个:

 [1, 2, 3].map(plusOne) // [2, 3, 4] 

另外,请记住plusOne()函数是如何创建的:

 const plusOne = csum(1) 

这使我们可以使用map()重写我们的构造,如下所示:

 [1, 2, 3].map(csum(1)) // [2, 3, 4] 

现在,使用相同的技术, isBiggerThan()函数。 如果需要,请尝试自己做,然后继续阅读。 在使用filter()方法时,这将消除不必要的构造。 首先,让我们将代码转换为这种形式:

 const isBiggerThan = (threshold, int) => int > threshold [1, 2, 3, 4].filter(int => isBiggerThan(3, int)) 

然后,除去所有多余的内容,我们获得了您在本材料开始时已经看到的代码:

 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('') // 'fhjl' 

现在,我们考虑两个简单的规则,这些规则允许您以此处讨论的样式编写代码。

规则编号1


以下两个构造是等效的:

 […].map(x => fnc(x)) […].map(fnc) 

规则编号2


始终可以重写回调以减少用于调用它的参数数量:

 const fnc = (x, y, z) => … […].map(x => fnc(x, y, z)) const fnc = (y, z) => x => … […].map(fnc(y, z)) 

如果您自己编写了isBiggerThan()函数,那么您可能已经采取了这种转换方法。 假设我们需要大于3的数字才能通过过滤器,可以这样做:

 const isBiggerThan = (threshold, int) => int > threshold […].filter(int => isBiggerThan(3, int)) 

现在,我们重写isBiggerThan()函数,以便可以在filter()方法中使用它,而不使用int=>构造:

 const isBiggerThan = threshold => int => int > threshold […].map(isBiggerThan(3)) 

锻炼身体


假设我们有以下代码片段:

 const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 keepGreatestChar('b', 'f') // 'f' //   'f'   'b' 

现在,基于keepGreatestChar()函数,创建keepGreatestCharBetweenBAnd()函数。 我们需要通过调用它,只将一个参数传递给它,同时它将传递给它的字符与字符b 。 该功能可能如下所示:

 const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const keepGreatestCharBetweenBAnd = char => keepGreatestChar('b', char) keepGreatestCharBetweenBAnd('a') // 'b' //   'b'   'a' 

现在编写greatestCharInArray()函数,该函数在reduce()数组方法中使用keepGreatestChar()函数,可让您搜索“最大”字符,不需要参数。 让我们从下面的代码开始:

 const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const greatestCharInArray = array => array.reduce((acc, char) => acc > char ? acc : char, 'a') greatestCharInArray(['a', 'b', 'c', 'd']) // 'd' 

要解决此问题,请实现creduce()函数,该函数可以在creduce()函数中使用,该函数在实际应用中允许不向其中传递任何东西,除非要在其中查找具有最大代码的字符的数组。

creduce()函数必须足够通用,以便可以解决任何需要使用标准reduce()数组方法功能的问题。 换句话说,该函数必须采用回调,初始值和要使用的数组。 因此,您应该获得一个可以使用以下代码片段的函数:

 const greatestCharInArray = creduce(keepGreatestChar, 'a') greatestCharInArray(['a', 'b', 'c', 'd']) // 'd' 

总结


也许现在您有一个问题,为什么按照此处介绍的方法处理的方法的名称以字符c开头。 c字符是curried的首字母缩写,我们讨论了curried函数如何帮助提高代码的可读性。 应当指出,这里我们并未努力严格遵守函数式编程的原则,但是,我们相信此处讨论的内容的实际应用可以使我们改进代码。 如果您对JavaScript的泛滥这个话题很感兴趣-建议您阅读本书第4章有关函数式编程的知识,并且,由于您已经达到了这一点,通常,请阅读整本书。 另外,如果您不熟悉功能编程,请查阅入门资料。

亲爱的读者们! 您是否在JavaScript开发中使用函数currying?

Source: https://habr.com/ru/post/zh-CN415059/


All Articles