以下是关于解决此问题的可能方法的讨论。对于这么简单的问题,这不是我可能会做的,但它展示了一种思维方式,可以更轻松地解决许多问题,并在你这样做时重用你的工作。
解决问题
您想在这里做几件事,因此将问题分解为多个步骤可能会有所帮助。
从根本上说,您想要颠倒某些单词。那么为什么不写一个函数来做到这一点呢?应该很简单。这是一个版本:
const reverseWord = word => word.split('').reverse().join('')
reverseWord('word') //=> 'drow'
但您只想对长度至少为 5 的单词执行此操作。我们可以使用它来编写更复杂的函数,而不是重写我们现有的函数:
const reverseIfGte5 = word => word.length >= 5 ? reverseWord(word) : word
reverseIfGte5('word') //=> 'word'
reverseIfGte5('supercalifragilisticexpialidocious')
//=> 'suoicodilaipxecitsiligarfilacrepus')
注意这里,我们写reverseIfGte5 使用 reverseWord。所以我们的reverseWord 函数也可以根据我们的选择被重用,而且每个函数都在做一些简单的事情。
我们现在可以用这个函数写spinWord,通过
const spinWords = sentence => sentence.split(/\s+/).map(reverseIfGte5).join(' ')
spinWords('Hey fellow warriors') //=> 'Hey wollef sroirraw'
这符合预期。这可能是一个停下来的好地方。不过……
每个函数只做一件事
我们上面的spinWords函数负责两件事:
理想情况下,一个函数应该只负责一件事。 (当然,在决定如何分解这些责任时需要做出一些判断。)
将其分开可能会很有用。我们可以通过不同的方式做到这一点。这是一个简单的:
const updateWords = wordFn => sentence => sentence.split(/\s+/).map(wordFn).join(' ')
const spinWords = updateWords(reverseIfGte5)
现在我们有了一个可重用的函数,我们可以使用它,例如,一个明显的 capitalizeFirstLetter 函数,如下所示:
const titleCase = updateWords(capitalizeFirstLetter)
titleCase('Hey fellow warriors') //=> 'Hey Fellow Warriors')
修复错误
我们的代码现在看起来像这样:
const updateWords = wordFn => sentence => sentence.split(/\s+/).map(wordFn).join(' ')
const reverseWord = word => word.split('').reverse().join('')
const reverseIfGte5 = word => word.length >= 5 ? reverseWord(word) : word
const spinWords = updateWords(reverseIfGte5)
这些函数从最有可能被重用到最不可能被重用。
请注意,这里的总代码,当使用这样的可重用函数时,比这个普通版本要长一点:
const spinWords = sentence => sentence.split(/\s+/).map(
word => word.length >= 5 ? word.split('').reverse().join('') : word
).join(' ')
但我们的版本有几个优点。显然,可重用性是其中之一。但另一个问题是,将问题分解成小块后,如果我们发现问题,我们就知道去哪里寻找解决方案。
你猜怎么着,这个解决方案实际上有一个潜在的错误。
如果我们用'Hey fellow warriors' 调用这个函数,我们会返回'Hey wollef oirraw' 而不是预期的'Hey wollef sroirraw'。我们的间距已关闭。
因为这个问题被分解成不同的功能,所以毫无疑问我们需要更改哪个功能才能解决这个问题。显然,它是负责将句子拆开并重新组合在一起的功能,updateWords。有一个简单的解决方法,从
const updateWords = wordFn => sentence => sentence.split(/\s+/).map(wordFn).join(' ')
到
const updateWords = wordFn => sentence => sentence.split(' ').map(wordFn).join(' ')
但我们最好使用来自CertainPerformance的答案的变体,并像这样重写它:
const updateWords = wordFn => sentence => sentence.replace(/\w+/g, wordFn)
这修复了错误,实际上处理的案例比我们最初处理的要多。 (注意它现在也处理标点符号):
spinWords('Hey, fellow warriors!') //=> 'Hey, wollef sroirraw!'
重要的一点是,为了修复我们的错误,我们能够隔离负责的函数并在不更改任何其他内容的情况下对其进行更改。
工作代码
这就是我们得到的结果:
const updateWords = wordFn => sentence => sentence.replace(/\w+/g, wordFn)
const reverseWord = word => word.split('').reverse().join('')
const reverseIfGte5 = word => word.length >= 5 ? reverseWord(word) : word
const spinWords = updateWords(reverseIfGte5)
console.log(spinWords('Hey, fellow warriors!'))
我们将问题分解为两个相当可重用的函数(reverseWord 和 updateWords),以及两个非常针对我们的问题的函数(reverseIfGte5 和 spinWords。)每个都有明确定义的职责,并且它们很容易单独测试。
进一步扩展
这是我可能会使用这样的功能。但是因为我的个人库经常包含一个函数when,它已经是其中一个的泛化,所以有时我可能更愿意在此基础上进行构建:
const when = (cond, then) => val => cond(val) ? then(val) : val
const reverseIfGte5 = when(word => word.length >= 5, reverseWord)
我创建了when,因为有时我想在某些条件为真时使用我的输入的更改版本,而在条件为假的情况下使用原样。这正是reverseIfGte5 需要做的事情,因此在when 之上构建它很有用。
这就是实用程序库的构建方式。几个具体问题有明显的联系,写了更通用的解决方案来处理它们。如果这些这样的解决方案足够通用,那么它们就是包含在您的个人库或团队库中的候选者。如果它们对更广泛的受众有用,它们可能会被包含在通用实用程序库中。
我是Ramda 的作者之一,这是一个用于 JS 函数式编程的实用程序库,而这正是它的构建方式。