函数组合的可读性

Readability for function composition

我正在尝试使用 Ramda 练习函数组合,但想知道它是否有点过头了,需要一些建议。

给出下面的对象

const articles = [
  {
    title: 'Everything Sucks',
    url: 'http://do.wn/sucks.html',
    author: {
      name: 'Debbie Downer',
      email: 'debbie@do.wn'
    }
  },
  {
    title: 'If You Please',
    url: 'http://www.geocities.com/milq',
    author: {
      name: 'Caspar Milquetoast',
      email: 'hello@me.com'
    }
  }
];

制作一个布尔函数,说明某个人是否写过任何文章。

示例函数调用

isAuthor('New Guy', articles) // should return false
isAuthor('Debbie Downer', articles) // should return true

我的解决方案

首先,我创建了一个函数来获取作者姓名,如下所示

const get = _.curry(function(x, obj) { return obj[x]; });

const names = _.map(_.compose(get('name'), get('author'))); // ['Debbie Downer', 'Caspar Milquetoast']

现在我有一个函数 names 可以使用了,我将尝试构建 isAuthor 函数,下面是我的两个尝试

尝试1:无合成

const isAuthor = function(name, articles) {
  return _.contains(name, names(articles))
};

尝试 2 组合

const isAuthor = function(name, articles) {
  return _.compose(
    _.contains(name), names
  )(articles);
}

两次尝试都得到了正确的结果,这个问题完全是从函数式编程的角度提出的,因为我是这个领域的新手,想知道哪个尝试比另一个更有利,希望这个问题不会因为意见而被关闭based,我真心认为这不是基于意见,而是寻求FP世界的行业实践。

除了我所做的两次尝试,也欢迎提供任何替代方案,谢谢!

至少在我看来,您的两种解决方案似乎都有些矫枉过正。

在不使用任何库的情况下,解决方案可以像这样简单,例如:

 const isAuthor = (name, articles) => articles.some((article) => article.author.name === name)

函数式编程很酷,但这并不意味着你应该让事情变得不必要的复杂。

从您的建议来看,第一个似乎比第二个更具可读性。

将 Ramda 视为一种工具,它可以帮助您以某种方式编写代码,而不是决定您如何编写代码的工具。 Ramda(免责声明:我是创始人之一)允许您使用漂亮的组合和管道以更具声明性的方式编写。但这并不意味着您需要在所有可能的地方使用它。

来自 tuomokar 的简单 vanilla JS 答案可能就是您所需要的,但 Ramda 确实有一些工具可以使它对某些人来说更具可读性。

所以我们可以这样写:

const isAuthor = (name) => (articles) => 
  includes (name) (map (path (['author', 'name'])) (articles))

const articles = [{title: 'Everything Sucks',url: 'http://do.wn/sucks.html', author: {name: 'Debbie Downer',email: 'debbie@do.wn'}}, {title: 'If You Please', url: 'http://www.geocities.com/milq', author: {name: 'Caspar Milquetoast', email: 'hello@me.com'}}]

console .log (isAuthor ('New Guy') (articles))
console .log (isAuthor ('Debbie Downer') (articles))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
<script>const {includes, map, path} = R                                  </script>

请注意,您的 get 作为 prop 内置于 Ramda 中,您的 names 可以写成 map (path (['author', 'name']))

我们可以自己或使用像 http://pointfree.io/ 这样的工具来消除这一点。它可能看起来像这样:

const isAuthor = compose ((o (__, map (path (['author', 'name'])))), contains)

但我发现它的可读性远不如其他任何建议。我不会打扰。

Ramda 有一组函数可以处理嵌套属性。在你的情况下,我们可以结合 pathEqany:

  1. pathEq returns true 如果给定路径的 属性 等于给定值。
  2. any 对列表的每个元素应用谓词函数,直到满足为止。

isAuthor 函数首先获取名称,然后 returns 获取列表的函数:

const {compose, any, pathEq} = R;

const isAuthor = compose(any, pathEq(['author', 'name']));

isAuthor('Debbie Downer')(articles);
//=> true

isAuthor('John Doe')(articles);
//=> false

但这不一定比:

const {curry} = R;

const isAuthor = curry((x, xs) => xs.some(y => y?.author?.name === x));
                                  
isAuthor('Debbie Downer')(articles);
//=> true

isAuthor('John Doe')(articles);
//=> false