是否值得惯用编程?一个 ES6 示例 [关闭]
Posted
技术标签:
【中文标题】是否值得惯用编程?一个 ES6 示例 [关闭]【英文标题】:Is it worth idiomatic programming? An ES6 example [closed] 【发布时间】:2018-07-11 14:55:13 【问题描述】:编程是关于如何实现任何一段代码的决策。根据这些决定,代码将或多或少具有可读性、高效性、复杂性等。一个常见的决定也将或多或少地使用惯用语,即使用特定语句或您的编程语言或范式。
作为概念证明,我开发了两个用 javascript 编写的代码 sn-ps 来分析性能。目标是生成tagA|tagB|tagC
形式的字符串,其中tagX
的数量是随机的,而后缀A
、B
、C
是随机整数。而且tagX
不能重复。
第一个实现更惯用,而第二个实现更传统。接下来是每一个的代码sn-ps:
惯用语:
performance.mark('A');
let tags = new Set(Array.from(length: tagCount, () => Math.floor(Math.random() * 10)));
tags = Array.from(tags).map(x => `tag$x`).join('|');
performance.mark('B');
传统
performance.mark('C');
let tagset = new Set();
let tagstr = "";
for (let tag=0; tag<tagCount; tag++)
tagset.add(Math.floor(Math.random() * 10));
tagset.forEach((tag) =>
tagstr += 'tag' + tag + '|'
);
tagstr = tagstr.slice(0, -1);
performance.mark('D');
为了衡量我使用Performance Timing API的性能
结果有点令人惊讶,至少对我来说。请注意,传统方式比其他方式高效得多。
Idiomatic: 0.436535
Traditional: 0.048177
我已经重复了几次实验,结果相似
惯用的方式看起来很酷且更短,但效率较低且难以阅读。
你怎么看?是否值得使用惯用编程而不是传统编程?是否有一个普遍的答案,或者它强烈依赖于每种情况?
代码可读性和复杂性如何?
已编辑:tagX
不能重复。
已编辑:仅供参考,我已将完整代码上传至 Github:https://github.com/aecostas/benchmark-js-looping
【问题讨论】:
为什么不简单使用:let tags = Array.from(length: tagCount, () => 'tag' + Math.floor(Math.random() * 10)).join('|');
因为tagX
不能重复。我已经编辑了帖子以明确这一点。无论如何,看起来 Array.from
在 CPU 使用方面非常昂贵,而不是经典的 for
我想你已经回答了这个问题。它的效率较低并且难以阅读。我认为您应该始终首先考虑可读性,如果性能成为问题,也许可以将代码更改为更难阅读的内容。如果这段代码既没有性能也没有可读性,并且它唯一的(?)好处是它看起来很酷,那么使用它的唯一正当理由是,如果你是一个 14 岁的脚本小子并且想给一些朋友留下深刻印象。
就个人而言,我认为这个问题更适合软件工程或代码审查,而不是堆栈溢出。提出的问题主要针对意见
这里的“更惯用”是什么意思?这些都不是你在实践中想要写任何东西的方式,而且你的基准测试也很可疑。您是否将这些作为同一个脚本的一部分运行,并且只运行一次? tagCount
是什么?现实吗?
【参考方案1】:
惯用方式看起来很酷且更短,但效率较低且难以阅读。
如果熟悉该语言的人觉得难以阅读,这不是“惯用语”。 (您可能会使用这个词的一个定义——“使用许多惯用语”——但这并不是人们提到惯用代码时的意思,尽管 Array.from(length, fn)
确实是一个惯用语¹ 用于基于 JavaScript 中的函数填充数组。相反,它是按照语言用户期望的方式编写的代码。)你可以通过命名一些东西来更接近它:
const tagNumbers = Array.from(
length: tagCount,
() => Math.floor(Math.random() * 10),
);
const uniqueTagNumbers = Array.from(new Set(tagNumbers));
const tagString =
uniqueTagNumbers
.map(n => 'tag' + n)
.join('|');
并利用实践中存在的实用函数类型:
import generate from '…';
import unique from '…';
const getRandomTag = () =>
Math.random() * 10 | 0;
const tags =
generate(tagCount, getRandomTag);
const tagString =
unique(tags)
.map(n => 'tag' + n)
.join('|');
不过,您只能使用玩具示例。
至于性能:并非所有内容都必须进行微优化。你这里的两个例子具有相同的渐近时间和空间复杂度,这对于许多情况来说已经足够了。毕竟,您使用 JavaScript 编写代码是因为它的语言特性可以让您以性能为代价更好地表达自己²。当涉及到优化时,您可能想要编写更准确的基准测试(这在 Node 中非常困难!)——例如尝试重新排序这些测量值。
¹ 是一种语言用户知道的表达方式,意思是表达本身没有清楚地传达这一点。² 或因为其他人或其他群体的人认为,现在你被困在他们的代码上,或者可能是出于某种原因,而不是表现力本身,或者前面提到的人在同一条船上³,...³哦,一个成语
【讨论】:
感谢您介绍渐近时间和空间复杂度概念!它们很好地补充了我的问题以上是关于是否值得惯用编程?一个 ES6 示例 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章