您在任何编程语言中都面临的最大设计缺陷是什么?[关闭]


29

所有编程语言都有其设计缺陷,这仅仅是因为没有一种语言像大多数(所有其他)事物一样是完美的。除此之外,在您作为程序员的历史上,哪种编程语言的设计错误最让您感到烦恼?

请注意,如果一种语言是“不好的”,仅仅是因为它不是针对特定事物而设计的,那不是设计缺陷,而是设计的功能,因此,请不要列出这种烦人的语言。如果一种语言不适合其设计目的,那当然是设计中的缺陷。实施特定的事物和内幕事物也不重要。


6
请注意,如果有人想问这个问题有多建设性,这对语言设计师是有建设性的(避免错误)。
安托


1
@greyfade:并非如此,这与实际语言中的缺陷有关,似乎与降低语言采用率有关,其中可能包括不良的标准库或该语言的不良网站。一些答案列出了例如错误的语法,但这并不是特定的设计缺陷
Anto

8
任何编程语言中最大的缺陷?人类。
乔尔·埃瑟顿

如果这些答案是最大的缺陷,那么编程语言会让我印象深刻。
汤姆·哈特芬

Answers:


42

我最大的烦恼之一是switch如果您忘记使用C语言派生的案例,默认情况下会陷入下一个案例break。我知道这在非常低级的代码(例如Duff's Device)中很有用,但通常不适用于应用程序级代码,并且是编码错误的常见来源。

我记得大约在1995年,当我第一次阅读有关Java的细节时,当我谈到有关该switch语句的部分时,我非常失望,因为他们保留了默认的默认行为。这只是用另一个名字而switch荣耀goto


3
@Christopher Mahan:switch不必那样做。例如,Ada的case / when语句(等效于switch / case)不具有穿透行为。
Greg Hewgill'3

2
@Greg:switch与C无关的语言中的类似语句不必那样工作。但是如果你使用C风格的控制流({... }for (i = 0; i < N; ++i)return等),语言推理会使人想到switch要像C的工作,并给予它的Ada /帕斯卡尔/ BASIC样的语义会迷惑人。C#由于相同的原因需要breakin switch语句,尽管通过禁止静默失败来减少出错的可能性。(但我希望您能写些文章fall;而不是丑陋的文章goto case。)
dan04'3

4
然后不要写switch,写if()else。您抱怨的原因是使转换更好的原因:这不是对/错条件,而是数字条件,这使它与众不同。您也可以编写自己的switch函数。
11

9
-1我认为允许跌倒是有好处的,除了愚蠢之外没有其他危险,但是它提供了其他功能。
2011年

11
问题不在于switch 允许失败。大多数情况下,fallthrough都不是故意的。
dan04 2011年

41

我从未真正喜欢使用C派生的语言=进行赋值和==相等性测试。混乱和错误的可能性过高。而且甚至不要让我开始使用===Javascript。

更好的是:=分配和=平等测试。语义本来可以与今天的语义完全相同,其中赋值是一个也会产生值的表达式。


3
@Nemanja Trifunovic:我最初想到的是该建议,但不幸的是,C语言中的歧义与负数(即x<-5)相比小于。C程序员不会容忍这种必需的空格:)
Greg Hewgill 2011年

7
@Greg:我更喜欢:===因为忘记了这太容易了,:并且不会像今天已经忘记的情况那样被通知(尽管已恢复),所以不会被通知=。我感谢编译器对此的警告...
Matthieu M.

13
在我用过的几乎所有键盘上,“:=”都需要在输入时更改Shift键。在我现在使用的那个字母上,“:”是大写字母,“ =”是小写字母,而我已经将其反转了。我输入了大量的作业,并且不需要麻烦的输入。
David Thornley

14
@大卫索恩利:代码读取很多更多的时间比它被写入。我不赞成有关“打字麻烦”的任何论点。
Greg Hewgill 2011年

8
@Greg Hewgill:当然,阅读的频率要高于书面的。然而,之间的问题,===不是在读书,因为他们是不同的符号。这是书面形式,并确保您选择正确的人。
David Thornley

29

的选择+在Javascript两个除了字符串连接是一个可怕的错误。由于值是无类型的,因此这导致拜占庭规则,这些规则+将根据每个操作数的确切内容确定是添加还是串联。

在一开始就引入一个全新的运算符(例如$用于字符串连接)很容易。


13
@Barry:不是。 +作为强类型语言中的字符串连接运算符,这很有意义。问题是Javascript使用它,但是没有强类型输入。
梅森惠勒

13
@梅森:+级联没有意义,因为级联绝对不是可交换的。就我而言,这是一种虐待。
Matthieu M.

12
@Matthieu:嗯...为什么这么重要?串联不是可交换的(就像加法一样),但是两个字符串的加法是无意义的,因此没人会这样想。您正在发明一个根本不存在的问题。
梅森惠勒

4
只需切换到C ++和任何一种添加到深奥的过载到您选择的运营商
Newtopian

8
@Matthieu:可交换性不是这里的问题。如果JS拥有Matrix类,那么您是否认为拥有一个*运算符是一种滥用?
dan04 2011年

24

我发现JavaScript默认是全局性的主要问题,如果您不使用JSLint或类似的语言,通常是bug的来源


2
默认为全局?我很高兴自己那时不使用JS ...
Anto

5
实际上,这是一种非常不错的语言,其中包含一些错误的设计选择。这是主要的,如果不对变量进行范围限定,则它将范围限定为全局变量。好消息是,通过使用Doug Crockford的Jslint程序,您可以捕获此错误以及更多错误。
Zachary K

1
只要var声明变量就可以使用。而且不要告诉我它是太多类型的,因为Java迫使您两次声明所有类型,而没有人抱怨它是一个糟糕的设计选择。
davidk01 2011年

3
@ davidk01:人们确实对此表示抱怨。同样,人们抱怨必须std::map<KEY, VALUE>::const_iterator在C ++中声明足够多的变量,然后再将auto其添加到该语言中。
dan04'3

1
"use strict"es5中添加的指令将未声明的引用更改为错误。
肖恩·麦克米伦

22

C和C ++中的预处理器是一个巨大的难题,它会像筛子一样泄漏出抽象的内容,通过大鼠的#ifdef语句嵌套来鼓励意大利面条代码,并需要极其难以理解的ALL_CAPS名称来解决其局限性。这些问题的根源在于它在文本级别而不是句法或语义级别运行。对于它的各种用例,应该已经用真实的语言功能代替了它。这是一些示例,尽管可以肯定地用C ++,C99或非官方但事实上的标准扩展解决了其中一些问题:

  • #include 应该已经被真实的模块系统所取代。

  • 内联函数和模板/泛型可以替代大多数函数调用用例。

  • 某种清单/编译时间常数特征可以用于声明此类常数。 D的enum扩展在这里非常有用。

  • 真正的语法树级别的宏可以解决许多其他用例。

  • 字符串混合可以用于代码注入用例。

  • static ifversion语句可用于条件编译。


2
@dsimcha:我同意这个#include问题,但是后来发明了模块系统!C和C ++旨在最大程度地向后兼容:/
Matthieu M.

@Matthieu:是的,模块系统是后来者发明的,但无论如何我们在这里谈论事后看来。
dsimcha 2011年

3
我同意这是身体各个部位的巨大痛苦,但是多遍设计具有简单的优点:C编译器在成功编译C代码块之前并不需要太多的操作上下文知识。仅当您可以证明在C语言本身(例如,类似于C ++的类)中使用虚拟模块系统的成本始终低于或与当前基于cpp的#include黑客攻击相当时,才可以将其视为设计错误。
reinierpost 2011年

我同意。但是,但是有些人认为预处理是一件好事,并且抱怨说(例如)Java不支持预处理。
斯蒂芬·C

5
@Stephen:我同意带有预处理器的Java可能比没有预处理器的Java更好,但这仅仅是因为 Java没有替换预处理器所必需的几个“实际”功能。在像D这样的语言中,它包括这样的功能;而在Python中,它通过动态获得其他方式的灵活性,我不会错过任何一点。
dsimcha'3

21

可以用数百种语言列出数百种错误,但是从语言设计的角度来看,IMO并不是一种有用的练习。

为什么?

因为在一种语言中可能会出错的东西在另一种语言中不会出错。例如:

  • 使C成为托管(即垃圾收集)语言或限制原始类型将限制其作为半便携式低级语言的用处。
  • 在Java中添加C风格的内存管理(例如,解决性能问题)会破坏它。

一些经验教训要学习,但是这些经验教训很少是明确的,要理解它们,您必须了解技术上的权衡...以及历史背景。(例如,繁琐的Java泛型实现是出于维持向后兼容性的最重要业务要求的结果。)

IMO,如果您认真设计一种新的语言,则需要实际使用多种现有语言(并研究历史语言)...并自己确定错误是什么。而且您需要记住,每种语言都是在特定的历史背景下设计的,以满足特定的需求。

如果有一般的课程要学习,那么它们处于“元”级别:

  • 您不能设计出适合所有目的的编程语言。
  • 您无法避免犯错误……尤其是从后视角度来看。
  • 对于您的语言用户而言,要纠正许多错误是很痛苦的。
  • 您必须考虑目标受众的背景和技能;即现有的程序员。
  • 你不能取悦所有人。

9
-1-编程语言遵循设计目标。与这些目标背道而驰的语言功能是设计缺陷,除非这是对其其他目标之一的必要折衷。满足所有人的意图的语言并不多,但是所有语言都应尽力满足那些最初要满足的人们。这种后现代主义的政治正确性对于编程语言的研究和开发是非常令人窒息的。
宫坂丽

我的观点是,不考虑设计目标就很难学习课程。
斯蒂芬·C

1
在我看来,OP已在问题的第二段中解释了您的答案。
艾丹·库利(Aidan Cully)

20

CC ++:所有那些没有任何意义的整数类型。

特别是char。是文本还是小整数?如果是文本,是“ ANSI”字符还是UTF-8代码单元?如果是整数,是带符号的还是无符号的?

int 原本是“本机”大小的整数,但在64位系统上则不是。

long可以大于或等于int。它可能是也可能不是指针的大小。无论是32位还是64位,这都是编译器编写者的任意决定。

绝对是1970年代的语言。在Unicode之前。在64位计算机之前。


10
C并非被设计为适合所有人的标准语言,因此它不会成为设计错误。它被设计为将cpu建模为可移植的汇编程序,从而避免了特定于cpu的汇编程序代码。但是,它已在Java中修复。

7
我认为更大的问题是,尽管历史向我们展示了这是一个糟糕的主意,但仍继续使用相同的毫无意义的术语的新型语言。至少C家伙注意到了他们的错误并创建了标准的int类型。
Mark H

5
字符不是utf-8单位。utf-8字符可能要花费超过8位来存储。C不是1970年代的语言,我现在(自愿)将其用于一个项目。
dan_waterworth 2011年

4
C仅仅是PDP-11处理器的高级抽象。例如,PDP-11直接支持前后递增。
比特币2011年

5
这是一个非常误导的答案。首先,C和C ++不可互换。其次,语言规范清楚地定义了char是什么- 声明为char类型的对象足够大,可以存储基本执行字符集的任何成员。。第三,C不是“ 70年代的语言”,它是一种与硬件紧密相关的语言,并且可能是最终允许所有高级抽象实际上对CPU有意义的语言。您以一个只懂高级语言并且不了解事物实际运行方式的人而脱颖而出。-1
Ed S.

18

null

它的发明者托尼·霍尔(Tony Hoare)称其为“十亿美元的错误”

它是60年代在ALGOL中引入的,并存在于当今大多数常用的编程语言中。

更好的选择,像OCaml中和Haskell语言中使用,是也许。一般的想法是对象引用不能为空/空/不存在,除非有明确的指示可能是空/空/不存在。

(尽管托尼的谦逊很棒,但我认为几乎每个人都会犯同样的错误,而他恰好是第一个。)


即使与它的发明者也不同意!!!null是指针/引用数据类型的空值。字符串有空字符串,集合有空集(Pascal空集= []),整数有0。大多数使用null / nil /的编程语言,如果正确分配了变量,则可以防止空错误。
umlcat 2011年

4
@ user14579:支持任何类型的集合,字符串或数组的每种语言都有{},但这在语义上仍然适当,除非您已经有可能导致数组边界错误的东西,否则它不会崩溃–但这是另一个问题。您可以处理一个空字符串以大写所有字符,这将导致一个空字符串。您对空字符串尝试相同的操作,如果没有适当的考虑,它将崩溃。问题在于,这种适当的考虑是乏味的,通常会被遗忘,并且使得编写单表达式函数(即lambda)变得困难。
宫阪丽

1
每次输入null时我都会赚钱...哦,对不起,每次输入null时都会有人亏钱。除了这次。
kevpie 2011年

3
@umlcat-当您使用Ocaml,Haskell和F#等具有模式匹配的语言时,请使用Maybe x | None模式可防止您在编译时忘记空值。在以null为既定习语的语言中,没有任何编译时的欺骗手段可以捕获错误。由于在具有Maybe和Some monad的语言中,您必须明确选择不处理空值情况,因此与“空值”方法相比,它们具有很大的优势。
JasonTrue 2011年

1
@Jason-我想将其maybe视为null选择加入,而null例外是选择退出。当然,关于运行时错误和编译时错误之间的区别也有很多话要说,但是null本质上是注入行为这一事实本身就值得注意。
宫阪丽

14

我感到设计PHP的人没有使用普通的键盘,甚至没有使用colemak键盘,因为他们应该已经意识到自己在做什么。

我是PHP开发人员。PHP不好玩。

Who::in::their::right::mind::would::do::this()?该::运营商需要在按住Shift键,然后两个关键印刷机。真是浪费能源。

虽然->这个->不是->不是->更好。这还需要三个按键,并且在两个符号之间进行切换。

$last = $we.$have.$the.$dumb.'$'.$character。美元符号使用了很多次,并且要求奖品的使用范围一直延伸到键盘的最顶端,同时按下Shift键。

他们为什么不能将PHP设计为使用键入速度更快的键?为什么不能we.do.this()或不让var以仅需要一次按键的键开头-或根本不需要(JavaScript)并仅预定义所有var(就像我必须为E_STRICT所做的那样)!

我不是慢打字员-但这只是一个la脚的设计选择。


Perl也有这种痛苦。
丹妮丝2011年

2
C ++也是如此,由于某些莫名其妙的奇怪原因,PowerShell也是如此。
宫阪丽

2
使用功能更强大的编辑器,然后为这些操作员定义自己的键序列。
凯文·克莱恩

1
I::have::nothing::against::this->at.all()
Mateen Ulhaq 2011年

1
也许他们不使用qwerty键盘。$和:不需要像键盘一样在所有键盘上按下Shift键。
2011年

13

asp.net中使用桌面启发式表单

总是感觉到了错误,并妨碍了网络的实际运行。值得庆幸的是,asp.net-mvc并非以同样的方式受苦,尽管他的灵感来自Ruby等。


18
那不是图书馆的事吗?
nikie 2011年

@nikie,这是个好点;)
鸽子

1
@nikie实际上,基于XML的ASPX代码是一种语言,因此您可以将其改用。:D
CodexArcanum 2011年

2
我认为ASP.NET的真正问题在于,它试图向程序员隐藏Web的细节有多么困难。实际上,ASP.NET中确实存在一些非常整洁,有用的内容,但是您必须如此艰苦奋斗,深入挖掘才能做到。
CodexArcanum 2011年

1
另一方面,有成千上万个简单而成功的数据收集应用程序,它们使用“经典”桌面应用程序组合在一起。唯一的坏事是在MVC之前,唯一的Microsoft选项是Windows窗体。
ElGringoGrande 2011年

13

对我来说,这是PHP在其标准库中绝对缺少命名和参数排序约定。

尽管JASS在释放/删除被引用对象之后必须取消引用(否则引用会泄漏并且会丢失几个字节的内存)的必要性更加严重,但是由于JASS是单一用途语言,因此并不是那么关键。


9
PHP的stdlib中缺乏约定,这可以说不是语言设计缺陷。

3
@delnan:缺乏约定是PHP设计的结果,因此与语言设计有很大关系。我也不清楚图书馆和语言之间是否有明显区别。Lisp特别具有引以为傲的传统,那就是将一种语言叠加在另一种语言之上。
btilly 2011年

1
关于JASS的真正非凡之处在于,它具有对句柄的引用计数,但是除非将它们手动销毁(图形界面创建的函数会泄漏内存到处),否则它不会清理它们!
Craig Gidney

13

我面临的最大设计缺陷是python最初并不是像python 3.x那样设计的。


1
好吧,甚至Guido都无法立即解决所有问题 ……

5
@delnan,哦,我知道,并且python <3仍然是一种非常好的语言,但是以我无法使用的python 3.x形式提供更好的语言有点烦人,因为它破坏了所有我需要。
dan_waterworth 2011年

继续游说您的python 3.x模块!同时,我将继续写2.5.4。多亏了SO,实际上让我想起了3.x仍然运行良好。
kevpie 2011年

@kevpie首先,游说向python添加条件编译,以使库维护人员更轻松地进行转换。从长远来看,2to3并不是一个可维护的解决方案。
伊万·普赖斯

12

C语言中的数组衰减,因此也就是C ++中的数组衰减


我也希望适当的数组支持。在C ++中,您可以使用abscons语法和模板防止衰变...但是,它更像是一个hack:/
Matthieu M.

1
请注意,这就是C ++必须具有单独的deletedelete[]运算符的原因。
dan04 2011年

您可以随时将数组放入结构中,并根据需要将其按值传递,但这通常比原始问题更尴尬。在C ++中,通常可以避免使用数组。
David Thornley

2
至少在C情况下,反对适当的数组支持的论点是“数组边界检查是昂贵的”,特别是考虑到C指针算术的工作方式。
斯蒂芬·C

@Stephen C:数组边界检查与数组衰减有什么关系?
Nemanja Trifunovic

11

Java中的原始类型。

他们打破了一切都是其后裔的原则,java.lang.Object从理论的角度来看,这导致了语言规范的额外复杂性,并且从实践的角度来看,它们使集合的使用极其繁琐。

自动装箱有助于减轻实际缺陷,但代价是使规范变得更加复杂并引入了巨大的香蕉皮:现在,您可以从看起来像简单的算术运算的操作中获得空指针异常。


如果删除基本类型,这将导致可怕的性能问题。而且自动装箱很容易搞砸,因此没有任何改善。
deadalnix

当时,这是Java设计人员的明智决定。鉴于90年代可用的机器/虚拟机,其性能提升超过了统一java.lang.Object周围所有内容的概念优势。
mikera 2011年

10

我最了解Perl,所以我选择它。

Perl尝试了许多想法。有些很好。有些不好。有些是原创的,没有充分的理由复制。

一种是上下文的概念-每个函数调用都在列表或标量上下文中进行,并且可以在每种上下文中执行完全不同的操作。正如我在http://use.perl.org/~btilly/journal/36756中指出的那样,这会使每个API变得复杂,并经常导致Perl代码中的细微设计问题。

接下来是将语法和数据类型完全绑定的想法。这导致了领带的发明,以允许对象伪装成其他数据类型。(使用重载也可以达到相同的效果,但在Perl中,tie是更常见的方法。)

许多语言犯的另一个常见错误是,首先提供动态范围而不是词汇。以后很难恢复此设计决策,并导致持久的疣。Perl中这些疣的经典描述是http://perl.plover.com/FAQs/Namespaces.html。请注意,这是在Perl添加our变量和static变量之前编写的。

人们理所当然地在静态类型与动态类型上存在分歧。我个人喜欢动态打字。但是,重要的是要有足够的结构来捕获错别字。Perl 5严格地做到了这一点。但是Perl 1-4弄错了。其他几种语言还具有执行严格检查的功能。只要您擅长执行棉绒检查,就可以接受。

如果您正在寻找更多坏主意(很多),请学习PHP并研究其历史。我最喜欢的过去错误(很久以前已修复,因为它导致了很多安全漏洞)默认是允许任何人通过传递表单参数来设置任何变量。但这远非唯一的错误。


5
是的,Perl有很多错误,因为构建它的人正在尝试新的想法,而当您这样做时,您常常会误会它们。(Perl也有一些很好的东西,并且是其他人似乎都抄袭过的Regexps的标准)
Zachary K

@ zachary-k:完全同意。在开始讨论问题之前,我试图弄清楚了这一点。
btilly 2011年

4
Lisps最初是动态范围的,并且随着时间的流逝逐渐变为词汇范围的(至少在Scheme和Common Lisp中)。改变并非不可能。
David Thornley

4
@ david-thornley:除非您牺牲了向后兼容性,否则这是不可能的。Scheme始终在词法范围内。Common Lisp从标准化开始就在词汇上有所限制,但是各种Lisp社区都在努力采用它。Emacs Lisp仍在使用动态作用域,即使很长时间以来一直希望对其进行更改。
btilly 2011年

1
顺便说一句,人们不喜欢Perl的许多东西不是在Perl中发明的,而是取自其他语言,主要是Bourne shell。
reinierpost 2011年

10

JavaScript对代码块和对象文字的含糊不清。

  {a:b}

可以是代码块,其中a是标签,b是表达式;或者它可以定义一个对象,其属性a具有值b


我实际上很喜欢JavaScript。语言结构的简单性很好,如果开发人员知道他们在做什么,则目的应该显而易见。
Xeoncross 2011年

2
Xeoncross:我通常不喜欢模棱两可。在这种情况下,对开发人员来说很明显,但是eval()需要额外的括号。
user281377 2011年

2
@ammoQ:很明显吗?之后怎么样了?一个对象还是一个代码块?
配置工具,

配置器:显然是一个对象。没有理智的人会使用名为的标签a
user281377 2011年

10

我将回到FORTRAN和空白不敏感状态。

它遍及整个规范。该END卡必须定义为在7-72列中依次具有'E','N'和'D'的卡,并且没有其他非空白,而不是适当地带有“ END”的卡专栏,仅此而已。

它导致了容易的语法混乱。 DO 100 I = 1, 10是一个循环控制语句,而是DO 100 I = 1. 10一个将值1.1分配给名为的变量的语句DO10I。(事实上​​,变量可以在不声明的情况下创建,其类型取决于它们的首字母。)与其他语言不同,没有其他方法可以使用空格分隔标记以消除歧义。

它还允许其他人编写真正令人困惑的代码。有理由为什么永远不会重复使用FORTRAN的此功能。


1
在一种可以重新定义文字的语言中,这是最糟糕的例子-我的意思是,它只会导致一个小小的航天器坠毁
Martin Beckett

尽早,您可以编写DAMNATION代替DIMENSION,并且它可以工作。
Mike Dunlavey

CS之外的人仍在教它。我仍然要处理以下问题:a)反对声明,b)反对空白,c)像“ continuation lines”,d)使用6个字符的名称,或4,d)当他们看到时被打结(test ? a : b),e)坚持使用时**,f)无法处理区分大小写的行为。这主要是由于50年代的按键打孔。
Mike Dunlavey

1
@Martin Beckett-在FORTRAN中重新定义文字确实是编译器缺陷而不是语言功能。这当然不是故意的语言功能。
斯蒂芬·C

1
@oosterwal:我当然做到了。我可能是错的,但是我隐约记得基于打孔卡的语言定义。它们是当时输入FORTRAN程序的主要方式,而保留了73-80列的80列行的想法来自打孔卡。
David Thornley

9

BASIC的最大问题之一是缺乏将语言扩展到其早期环境之外的任何明确定义的方法,从而导致了一堆完全不兼容的实现(以及在任何标准化下几乎不相关的事后尝试)。

几乎所有的语言都会被一些疯狂的程序员所使用。最好在开始时针对通用用途进行规划,以防疯狂的想法产生。


2
+1:任何没有适当模块和库的语言都是要等待的错误。COBOL也受此困扰,导致了不兼容的特殊变体。
S.Lott

8

我相信DSL(特定于域的语言),我对一种语言的重视是,如果它允许我在其之上定义DSL。

在Lisp中有宏-大多数人和我一样都认为这是一件好事。

在C和C ++中,存在宏-人们抱怨它们,但是我能够使用它们定义DSL。

在Java中,它们被遗漏了(因此在C#中被遗漏了),并且声明缺少它们是一种优点。当然可以,您可以拥有智能感知能力,但是对我来说,这只是一件小事。要进行DSL,我必须手动扩展。这很痛苦,即使让我用更少的代码来做更多的事情,也使我看起来像一个糟糕的程序员。


4
我同意,任何没有体面宏的语言都是一个巨大的无法修复的设计缺陷。但是,“ 他们被排除在外 ” 是什么意思?C预处理器不是任何一种像样的宏系统。Java不是从任何具有宏的适当语言派生的。
SK-logic

1
您可以使用外部宏处理语言(例如,m4等)编写DSL。
我的正确观点

4
@SK:例如,与Lisp相比,我不会说C预处理器是一个不错的宏系统。但是,比起什么都没有,它非常有用。
Mike Dunlavey

4
@reinierpost:我正在考虑我可以在Lisp中做的事情,例如引入诸如差分执行和回溯之类的控制结构。这些可以通过Lisp宏来完成。在C / C ++中,我可以使用C宏(和一些程序员知识)来执行差分执行,但不能回溯。使用C#,我什么都做不到。我得到的是类似智能感知的东西。BFD。
Mike Dunlavey

1
@David:我这样做的方式是我有一个宏来包装普通代码,例如语句列表。它将采用cdr列表的,并从列表中形成一个lambda闭包(即连续),并将其作为参数传递给car列表的。当然,这是递归完成的,将对条件,循环和函数调用“做正确的事”。然后,“选择”功能就变成了正常循环。不漂亮,但功能强大。问题是,它使创建过度嵌套的循环变得非常容易。
Mike Dunlavey

7

陈述,每种具有陈述的语言。它们不执行您无法使用表达式执行的操作,并阻止您执行很多操作。?:三元运算符的存在只是必须尝试绕开它们的一个示例。在JavaScript中,它们特别令人讨厌:

// With statements:
node.listen(function(arg) {
  var result;
  if (arg) {
    result = 'yes';
  } else {
    result = 'no';
  }
  return result;
})

// Without:
node.listen(function(arg) if (arg) 'yes' else 'no')

我在这里感到困惑:您是否只想以更简单的方式来做事情?
TheLQ 2011年

2
正确。一切表达。
优厚

1
Lisp为此很好。
David Thornley

1
@ SK-logic:我怀疑语句是通过FORTRAN,ALGOL和COBOL盲目地从机器语言继承的。
David Thornley

1
我很确定机器语言是共同的祖先,这仅反映了一个事实,即基于von Neumann架构的现代计算机顺序执行指令并修改状态。最终,当发生IO时,将存在不产生有意义的数据的表达式,因此语句在语义上指示某些代码仅具有副作用并不是完全没有用的。甚至具有unit类型(aka ())而不是语句的语言也需要特别注意以确保它们不会引发警告或表现异常。
宫坂丽

6

对我来说,正是设计问题困扰了所有源自C的语言。即“ 悬空 ”。这个语法问题应该已经用C ++解决了,但是已经在Java和C#中实现了。


3
C ++的核心目标之一是与C完全向后兼容。如果它们大大改变了语义行为,它可能不会像以前那样流行(或者至少是当时的想法)
Ed S.

2
@Ed S.,但是,消除“悬而未决”的问题可以通过消除<compound_statement>(又名<block>)语法生成并将大括号并入条件和迭代控制结构中来实现,就像它们处理时一样。添加了try / catch异常处理控制结构。没有理由不纠正Java和C#中的这种语法歧义。当前,针对这种语法歧义的防御方法是使遵循条件或迭代控制语句的每个语句成为复合语句。
比特币2011年

1
你是什​​么意思?这不是一个“语法问题”(这是明确的)。您将如何“解决”它?我实际上发现C中的规则令人满意。可以说,只有类似Python的语法(有意义的缩进)才能真正解决此问题。此外,我很高兴现代语言要求括号。我同意所有类似C的语法都很烂,但是悬挂式-else是它们中最少的问题。
康拉德·鲁道夫

1
续:我认为Python使用缩进来描绘语句列表的方式令人难以置信。通过将词法扫描与语法分析紧密耦合,该技术违反了“关注点分离”原则。无上下文语法应该能够在不了解源布局的情况下进行解析。
比特商

3
@ bit-twiddler:不,不是。Python词法分析器仅将空格转换为适当的INDENT和DEDENT标记。完成此操作后,Python将具有非常传统的语法(docs.python.org/reference/grammar.html)。
dan04 2011年

6

我认为到目前为止的所有答案都指向许多主流语言的单一失败:

在不影响向后兼容性的情况下,无法更改核心语言。

如果解决了这个问题,那么其他所有难题都可以解决。

编辑。

可以通过使用不同的名称空间在库中解决此问题,并且您可以设想对语言的大多数核心执行类似的操作,尽管这可能意味着您需要支持多个编译器/解释器。

最终,我认为我不知道如何以完全令人满意的方式解决问题,但这并不意味着不存在解决方案,或者无法完成更多工作


1
您将如何“解决”这个问题?
Bjarke Freund-Hansen

我不确定它是否可以完全解决-显然将事情排除在核心语言之外,并保存在标准库中会有所帮助,所以我认为我会尽力将其推到最大
jk。

1
向后兼容,您是说较新的编译器应该能够编译旧代码?
宫阪丽

我的意思是新的编译器不应该改变的旧代码的含义,未能编译将是的一个子集
JK。

在大多数情况下,当不存在某个特定功能或想要更改某个特定功能时,人们会根据前一个功能创建一种新语言。带有类=> C ++的C
umlcat 2011年


4

由于希望在添加泛型时保持向后兼容性,因此Java和C#的类型系统都存在烦人的问题。Java不喜欢混合泛型和数组。C#不允许使用一些有用的签名,因为您不能将值类型用作界限。

作为后者的示例,请考虑

公共静态T Parse <T>(Type <T> type,string str)其中T:枚举
并排或替换
公共静态对象Parse(类型,字符串str)
在里面 Enum课堂上会允许
MyEnum e = Enum.Parse(typeof(MyEnum),str);
而不是重言式的
MyEnum e =(MyEnum)Enum.Parse(typeof(MyEnum),str);

tl; dr:开始设计类型系统时要考虑参数多态性,而不要在发布版本1之后考虑。


2
在C#中,无法将类型限制为枚举很烦人,但是您可以像这样解决它,MyMethod<T>(T value) where T : struct, IComparable, IFormattable, IConvertible 但是您仍然必须测试枚举,这是一个hack。我认为C#泛型的最大不足是不支持更高的种类,这确实会使该语言具有一些很酷的概念。
CodexArcanum 2011年

4

我觉得自己很容易发火,但是我真的很讨厌在C ++中通过引用传递普通的旧数据类型的能力。我只是有点讨厌能够通过引用传递复杂类型。如果我正在查看一个函数:

void foo()
{
    int a = 8;
    bar(a);
}

从调用点来看,没有办法告诉bar,可以在完全不同的文件中定义的是:

void bar(int& a)
{
    a++;
}

有人可能会争辩说,这样做可能只是一个糟糕的软件设计,不能怪罪该语言,但我不喜欢该语言首先允许您这样做。使用指针并调用

bar(&a);

更具可读性。


+1我不同意您的看法,但感谢您的推理。
乔恩·普迪

@Jon我实际上会对您的想法非常感兴趣。您是否持“不要责怪语言”的观点?
杰夫

6
@Jeff:一方面,引用语义进入C ++的主要原因是运算符重载,对此,统一的引用行为很有意义。但是,更重要的是,C ++设计为通用的,并提供非常细粒度的功能,即使这样做会招致严重的程序员错误风险。所以是的,至少在这种情况下,不要责怪这种语言。我宁愿能够犯错,也不愿让一种语言妨碍我。
乔恩·珀迪

@Jon同意,引用引用适用于POD之外的所有内容非常奇怪。如果此功能完全没有C ++替代,我希望使用它,但是我们可以不同意:)。感谢您的输入!
杰夫

Java似乎不像您那样喜欢指针。
Mateen Ulhaq 2011年

4

改变

当我学习COBOL时,ALTER语句仍然是标准的一部分。简而言之,该语句使您可以在运行时修改过程调用。

危险是您可能将此语句放在很少访问的晦涩难懂的代码部分中,并且有可能完全改变程序其余部分的流程。使用多个ALTER语句,您几乎不可能随时了解您的程序在做什么。

我的大学讲师非常强调说,如果他在我们的任何程序中看到该陈述,他都会自动使我们失望。


它确实有很好的用例-存根或备忘录。不用写,而是v() { if (not alreadyCalculatedResult) { result = long(operation); alreadyCalculatedResult = true; } result; }v() { result = long(operation); v = () => result; result; }
配置器,

4

编程语言最严重的缺点还没有得到很好的定义。我记得一个例子是C ++,它的起源是:

  1. 定义得很糟糕,以致于无法通过遵循书籍或示例来编译和运行程序。
  2. 调整完程序以在一个编译器和OS上编译并运行后,如果切换了编译器或平台,则必须重新开始。

我记得,花了大约十年的时间才使C ++定义得足够好,以使其与C一样具有专业上的可靠性。这种事情永远都不会发生。

我认为有其他事情(应该以不同的答案回答)是一种以上的“最佳”方式来完成某些常见任务。(还是)C ++,Perl和Ruby就是这种情况。


1
我看不出如何避免使用不断发展的语言带来的不确定性。或者,就此而言,使用预先设计的语言,其中原始设计师错过了一些重要要点(例如Pascal)。
David Thornley

@David Thornley定义明确。经得起错误,大多数编程语言设计师从一开始就正确地做到了。工具可以检查语法是否完整(C ++至少需要三种语法),并且应该为标准实现指定语义。
2011年

我同意定义良好的语言是可能的,但这不会在像标准C ++这样的不断发展的语言中发生。另一种选择是,每种语言在发布之前都经过全面设计,这不一定是获得最佳语言的方法。我要说的是,大多数编程语言设计师一开始都会犯错,因为语言设计非常复杂。
David Thornley

我想我很难理解“定义明确”的含义。您是否抱怨不同的C ++编译器实际上没有编译相同的语言?
肖恩·麦克米伦

3

C ++中的类是该语言中的某种强制设计模式。

实际上,结构和类之间在运行时没有区别,要理解“信息隐藏”的真正编程优势到底是什么,真是令人困惑,以至于我想把它放在那里。

我为此会感到沮丧,但是无论如何,C ++编译器很难编写这种语言,感觉就像是一个怪物。


2
信息隐藏很重要,因为它使您可以从API的可访问部分(API的“ UI”)隐藏可能会更改的实现特定细节,从而使对程序的更改变得更容易且痛苦更少。
安托

1
API的用户界面...不,很严重,我不购买。
11

3
这种差异不是C ++中最令人反感的部分,甚至都不是最接近的部分。唯一的区别是默认的访问修饰符(对结构来说是公共的,对类来说是私有的)。C ++是一种可怕的,可怕的语言,但肯定不是这一部分。
SK-logic

sk-logic:好吧,我可以说恐怖始于此。
jokoon 2011年

2
信息隐藏是好的;您可以在此找到所有讨论。我能想到的唯一著名的软件书就是布鲁克斯的《神话人月》,后来他认为这是书中最大的错误。如果您不了解优势,那么您实际上就没有资格做出自己的判断。
David Thornley

3

尽管每种语言都有其缺点,但一旦您了解它们,就不会造成麻烦。除了这对:

复杂的语法加上冗长的API

对于像Objective-C这样的语言,尤其如此。该语法不仅非常复杂,而且API使用的函数名称如下:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

我全力以赴,做到明确明确,但这很荒谬。每次我使用xcode坐下时,都会感到自己像个n00b,这真令人沮丧。


Objective-C的语法非常复杂吗?您没看过C ++吗?实际的方法名称为tableView:cellForRowAtIndexPath:,在我看来这是非常可描述的。
右对齐

学会触摸打字。
finnw

2
  1. 在C中签名的字符-一种令人厌恶的发明,使数学家可以收集大量小物品
  2. 使用案例来携带语义内容-同样对于数学家来说,他们不需要讲话,也永远没有足够的空间来容纳公式
  3. 基本方言中的让/普通分配与集合分配-我认为这里没有数学家

我对您签署的字符注释不知所措,您能解释一下吗?
Winston Ewert

1
对于人类来说,(无符号)字符的概念以及告诉编译器将无符号字符用作默认值的必要性肯定与数学家认为2!= 2一样疯狂,因为第二个2是大写的粗体或斜体。
Ekkehard.Horner

5
问题在于,C混淆了“字符”(即文本字符串的一部分)和“字节”(即(u)int_least8_t)的概念。对于小整数,符号完全有意义,但对于字符则完全没有意义。
dan04'3

@ dan04:我同意,我希望它们对于小整数(有符号和无符号),字节和字符具有不同的类型。当您向新手解释要操纵原始内存时,他们需要char*像C-String一样强制转换为...,他们会感到非常困惑。
Matthieu M.

C#得到这个权利,独立sbytebytechar类型。
dan04 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.