正在等待被锤的忐忑心理中。
1
eason1874 2021-03-06 17:23:02 +08:00
不太懂。是循环展开的意思吗?话说现在 JS 不是都要编译了吗,这个在编译时不会自动优化吗?
|
2
hupo0 OP @eason1874 应该不叫循环展开,是把多次循环合成一个。只见过 c++ 和 rust 有这类优化,而且对代码有不少限制,可能高级语言要考虑闭包什么的。
|
3
cyberpoint 2021-03-06 18:06:33 +08:00
比如程序员为了方便快捷就会写这种代码:const foo = bar.filter(...).map(...).reduce(...)
这样就会有 3 次遍历,但是其实这些...的过程是可以在一次遍历中完成的 |
4
muzuiget 2021-03-06 18:12:47 +08:00
明明就是写法问题,这种 [...xxx].map(xxx).filter(xxx).slice(xxx) 我为什么不直接用 for,也是一次循环。
|
5
kkocdko 2021-03-06 18:12:58 +08:00
是这样的,我记得 rust 的迭代器有这种优化
|
6
hupo0 OP @cyberpoint 表达到位
|
7
Jirajine 2021-03-06 18:13:24 +08:00 via Android 1
这个应该不是语言的优化吧,只要方法实现上不返回另一个数组,而是返回一个 lazy evaluation 的 iterator 就可以链式调用多次只遍历一次了。
|
8
muzuiget 2021-03-06 18:14:36 +08:00
谁白了这种链式求值,就是方便而已,不在乎这点性能损失,真遇到巨大数组,就应该专门写优化代码。
|
9
supermao 2021-03-06 18:17:22 +08:00
这种不需要猜,你自己做个 benchmark 不就出来了?
|
12
hupo0 OP @Jirajine iterator 转成另一个 iterator,一般是包多一层,该做的“结束检查”还是不会漏的,还是会有额外的性能损耗。实际用 benchmark 测也会发现比较慢。
|
13
jones2000 2021-03-06 19:35:50 +08:00 1
太高深了, 看不懂, 学了 2 年 js, 只会用 for(var i=0;i<data.length;++i) .......
|
14
musi 2021-03-06 20:02:53 +08:00 1
纠正一下,这不叫比“ js 原生数组方法要快”
js 可没让你本来遍历一次的事用三次遍历完成 |
15
geelaw 2021-03-06 20:06:38 +08:00 via iPhone
这个问题和数组遍历几遍没关系,主要区别在于内存分配。
Array.map 每次都要分配新的数组,可以想象 no-stream 的 map 只是变换迭代器,当然快。当然这不能怪 Array,毕竟功能不同,要写出值得比较的代码可以对 mf 复合自己 map_count 次,再变换成 reducer,然后直接在 data 数组上用 reduce 。 而且好好写 for 循环不香吗? |
16
kuunnnn 2021-03-06 21:01:46 +08:00 1
这不就是延迟计算吗,lazy.js 有做这个的,lodash 好像也有类似的
|
17
shyangs 2021-03-06 22:45:25 +08:00
為什麼不用 lodash 的_.chain()
|
18
molika 2021-03-06 23:12:16 +08:00 via iPhone
transduce 而已 clojure/clojureScript N 年前就这么玩了 话说 js 的 map reduce 啥的竟然不是惰性的 transduce ? 吃惊
|
19
hupo0 OP @shyangs lodash 没用过,我才知道 chain 。看了下源码,他用一个 Wrapper 的抽象,在求值前,把中间的 actions 合成一个。虽然感觉大家想做的事情一样,但实现上效率不那么高。不过 lodash 自带的东西多,容错好,本身不用太注重效率。
实际 benchmark 跑下来,速度和数组自带的方法差不多,但是内存上应该会更优越。 |
20
hupo0 OP |
22
civet 2021-03-07 00:59:17 +08:00 via iPhone
@molika js 不是天生就是为函数式编程服务的,而是社区各路大神引领了这种潮流,实际上就是底层没实现,写法先出现
|
23
Rocketer 2021-03-07 01:58:30 +08:00 via iPhone
js 聊性能的意义不是特别大,毕竟主要用途是前端,数组大小不会超过 100 条。相比较而言,节约代码提高开发效率更受欢迎。
真要用在后端处理大数据,那还是像 Python 那样用 C 做类库,让 js 调用吧,纯 js 再努力也提升不了太多 |
24
darknoll 2021-03-07 11:59:43 +08:00
没有意义,前端无需注重效率,数据量大了应该是分页之类的,反正 Iterable 对象我都用 for of,我也知道 for 循环速度最快。
|
25
wxsm 2021-03-07 23:27:09 +08:00 via iPhone
从算法角度来说,1 次循环,3 次循环,n 次循环,时间复杂度都是 O(n),并没有实质区别。
|
26
cenbiq 2021-03-08 12:27:49 +08:00
不禁感慨,C#的 Linq 真是有远见
|
27
hupo0 OP @cenbiq 看看所谓的 Zero cost abstraction - 刘雨培的文章 - 知乎
https://zhuanlan.zhihu.com/p/24975048 吐槽 C# - 刘雨培的文章 - 知乎 https://zhuanlan.zhihu.com/p/30653282 参考这两篇回答,17 年的 C#还没有对 linq 进行优化。虽然用迭代器也是相当于只遍历一遍,但是迭代器“调用”迭代器的过程还是会有额外的性能损耗。完美一点的是,能做到跟 C++一样,把迭代器 yield 的逻辑内联到一个循环里。 想来 no-stream 的做法,是函数调用层面,把"yield"部分通过函数包函数的方式合并到一个循环里。虽然也是有函数调用方面的开销,但目前来看,会比 JS 的 Generator 还节省性能些。 从现有的 C++和 Rust 的案例来看,其实正道还是 iterator,似乎这样的结构更容易优化。linq 也是在正道上,只是需要引擎对这部分进行优化。与之对应的是 JS 的 Generator 。可惜就是目前他们还很拉胯。 |