函数组合的可读性
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 有一组函数可以处理嵌套属性。在您的情况下,我们可以结合 pathEq
和 any
:
-
如果给定路径上的属性等于给定值,
pathEq
将返回 true
。
any
将谓词函数应用于列表的每个元素,直到满足为止。
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
【讨论】:
非常好!比我的无点解决方案好得多!以上是关于函数组合的可读性的主要内容,如果未能解决你的问题,请参考以下文章