函数组合的可读性

Posted

技术标签:

【中文标题】函数组合的可读性【英文标题】:Readability for function composition 【发布时间】:2021-10-28 15:20:45 【问题描述】:

我正在尝试使用 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);

这两种尝试都取得了正确的结果,这个问题只是从函数式编程的角度提出的,因为我对这个领域完全陌生,想知道哪种尝试比另一种更有利,希望这个问题不会因为基于意见而被关闭,我真诚地认为这不是基于意见,而是在 FP 世界中寻求工业实践。

除了我所做的两次尝试之外,还可以随意提供任何替代方案,谢谢!

【问题讨论】:

看起来你的 get 函数是 Ramda 的 prop 函数。 函数式编程是关于函数做一件没有副作用的事情,不管你的函数有多复杂。因此,如果您使用多个抽象并不重要。只要您的功能有唯一目的。任何范式都鼓励简单,所以如果你能用最少的层数做同样的事情而不会失去可读性,那么你就是可靠的。 【参考方案1】:

至少在我看来,您的两种解决方案都显得有点矫枉过正。

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

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

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

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

【讨论】:

【参考方案2】:

将 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 在 Ramda 中内置为 prop,您的 names 可以写为 map (path (['author', 'name']))

我们可以自行或使用http://pointfree.io/ 之类的工具来实现这一点。它可能看起来像这样:

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

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

【讨论】:

【参考方案3】:

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

    如果给定路径上的属性等于给定值,pathEq 将返回 trueany 将谓词函数应用于列表的每个元素,直到满足为止。

isAuthor 函数首先接受名称,然后返回一个接受列表的函数:

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

【讨论】:

非常好!比我的无点解决方案好得多!

以上是关于函数组合的可读性的主要内容,如果未能解决你的问题,请参考以下文章

什么是更 Pythonic - 函数组合、lambdas 或其他东西? [关闭]

初始函数

团队作业

函数式编程[4]functor和monad

写出规范化的高可读性的函数代码注释

方法理论学习