请教关于函数式编程的优势

2022-10-01 12:08:30 +08:00
 lerefe

最近在看函数式编程相关的内容,并且结合公司一些人用函数式变成的处理,产生一个疑问,从算法复杂度和速度来说,很多时候一个循环能做到的事情,会用 lambda 循环多次处理,这样做的根据是什么?

8581 次点击
所在节点    Java
65 条回复
Mogeko
2022-10-01 22:04:04 +08:00
因为 Java 的函数式本来就是残废的。

像 Haskell 这类正统的函数式语言,默认珂里化,甚至参数都不用写全。灵活又高效。

另外函数式编程最大的爽点是无副作用所带来的心智负担的降低;以及超高的鲁棒性。在这两点好处面前,性能的些微下降不值一提。
iseki
2022-10-01 22:27:12 +08:00
第一种我必须人脑运行一次才能理解发生了什么;第二种虽然能一眼看明白在干啥,但是写的有点恶心,但我认为这是 Java 的问题,用 Kotlin 就好了
zmal
2022-10-01 22:49:33 +08:00
函数式写法很大程度上是为了增加可读性,但 op 你发的这个写法 2 个人认为挺拉的。
可读性没降低多少,时间复杂度从 n 升级到 n * log n 。
lmshl
2022-10-01 23:23:02 +08:00
写法 2 性能差不是 fp 的原因,而是楼主没能等价改写。
实际上这里应该用 foldLeft 而不是 sorted/findFirst
在 java stream api 中应该 reduce 是可以用的
这样两段代码复杂度就一样了
zmal
2022-10-01 23:26:44 +08:00
如果是我的话可能会这样写:

https://imgur.com/YBBKeIT
zmal
2022-10-01 23:46:16 +08:00
abc612008
2022-10-01 23:56:07 +08:00
来个 kotlin 版本,比 java stream 舒服很多。(吹爆 kotlin

```kotlin
data class Book(val category: String, val price: Double)

fun mostExpensiveByCategory(books: List<Book>): Map<String, Book> {
return books.groupBy { it.category }
.mapValues { (_, books) -> books.maxBy { it.price } }
}
```
msg7086
2022-10-02 00:53:14 +08:00
函数式一般可以把算法轻松地拆分成多个步骤。
第一种写法的坏处就是在任何一个时间点的数据都缺乏一致性,即一半数据处理了,一半数据没处理。
如果数据出错,你打断点拿到的也是这样一半一半的数据。
函数式这样每次跑一步出来都是完整的数据集,一眼就能找到问题点。
zjp
2022-10-02 01:35:31 +08:00
一次遍历就可以了,感觉楼上代码都被楼主带偏
每个 key 对应单值而不是集合的优先考虑 Collectors.toMap()

dataSource.stream().collect(Collectors.toMap(Book::getCategory, Function.identity(),
(o1, o2) -> o1.getPrice().compareTo(o2.getPrice()) > 0 ? o1 : o2)).values();

理论上总能用 lambda 写出一样的复杂度,只是可能有 API 的限制
GeruzoniAnsasu
2022-10-02 03:18:38 +08:00
> 从算法复杂度和速度来说

从这些角度来说函数式不一定有优势。




函数式编程的本质是定义 A 到 B 的变换映射,当映射比流程更明确时,函数式更容易写对;反之若步骤和流程比映射更明确,那么强行使用函数式风格则是下策。
zhuweiyou
2022-10-02 10:48:24 +08:00
这点数据 循环一次和循环几次没啥差别, 我的原则是能一行代码解决的 不写七八行.
zddwj
2022-10-02 20:52:32 +08:00
链式调用并不是函数式编程的专利,函数式编程的核心特征是不对变量进行重复赋值
Envov
2022-10-03 00:10:21 +08:00
函数式编程很多时候都会牺牲一定的性能,但是获得了可读性上的增强。
我个人很喜欢在 javascript 里面使用函数式,比如说组合函数,纯函数,hof 等等,但是同事都不是很认可,后来放弃了。只在自己的项目里面用。
AllenHua
2022-10-03 10:55:51 +08:00
Java 可真残废 (doge )
optional
2022-10-03 12:11:26 +08:00
可读性强,可测试性高,可并行化改造,可以从组合的维度思考问题。同时流程清晰,可以很方便的进行优化,包括语法编译层面的优化。
likunyan
2022-10-03 15:02:17 +08:00
@lerefe @july1995 不好意思,看错了 :->
git00ll
2022-10-03 22:51:58 +08:00
简单场景下,虽然第二种方式可读性更高一些。
但是因为是简单场景,完全可以给这个方法加一行注释 “按照 xxx 分组,获取每个分组内 xxx 最小的元素”
来解决可读性差的问题。

但是第二种写法的扩展性就不如第一种了,如取分组内最小的和第二小的,改起来逻辑就没这么顺了。
WispZhan
2022-10-03 23:09:02 +08:00
@lmshl zio 好用吗?
acapla
2022-10-04 04:13:32 +08:00
楼主在看哪本书啊? 可以推荐一下学习资料吗?
lmshl
2022-10-04 10:49:53 +08:00
@WispZhan
akka-stream 、zstream 、fs2 都有上生产环境,目前用下来总体感觉 zio 的模型是上手最快,最容易写的。

akka-stream 的错误处理建模会很恶心,fs2 和 zstream 差不多但是 zstream 的类型没有 fs2 那么高理解成本。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/884185

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX