由于此功能的实现非常简单,为什么它不更普遍?
我知道这可能会导致某些丑陋的代码,但过去并没有阻止语言设计者添加易于使用的有用功能(宏,三元运算符,不安全的指针)。 />实际使用案例:
实现缺少的运算符(例如Lua没有按位运算符)
模仿D的
~
(数组串联)DSLs
将
|
用作Unix管道样式语法糖(使用协程/生成器)我对允许自定义运算符的语言也很感兴趣,但我对为什么将其排除在外更感兴趣。我曾考虑过分叉一种脚本语言来添加用户定义的运算符,但是当我意识到自己没在任何地方看到它时就停下来了,所以可能有充分的理由为什么比我更聪明的语言设计师不允许这样做。 >
#1 楼
在编程语言设计中有两种截然相反的思想流派。一种是程序员在较少的限制下编写更好的代码,另一种是他们在更多的限制下编写更好的代码。在我看来,现实是,经验丰富的程序员可以不受限制地发展壮大,但是这些限制可以使初学者的代码质量受益。初学者完全糟糕的代码。因此,您的语言是否包括它们取决于您的语言设计师的思想流派。评论
关于这两种思想流派,plus.google.com/110981030061712822816/posts/KaSKeg4vQtz也+1,因为他们对语言设计者为什么选择一个而另一个选择了最直接(也可能最正确)的答案。我还要说的是,根据您的论点,您可以推断出更少的语言可以使用它,因为一小部分开发人员比其他人精通高技术(按定义,任何事物的最高百分比将始终是少数)
–吉米·霍法(Jimmy Hoffa)
2012-12-29 21:27
我认为这个答案含糊不清,与这个问题仅是微不足道的。 (我也偶然认为您的描述是错误的,因为它与我的经验相矛盾,但这并不重要。)
–康拉德·鲁道夫(Konrad Rudolph)
2012年12月30日12:03
正如@KonradRudolph所说,这并不能真正回答问题。采用Prolog之类的语言,它可以让您对运算符进行任何操作,包括在插入中缀或前缀时定义其优先级。我认为您可以编写自定义运算符与目标受众的技能水平没有任何关系,而是因为Prolog的目标是尽可能逻辑地阅读。包含自定义运算符使您可以编写逻辑上读取的程序(毕竟,序言程序只是一堆逻辑语句)。
–大卫·考登(David Cowden)
13年1月1日于22:35
我怎么想念那个Stevey rant?噢,man
–乔治·莫尔(George Mauer)
13年1月7日在20:05
如果我认为如果语言为程序员提供工具,使他们能够说出编译器何时应该进行各种推断(例如,对Format方法的自动装箱参数)以及何时应该拒绝的工具,那么我会陷入哪种哲学? (例如,自动装箱ReferenceEquals的参数)。语言能力越强,程序员可以说出何时某些推论是不合适的,那么在适当的时候,它越安全地提供方便的推论。
–超级猫
2014年2月24日23:40在
#2 楼
给定在使用〜或“ myArray.Concat(secondArray)”连接数组之间的选择,我可能更喜欢后者。为什么?因为〜是一个完全无意义的字符,仅具有其含义-数组串联的含义-在编写该字符的特定项目中给出。正如您所说,基本上,运算符与方法没有什么不同。但是,虽然可以为方法赋予可读性,易懂的名称,从而加深了对代码流的理解,但运算符却不透明且处在特定情况。或Haskell或OCaml中的大多数运算符,尽管在这种情况下,一些针对功能语言的通用标准正在出现。
评论
所有运营商都可以这样说。是什么使它们良好,也可以使用户定义的运算符(如果明智地定义了)也变得良好:尽管用于它们的字符只是一个字符,但广泛使用和可能的助记符属性立即在源代码中使其含义对于熟悉它的人来说很明显。因此,您目前的答案并不是说服恕我直言的。
–user7043
2012-12-29 18:38
正如我在最后提到的那样,某些运算符具有通用识别性(例如标准的算数符号,按位移位,AND / OR等),因此其简洁性超过了其不透明性。但是,能够定义任意运算符似乎使这两个方面都变得最糟。
– Avner Shahar-Kashtan
2012年12月29日在18:44
因此,您也赞成禁止在语言级别使用单字母名称吗?更笼统地说,是不是不允许使用任何看起来弊大于利的语言?这是一种有效的语言设计方法,但不是唯一的方法,因此我认为您不能以它为前提。
–user7043
2012-12-29 18:45
我不同意自定义运算符正在“添加”功能,它消除了限制。运算符通常只是函数,因此可以使用动态的,与上下文相关的符号代替运算符的静态符号表。我想这就是运算符重载的处理方式,因为+和<<当然不是在对象上定义的(在C ++的裸类上执行此操作时,我得到“在...中没有运算符+的匹配项”)。
–beatgammit
2012-12-29 20:09
需要很长时间才能弄清楚,编码通常不是要让计算机为您做某事,而是要生成一个人和计算机都可以阅读的文档,而人比计算机要重要得多。这个答案是完全正确的,如果下一个人(或您在2年后)甚至要花10秒钟来试图找出〜的含义,那么您可能还应该花10秒钟来键入方法调用。
– Bill K
2012年12月30日20:06在
#3 楼
由于此功能的实现非常简单,为什么它不更普遍?
您的前提是错误的。这并不是“实施起来很简单”。实际上,它带来了很多问题。
让我们看看帖子中建议的“解决方案”:
没有优先级。作者本人说:“不使用优先规则根本不是一种选择。”
语义感知解析。如文章所述,这将要求编译器具有很多语义知识。这篇文章实际上并没有提供解决方案,让我告诉您,这根本不是一件小事。编译器的设计旨在权衡复杂性。特别地,作者提到了收集相关信息的预解析步骤,但是预解析效率低下,编译器竭尽全力使解析次数最小化。
没有自定义的infix运算符。好吧,这不是解决方案。
混合解决方案。该解决方案具有语义感知解析的许多(但不是全部)缺点。特别是,由于编译器必须将未知标记视为可能代表自定义运算符,因此它通常无法产生有意义的错误消息。它还可能需要定义该运算符以继续进行解析(以收集类型信息等),这又需要额外的解析过程。
总而言之,这是实施昂贵的功能,无论是在解析器的复杂性还是在性能方面,目前尚不清楚是否会带来很多好处。当然,定义新运算符的功能有一些好处,但即使是那些有争议的(只要看看其他答案就认为拥有新运算符不是一件好事)。
评论
感谢理智的声音。我的经验表明,认为琐碎事的人要么是专家,要么是幸福无知,并且在后一类中更常见:x
– Matthieu M.
2012-12-30 17:37
这些问题中的每一个都已经以20年的语言解决了...
– Philip JF
2012年12月31日下午4:38
但是,实现起来非常琐碎。忘掉所有关于龙书的解析算法,这是21世纪,是时候继续前进了。甚至扩展语言语法本身也很容易,并且添加给定优先级的运算符很简单。例如,看一下Haskell解析器。对于“主流”肿的语言而言,它比解析器要简单得多。
– SK-logic
2012年12月31日上午9:39
@ SK-logic您的笼统断言并没有说服我。是的,解析已经开始。不,根据类型实现任意运算符优先级并非“琐事”。在有限的上下文中生成良好的错误消息并非“轻而易举”。用需要多次转换的语言来生产高效的解析器是不可行的。
–康拉德·鲁道夫(Konrad Rudolph)
13年1月4日在16:23
在Haskell中,解析是“容易的”(与大多数语言相比)。优先级与上下文无关。给您带来严重错误消息的部分与类型类和高级类型系统功能有关,而不与用户定义的运算符有关。这是一个难题,而不是用户定义,这是重载。
– Philip JF
2013年1月4日23:26
#4 楼
现在,让我们忽略整个“运算符被滥用以损害可读性”的论点,而将注意力集中在语言设计的含义上。中缀运算符比简单的优先级规则具有更多的问题(尽管直率,链接您引用的内容简化了该设计决策的影响)。一种是解决冲突:定义
a.operator+(b)
和b.operator+(a)
会发生什么?一个优先于另一个会导致破坏该算子的预期交换性质。引发错误可能导致原本可以正常工作的模块一起损坏。当您开始将派生类型扔进混合中时会发生什么?事实是,运算符不仅仅是函数。函数可以是独立的,也可以归其类所有,这些函数明确提供了拥有多态调度的参数(如果有)。
这忽略了操作员引起的各种包装和分辨率问题。语言设计人员(总体上)限制中缀运算符定义的原因是因为它在为语言提供了可争议的好处的同时给语言造成了很多问题。
坦率地说,因为实现起来并不容易。 br />
评论
我相信这就是Java不包含它们的原因,但是包含它们的语言对于优先级有明确的规则。我认为这类似于矩阵乘法。它不是可交换的,因此在使用矩阵时必须要知道。我认为在处理类时也是如此,但是是的,我确实同意拥有非交换+是邪恶的。但这是否真的反对用户定义的运算符?这似乎是一个反对操作符重载的论点。
–beatgammit
2012年12月29日在20:27
@tjameson-是的,语言确实有明确的优先顺序和数学规则。如果运算符因boost :: spirit之类的操作而过载,则数学运算的优先级规则可能不会真正适用。一旦允许用户定义的运算符,情况就会恶化,因为没有很好的方法甚至可以很好地定义数学优先级。我在一种语言的背景下写了一些关于我自己的文章,该语言专门用于解决任意定义的运算符的问题。
– Telastyn
2012年12月29日20:37
先生,我叫废话。 OO贫民窟之外有生命,并且功能不一定属于任何对象,因此找到合适的功能与找到合适的操作员完全相同。
– Matthieu M.
2012年12月30日17:35
@matthieuM。 -当然可以在不支持成员功能的语言中,所有权和调度规则更加统一。但是到了那时,原始问题变成了“为什么非OO语言不是更普遍?”这是另外一回事。
– Telastyn
2012-12-30 18:20
您是否看过Haskell定制运算符的方式?它们的工作原理与正常功能完全相同,不同之处在于它们还具有关联的优先级。 (实际上,普通函数也是如此,因此即使它们之间也没有什么真正的区别。)基本上,默认情况下,运算符为infix,名称为前缀,但这是唯一的区别。
– Tikhon Jelvis
2012-12-30 18:53
#5 楼
我想您会惊讶于以某种形式实现操作符重载的频率。但是它们在很多社区中并不常用。为什么使用〜连接到数组?为什么不像Ruby那样使用<<?因为您使用的程序员可能不是Ruby程序员。还是D程序员。那么,当他们遇到您的代码时,他们会怎么做?他们必须去查找该符号的含义。
我曾经和一个非常优秀的C#开发人员一起工作,他对功能语言也很感兴趣。突然,他开始通过扩展方法和使用标准monad术语将monad引入C#。一旦知道了它的含义,就不会有人质疑他的某些代码更简洁,甚至更具可读性,但这确实意味着每个人都必须在代码有意义之前学习monad术语。
足够,您认为?这只是一个小团队。我个人不同意。每个新开发人员注定会对此术语感到困惑。另一方面,我会很高兴在C#中使用
??
运算符,因为我希望其他C#开发人员知道它是什么,但是我不会将其重载为默认情况下不支持的语言。评论
我不理解您的Double.NaN示例,此行为是浮点规范的一部分,并且在我使用的每种语言中都受支持。您是说不支持自定义运算符,因为它会使不了解该功能的开发人员感到困惑吗?这听起来像是反对使用三元运算符或您的??的相同论点。例。
–beatgammit
2012年12月29日在20:20
@tjameson:实际上,您是对的,这与我要提出的观点有点相切,并且写得不是特别好。我想到了??我写那个例子时喜欢这个例子。删除double.NaN段落。
– pdr
2012年12月29日在20:24
我认为您的“每个新开发人员注定会被此术语所迷惑”的观点有点乏味,对我来说,担心新开发人员对代码库的学习曲线与担心新开发人员对代码库的学习曲线一样.NET的新版本。我认为更重要的是,当开发人员学习它(新的.NET或您的代码库)时,它有意义并显示出价值吗?如果是这样,那么学习曲线是值得的,因为每个开发人员只需要学习一次,尽管代码需要处理无数次,因此,如果对代码进行改进,则花费很小。
–吉米·霍法(Jimmy Hoffa)
2012-12-29 21:08
@JimmyHoffa这是经常性费用。为每位新开发人员支付预付款,并影响现有开发人员,因为他们必须提供支持。同时还有文档成本和风险元素-今天感觉足够安全,但是30年来,每个人都将继续前进,语言和应用程序现在都是“旧版”,文档是一大堆蒸蒸日上的东西那些懒得不能键入“ .concat()”的程序员的聪明才智会给他留下一些可怜的傻瓜。期望值是否足以抵消成本?
–西尔维德拉格
2012年12月30日在6:25
同样,“每个新开发者注定会被这种术语所混淆。我们在学习新领域时是否没有足够的问题?”可能早在80年代就已应用于OOP,之前的结构化编程或10年前的IoC。现在是时候停止使用这种谬论了。
–毛里西奥·谢弗(Mauricio Scheffer)
2014年3月28日在16:52
#6 楼
我可以想到一些原因:实现起来并不容易-允许任意的自定义运算符会使您的编译器更加复杂,尤其是在允许用户定义优先级,可修复性的情况下和团结规则。如果简单是一种优点,那么运算符重载将使您远离良好的语言设计。
它们被滥用-大多数程序员认为重新定义运算符并开始为各种自定义类重新定义它们是“很酷的”。不久之后,您的代码中到处都是大量的自定义符号,其他任何人都无法阅读或理解,因为操作员没有遵循易于理解的常规规则。我不赞成使用“ DSL”这一论点,除非您的DSL恰好是数学的一个子集:-)
它们会损害可读性和可维护性-如果经常忽略操作员,那么在使用此功能时可能很难发现被使用,编码人员被迫不断问自己操作员在做什么。给出有意义的函数名称要好得多。输入一些额外的字符很便宜,长期的维护问题也很昂贵。
它们可能破坏隐含的性能预期。例如,我通常希望在数组中查找元素为
O(1)
。但是使用运算符重载时,根据索引运算符的实现,someobject[i]
可能很容易成为O(n)
操作。实际上,与仅使用常规函数相比,运算符重载具有合理的用途。一个合理的例子可能是设计一个复数类,以供数学家使用,他们理解为复数定义数学运算符的众所周知的方法。但这确实不是一个很常见的情况。
要考虑一些有趣的情况:
Lisps:通常根本不区分运算符和函数-
+
只是一个常规函数。您可以随意定义函数(通常有一种在单独的命名空间中定义函数的方法,以避免与内置的+
冲突),包括运算符。但是在文化上倾向于使用有意义的函数名,因此不会被滥用。另外,在Lisp中,前缀表示法倾向于专门使用,因此运算符重载提供的“语法糖”中的值较小。Java-不允许运算符重载。有时候这很烦人(对于像复数的情况这样的东西),但是平均而言,对于Java来说,这可能是正确的设计决策,旨在作为一种简单的通用OOP语言。由于这种简单性,对于中低技能的开发人员来说,Java代码实际上非常容易维护。
C ++具有非常复杂的运算符重载。有时这会被滥用(有人吗?),但考虑到C ++作为一种复杂语言的定位,这种方法是有意义的,该语言可以进行高级编程,同时仍然使您与性能非常接近,因此例如编写一个复杂的数字类,该类的行为完全符合您的要求,而不会影响性能。据了解,用脚射击自己是您的责任。
#7 楼
由于此功能的实现非常简单,所以为什么它不更常见?
实现起来并不容易(除非实现得很简单)。即使以理想的方式实现,它也不会给您带来太多好处:简洁带来的可读性增益被陌生和不透明带来的可读性损失所抵消。用户的时间。
这就是我可以想到的三种语言,它们以不同的方式来实现:
球拍,方案,当不是所有S-expression-y都允许并且希望您为要扩展的任何语法编写相当于解析器的内容(并提供有用的钩子以使其易于处理)。
Haskell,纯函数编程语言,允许定义仅由标点符号组成的任何运算符,并允许您提供固定性级别(可用10个级别)和关联性。三元运算符等可以用二进制运算符和高阶函数来创建。在同一程序中被定义为运算符,但其词法分析器,解析器和评估器都因此紧密耦合。
评论
您已经说过它“不平凡”,并立即列出了三种语言,其中包含一些极其琐碎的实现。所以,毕竟这是很琐碎的,不是吗?
– SK-logic
2013年1月2日,9:22
#8 楼
不鼓励使用自定义运算符的主要原因之一是,因为这样任何运算符都可以意思/可以做任何事情。例如,cstream
备受批评的左移重载。语言允许运算符重载,通常鼓励使运算符行为与基本行为保持相似以避免混淆。
用户定义的运算符也使解析变得困难得多,尤其是在还存在自定义首选项的情况下规则。
评论
我看不到有关运算符重载的部分如何应用于定义全新的运算符。
–user7043
2012年12月29日19:01
我已经看到很好地使用了运算符重载(线性代数库),但是正如您提到的那样,它非常糟糕。我认为这不是反对自定义运算符的好理由。解析可能是一个问题,但是当期望有运算符时,这很明显。解析后,由代码生成器查找运算符的含义。
–beatgammit
2012年12月29日19:49
这与方法重载有何不同?
–Jörg W Mittag
2012年12月29日20:19在
@JörgWMittag带有方法重载(大部分时间)都附加了有意义的名称。带有符号,更难以简洁地解释会发生什么
–棘轮怪胎
2012年12月29日23:39
@ratchetfreak:好吧。 +加两件事,-相减,*相乘。我的感觉是,没有人强迫程序员使函数/方法添加实际上添加任何东西,而noNothing可能会引发核武器。而且a.plus(b.minus(c.times(d))。times(e)的可读性比a +(b-c * d)* e(额外的奖励-开头的错误是转录错误)少我不知道首先如何更有意义...
– Maciej Piechotka
2012年12月30日19:15
#9 楼
我们不使用用户定义的运算符,原因与我们不使用用户定义的单词相同。没有人会称其功能为“ sworp”。向他人传达您的想法的唯一方法是使用共享语言。这意味着您要为其编写代码的社会必须同时知道单词和符号(运算符)。因此,您在编程语言中使用的运算符就是我们所教的在学校(算术)或在编程社区中建立的算术运算符,例如布尔运算符。
评论
此外,还可以发现功能背后的含义。对于“ sworp”,您可以很容易地将其粘贴到搜索框中并找到其文档。让操作员陷入搜索引擎是一个完全不同的球场。我们当前的搜索引擎只是在寻找运算符,而在某些情况下,我浪费了大量时间来寻找含义。
– Mark H
2012年12月31日在1:22
您算多少“学校”?例如,我认为ε表示elem是一个好主意,当然每个人都应该理解一个运算符,但是其他人似乎不同意。
– Tikhon Jelvis
2012-12-31 2:03
这不是符号的问题。这是键盘的问题。例如,我使用启用了Unicode美化器的emacs haskell-mode。并且它会自动将ascii运算符转换为unicode符号。但是,如果没有ascii等效项,我将无法键入。
–Vagif Verdi
2012年12月31日上午8:32
自然语言仅由“用户定义的单词”构成,而没有其他内容。实际上,某些语言确实鼓励自定义单词的形成。
– SK-logic
13年1月2日,9:24
#10 楼
至于确实支持这种重载的语言:Scala可以,实际上C ++可以用一种更干净,更好的方式。大多数字符都可以在函数名称中使用,因此您可以根据需要定义!+ * = ++之类的运算符。内置了对infix的支持(所有带有一个参数的函数)。我认为您也可以定义此类功能的关联性。但是,您不能定义优先级(仅使用丑陋的技巧,请参阅此处)。#11 楼
尚未提及的一件事是Smalltalk的情况,其中所有内容(包括运算符)都是消息发送。+
,|
等“运算符”实际上是一元方法。所有方法都可以被覆盖,因此,如果
a + b
和a
都是整数,则b
表示整数加法,如果它们是整数,则表示矢量加法都OrderedCollection
。没有优先级规则,因为这些只是方法调用。这对标准数学符号具有重要意义:
3 + 4 * 5
表示(3 + 4) * 5
,而不是3 + (4 * 5)
。(这是Smalltalk新手的主要绊脚石。违反数学规则将删除一个特殊情况,以便进行所有代码评估统一从左到右,使语言更加简单。)
#12 楼
您在这里与两件事作斗争:为什么运算符首先存在于语言中?
运算符在函数/方法上的优点是什么?
在大多数语言中,运算符并没有真正实现为简单功能。它们可能具有一些功能支架,但是编译器/运行时明确意识到其语义含义以及如何将其有效地转换为机器代码。即使与内置函数相比,这也更为正确(这就是为什么大多数实现在其实现中也不包括所有函数调用开销的原因)。大多数运算符是对CPU中原始指令的更高级别的抽象(这就是为什么大多数运算符是算术,布尔或按位运算的部分原因)。您可以将它们建模为“特殊”功能(称它们为“原始”或“内建”或“本机”或其他),但是要做到这一点通常需要一组非常强大的语义来定义此类特殊功能。另一种选择是使内置运算符在语义上看起来像用户定义的运算符,但在编译器中调用特殊路径。这与第二个问题的答案不符...
除了上面提到的机器翻译问题外,在语法层面上,运算符与功能并没有什么不同。它们与众不同的特征往往是简洁和象征性的,这暗示着它们必须有用的显着附加特征:它们必须对开发人员具有广泛理解的含义/语义。简短的符号不会传达太多的含义,除非它是已经理解的语义集合的简写形式。这使用户定义的运算符本质上无济于事,因为就其本质而言,它们未被广泛理解。它们与一个或两个字母函数名称一样有意义。
C ++的运算符重载为对此进行检查提供了沃土。大多数运算符重载“滥用”都以重载的形式出现,这些重载破坏了一些广为人知的语义协定(经典示例是运算符+的重载,例如a + b!= b + a,或者其中+修改了其中一个如果您查看Smalltalk,它确实允许运算符重载和用户定义的运算符,那么您会发现一种语言可能如何实现这一目标以及它的用处。在Smalltalk中,运算符只是具有不同语法属性的方法(即,它们被编码为中缀二进制)。该语言对特殊的加速运算符和方法使用“原始方法”。您发现几乎没有创建任何用户定义的运算符,而且当它们创建时,它们往往没有像作者可能打算使用的那样被广泛使用。甚至等同于运算符重载的情况也很少见,因为将新函数定义为运算符而不是方法主要是净损失,因为后者允许表达该函数的语义。
#13 楼
我一直发现C ++中的运算符重载对于单个开发人员来说是一个方便的快捷方式,但是从长远来看,这会引起各种混乱,这仅仅是因为方法调用以不容易的方式被“隐藏”了为了使诸如doxygen之类的工具脱颖而出,人们需要理解这些习语才能正确使用它们。有时,甚至比您期望的要难得多。曾几何时,在一个大型的跨平台C ++项目中,我认为通过创建一个FilePath
对象(类似于Java的File
对象)来规范构建路径的方式是一个好主意,该对象将具有operator /在其上连接另一个路径部分(因此您可以执行File::getHomeDir()/"foo"/"bar"
之类的操作,并且它将在我们所有受支持的平台上执行正确的操作)。看到它的每个人都会说:“到底是什么?字符串分割?...哦,这很可爱,但我不相信它会做正确的事情。” 类似地,有一个在图形程序设计或其他发生矢量/矩阵数学运算的领域中,很多情况很容易发生,例如Matrix * Matrix,Vector * Vector(点),Vector%Vector(叉),Vector * Vector(叉),Matrix * Vector(矩阵变换), Matrix ^ Vector(忽略均匀坐标的特殊情况的矩阵变换-对表面法线很有用),依此类推,但是虽然为编写矢量数学库的人节省了一些解析时间,但最终只会使问题变得混乱更多给别人。只是不值得。
#14 楼
运算符重载是一个坏主意,其原因与方法重载是一个坏主意的原因相同:屏幕上的相同符号根据其周围的内容而具有不同的含义。这使得休闲阅读更加困难。由于可读性是可维护性的关键方面,因此应始终避免过载(在某些非常特殊的情况下除外)。每个符号(无论是运算符还是字母数字标识符)最好具有自己站立的独特含义。
举例说明:在阅读不熟悉的代码时,如果遇到不知道的新字母数字标识符,至少您会拥有不知道的优势。然后,您可以查找它。但是,如果看到知道其含义的通用标识符或运算符,则您不太可能注意到它实际上已经被重载以具有完全不同的含义。要知道哪些操作符已被重载(在广泛使用了重载的代码库中),即使您只想阅读其中的一小部分,也需要掌握完整代码的使用知识。这将使新开发人员无法加快该代码的开发速度,也很难使人们从事少量工作。这可能对程序员的工作安全性有好处,但是如果您对代码库的成功负责,则应不惜一切代价避免这种做法。
由于运算符的大小较小,因此重载运算符将允许更密集的代码,但是使代码密集并不是真正的好处。具有两倍逻辑的行需要两倍的读取时间。编译器不在乎。唯一的问题是人类可读性。由于使代码紧凑不会提高可读性,因此紧凑性并没有真正的好处。继续并占用空间,为唯一的操作赋予唯一的标识符,从长远来看,您的代码将更加成功。
评论
“屏幕上的相同符号根据周围的含义而具有不同的含义”-大多数语言的许多操作员都已经遇到了这种情况。
–rkj
13年7月27日在2:01
#15 楼
我认为处理优先级和复杂的解析存在技术难题,我认为必须考虑编程语言的一些方面。运算符通常是简短的逻辑结构,可以很好地定义和记录以核心语言(比较,分配)。如果没有文档,通常也很难理解它们(例如,将
a^b
与xor(a,b)
比较)。在正常编程中,实际上可能有意义的操作符数量非常有限(>,<,=,+等)。 我的想法是,最好坚持使用一种语言定义的一组明确的运算符-然后允许运算符重载这些运算符(给出的软性建议是,这些运算符应执行相同的操作,但建议
您的
~
和|
用例实际上可以通过简单的运算符重载(C#,C ++等)实现。 DSL是有效的使用区域,但可能是唯一有效的区域之一(从我的角度来看)。但是,我认为有更好的工具可以在其中创建新语言。通过使用任何一种编译器工具,在另一种语言中执行真正的DSL语言并不困难。 “扩展LUA参数”也是如此。一种语言很可能主要是为了以特定方式解决问题而定义的,而不是作为子语言的基础(存在例外)。#16 楼
另一个原因是,使用可用的运算符定义操作并不总是那么简单。我的意思是,是的,对于任何类型的数字,“ *”运算符都是有意义的,并且通常以语言或现有模块来实现。但是,对于需要定义的典型复杂类(例如ShipingAddress,WindowManager,ObjectDimensions,PlayerCharacter等),其行为尚不清楚...向地址添加或减去数字是什么意思?当然,您可以定义将字符串添加到ShippingAddress类意味着自定义操作,例如“在地址中替换第1行”(而不是“ setLine1”函数),并添加一个数字是“替换邮政编码”(而不是“ setZipCode”),但是这样的代码不太易读和令人困惑。我们通常认为运算符被用在基本类型/类中,因为它们的行为是直观,清晰和一致的(至少一旦您熟悉该语言)。考虑诸如Integer,String,ComplexNumbers等类型。
因此,即使在某些特定情况下定义运算符可能非常有用,但它们的实际实现还是非常有限的,因为99%的基本语言包中已经实现了明显的优势。
评论
关于这个问题,Reddit正在进行讨论。@dimatura:我对R不太了解,但自定义运算符似乎仍然不太灵活(例如,没有适当的方法来定义固定性,范围,重载等),这解释了为什么它们使用不多的原因。在其他语言中则有所不同,当然在Haskell中,后者大量使用自定义中缀运算符。具有合理的自定义中缀支持的过程语言的另一个示例是Nimrod,当然Perl也允许它们。
还有另一个选择,Lisp实际上在运算符和函数之间没有区别。
尚未提及Prolog,这是另一种语言,其中运算符只是函数的语法糖(甚至是数学运算符),并且允许您定义具有自定义优先级的自定义运算符。
@ BeardedO,Lisp中根本没有infix运算符。一旦介绍了它们,就必须优先处理所有此类问题。