Java 语言的出现是为了降低 C++ 的开发成本和学习难,但也将 C++ 一些非常有价值的特性搞丢了,运算符重载就是一个非常优秀的特性,但 Java 搞丢了,虽然运算符重载不是一个常用特性,但随着数据分析领域的发展,表达式的编译也就有了强烈的需求。
首先,我解释一下为什么需要运算符重载,Java 是一个严谨的逻辑性编程语句,有丰富的工程化集成能力,通常 Java 编译后的代码只是 JVM 中运行,但也会在其它的可执行单元中运行,常见的有:数据库(关系型、对象型、KV 型和预计算引擎等)、也可通过是一次远程服务调用。倘若这类可执行单元的协议中存在表达式(算术、比较和逻辑),这也就意味着在 Java 中需要通过字符串的形式表现这类表达式,此时就会出现下列的代码:
目标输出协议:
"(sum(order.no) + 100) > 1000 and order.id < 999"
构造协议的 Java 代码:
and(gt(plus(funCall("sum", "order.no"), 100)), lt(fieldCall("order", "id"), 999));
对比一下将计算表达式转换为 Java 中的方法调用,变得极难理解,但这种方式也是目前大都 ORM 框架所使用的方式,这样做的方式可以使得构造过程动态化,而不是简单的字符串拼接,当然其它内部原理还是字符串拼接。
在这样的使用场景下,如果 Java 的表达式支持重载则会变的非常简单,下面是 ObjectiveSQL 的代码:
Order.Table order = Order.asTable();
Expression expr = (sum(order.no) + 100) > 1000 && order.id < 999;
其中的Expression
是实现了 Java 运算符重载,内部有种数学计算、比较和逻辑计算的相关方法,最终也是通过拼接字符串的形式输出上述目标协议。
详细可参考: https://github.com/braisdom/ObjectiveSql
如果认可项目,请点个赞,欢迎交流...
1
Braisdom OP 有兴趣的,可以交流
|
2
lewis89 2020-12-04 14:03:18 +08:00 1
重载运算符是一个优秀的特性???????老哥不是开玩笑吧
operate * operate + operate = 犹如噩梦啊,再配合 拷贝构造 引用 指针 指针的引用 右值引用 左值引用 不把脑子搞炸那还是 C++? |
3
Braisdom OP 没有运算符重载者是恶梦的开始
|
4
wysnylc 2020-12-04 14:13:35 +08:00
|
5
xzenge 2020-12-04 14:26:32 +08:00
为什么不直接用 C++呢
|
6
no1xsyzy 2020-12-04 14:31:27 +08:00
@lewis89 使下层变复杂的同时使上层变得清晰。
Python 也能重载运算符(__add__ 等魔法方法),并且 NumPy 时常依赖重载 如果运算符重载没有使上层更清晰,那肯定是不需要重载瞎 JB 重载。 |
7
PVG 2020-12-04 14:33:55 +08:00
scala 在这一块做得很好,你可以为所欲为
|
8
lewis89 2020-12-04 14:41:11 +08:00 1
@no1xsyzy #6 有的时候 选择太多并不是一件好事,C++为什么被称为 horrible 的语言 就是因为选择太多了,一个 option 的选择有 3 个 另一个 option 有 3 个 再加上一个 option 的选项有 7 个 3 * 3 * 7 = 42 种选项.. 脑子都会炸
|
9
no1xsyzy 2020-12-04 14:46:18 +08:00
@wysnylc Smalltalk 是目前公认无二的最符合 OOP 思想的语言,操作符只是一种特殊的信号,可以通过 double dispatch 来重构行为
OOP 的 “代码与对象 / 类绑定” 的想法甚至比函数式更允许运算符重载(天然带 single dispatch )。 跟 OOP 没关系,但具体什么原因我就不清楚了,可能是严谨性的因素。 |
13
no1xsyzy 2020-12-04 14:51:24 +08:00
@lewis89 3*3*7=63 你的算数是体育老师教的罢!(ry
滥用功能与语言没关系…… checked exception 不也被 throws Exception 了…… |
14
Braisdom OP @no1xsyzy Smalltalk 是面向对象的鼻祖,很多概念都被借鉴了,为什么不能发展起来,也是有它内在的原因
|
15
no1xsyzy 2020-12-04 14:53:45 +08:00
准确地说,运算符重载是一个 DSL-ish 的做法。
要不是现在的语言从抽象设计上都羸弱不堪,哪还需要运算符重载?来个宏就行了。 |
17
lewis89 2020-12-04 14:55:53 +08:00 1
@no1xsyzy #13 唉,年轻人啊就是没法理解我的梗,option 太多了 就算你数学是数学老师教的又能怎么样,人脑是有限的,大部分人 口算 心算 超过 2 个以上 出问题的概率激增,99 乘法表 两个一位数当然可以肌肉肌肉背诵,保证出错率低
4 个呢 5 个呢 只要复杂度在增加 人脑是肯定不够用的 |
19
Braisdom OP @no1xsyzy 我相信 Java 后面必定会支持运算符重载,虽然现在没有真实的项目在用,我是第一个,国外有几篇论文在讨论。
DSL 是一个很泛的概念,SQL 是一个 DSL,基于 Java 的 API 封装的 SQL 不一定属于 DSL 的范畴,因为它是一个高度抽象的语言,存在的只是一堆 Interface,基于 Interface 可以实现很多特性 |
21
Braisdom OP |
23
ximigou007 2020-12-04 15:02:19 +08:00
当你熟练掌握一个东西你就想要灵活性,当你不熟练的时候灵活性就是灾难
|
24
no1xsyzy 2020-12-04 15:02:59 +08:00
|
25
ximigou007 2020-12-04 15:03:13 +08:00
重载不重载本质上是设计哲学的冲突,总会有 trade off
|
26
no1xsyzy 2020-12-04 15:15:33 +08:00
@Braisdom #19 路线预测我就不清楚了。
所以我称为 DSL-ish 。 #0 举的例子确实发生了范式转移( paradigm shift ),从命令式的代码转换成了描述式的代码。保持 Java 的思维粗略地一看会彻底误解这段代码的作用。 #21 外部因素对语言成否的影响更大,这有统计上的实证,C - Unix,Python - Data Science,JavaScript - Web,至于 Java 跨平台方面始终不温不火,直到 Android 。 和理论上的解释,见 #18 |
27
cpstar 2020-12-04 16:38:39 +08:00
个人认为对于 OO 概念来讲,加减乘除运算符也不应该有,而是 Number.Add/Minus/Multiply/Divide,所以,重载,睡了吧
|
28
FireFoxAhri 2020-12-04 16:42:01 +08:00
可以了解一下 Scala 。。运算符也可以定义成函数
|
29
Braisdom OP @ximigou007 我觉得是个人偏好的问题,只是 Java 的创始人不喜欢运算符重载,但它想的不够远
|
30
Braisdom OP @cpstar 这是 OO 里最传统的想法,不能为了 OO 而去 OO,过于理论化的思考,而不考虑实际。
一个同环比的计算是最常见的:(sum(current) - sum(last)) / sum(last) * 100 这样的表达式用函数做就太复杂了 |
31
aguesuka 2020-12-04 18:04:17 +08:00 via Android
中缀表达式有 Monoid 之类的讲究,但是 java 没有静态检验的能力,社区也没有 plt 的气氛。java 一切皆对象,java 中的运算符都是纯函数,重载的运算符不管是实例方法还是静态函数都很尴尬。
kotlin 有中缀表达式,但是表达能力和 "实例.方法(参数,参数)"没有区别。我觉得你的需求应该自己定义语法,用 idea 的 language inject 做代码提示和静态分析。 |
32
Braisdom OP |
33
wysnylc 2020-12-04 18:10:10 +08:00
lambda 可以实现运算符重载的一切功能
|
35
namelosw 2020-12-04 18:26:31 +08:00
运算符和方法本质区别不大, 像 Ruby Scala Haskell 之类的设计其实比较优雅. 比较尴尬的是 C++那种有限重载.
a + b 就是 a.+(b) 就是 a.add(b) 运算符在命名方面不会让语言可读性变得更差, 反而是该用运算符非得用字母比如.eq()之类的可读性不好. 但是运算符最大的问题是优先级容易乱. 像 LISP 之类强制括号的话, 其实无所谓是运算符还是字母. |
36
secondwtq 2020-12-04 18:42:43 +08:00
这贴感觉开错了 ... 这标题就注定了下面必然会以 OT 为主 ...
优先级是个老问题,个人偏向于放弃优先级,强制括号 S-expr 也行,不过我习惯看中缀是改不过来了,我喜欢 S-expr 是因为——目前基本所有中缀运算符的语法,都必须硬点一部分符号给运算符用(就不说还要钦定优先级了),S-expr 只占括号和空格,其他符号都可以解放出来,甚至包括 hyphen C++、Java 的运算符重载(以及函数重载)的问题很大程度也是钦定的东西太多了 |
37
Braisdom OP |
38
ximigou007 2020-12-04 21:04:42 +08:00
@Braisdom 问题是可以随意重载运算符的话,如果使用不当会有不可预知的风险,如果不可重载,所有结果是可以预期的
|
39
Braisdom OP |
40
TheCure 2020-12-04 21:11:10 +08:00
本人 Python 程序员, 极度讨厌运算符重载
从这里面的代码看, string interpolation 可以解决一部分问题, 可以试试 kotlin |
42
namelosw 2020-12-04 23:35:18 +08:00 1
@Braisdom 我已经说了, 问题是优先级. 所以:
1. 主要问题不在操作符重载, 而在于有优先级而不强制括号才会导致你的表达式看不清. LISP 可以乱用操作符, 也没看谁搞不清, 因为跟普通函数没有任何区别. 2. 次要问题是传递性, 即使是同一个操作符优先级一样, 也有左传递右传递问题, 比如加减和赋值的传递是相反的. 写一个 C 语言的 parser 你就全理解了. |
43
Braisdom OP @namelosw 你说的很对,优先级是主要问题,是从数学延续下来的,也是目前无法改变的事实,只能用其它的方法来解决,表达式的表现力 相比 函数嵌套的表达力 还是强很多的。
所以重载在这种场景下,是必须的 |
44
1490213 2020-12-05 12:12:21 +08:00 via Android 2
我不认为加入运算符重载能带来的好处有多少,但重载操作符极大的增加了阅读代码的心智负担。这种东西本质就是有了一种新的 DSL,导致不能通过现有认知的心理模型去理解,必须重新学习或者查询,看到熟悉的符号也不能断定其意义。
另外,有了重载运算符,下一步是不是就需要搞 haskell 的自定义结合律和优先级了?学术语言或者专业语言搞搞可以理解,但是普通人使用的语言我觉得应该简化复杂度和清晰规则,而不是相反。 |
45
ruyu 2020-12-05 13:21:01 +08:00
所以 scheme 多好, scheme 表示不知道你们在争论什么
|
46
Braisdom OP |
47
ruyu 2020-12-05 15:20:20 +08:00
我觉得楼主这个挺好的, 我就歪个楼, scheme 根本不用争论运算符应不应该重载的问题, 因为它根本没有运算符 (
|
48
Braisdom OP @ruyu 诚心做事,也希望得到更多人认可,https://github.com/braisdom/ObjectiveSql 这是我的项目,感兴的点个赞
|