Walter Bright 使用了“冗余”这个词……或者“这到底是啥意思?”
Posted
技术标签:
【中文标题】Walter Bright 使用了“冗余”这个词……或者“这到底是啥意思?”【英文标题】:Walter Bright's use of the word "redundancy"... or 'The heck does that mean?'Walter Bright 使用了“冗余”这个词……或者“这到底是什么意思?” 【发布时间】:2011-04-01 05:35:19 【问题描述】:所以我正在阅读 Walter Bright 关于 Bitwise 中的 D 语言 (http://www.bitwisemag.com/copy/programming/d/interview/d_programming_language.html) 的采访,我看到了这句关于语言解析的非常有趣的引述:
然而,从理论的角度来看,能够生成良好的诊断要求语法中存在冗余。冗余用于猜测预期的内容,冗余越多,猜测越有可能是正确的。这就像英语——如果我们不时拼错一个 wrod,或者如果一个单词丢失,冗余使我们能够正确猜测其含义。如果一种语言没有冗余,那么任何随机的字符序列都是有效的程序。
现在我想弄清楚他说“冗余”是什么意思。
我几乎无法理解最后一部分,他提到有可能拥有一种“任何随机字符序列都是有效程序”的语言。我被告知存在三种错误:句法、运行时和语义。是否存在唯一可能的错误是语义错误的语言?组装是这样的吗?机器码呢?
【问题讨论】:
我猜他在这里谈论的是语法。机器代码可能与他所说的非常接近。任何有效的机器码操作字符串都是有效的语法。 这取决于什么指令集。有些肯定有非法指令,这往往会造成陷阱。 但这就像在脚本中使用来自字符集中的无效字符一样,这并不是语法问题。如果您将输入字符集限制为有效的机器代码操作,那么您可以将它们按任何顺序排列,并且您拥有一个有效的程序(就语言的语法而言)。所以这种语言没有冗余。 我同意。如果我们接受 OP 的论点,即存在 3 种错误,那么机器语言最多允许两种,运行时(非法指令)和语义(只是做错事)。当然有语言(不一定有用),只有语义错误。 现在有点晚了,但真的很短的答案是它可以让编译器告诉你“缺少分号”之类的东西。 【参考方案1】:我将重点介绍为什么(我认为)Walther Bright 认为冗余是好的。我们以 XML 为例。这个sn-p:
<foo>...</foo>
有冗余,如果我们使用 S-Expressions 代替,结束标签是冗余的:
(foo ...)
它更短,程序员不必经常输入foo
来理解该sn-p。减少冗余。但它也有缺点,正如http://www.prescod.net/xml/sexprs.html 中的一个例子所示:
(document author: "paul@prescod.net"
(para "This is a paragraph " (footnote "(better than the one under there)" ".")
(para "Ha! I made you say \"underwear\"."))
<document author="paul@prescod.net">
<para>This is a paragraph <footnote>(just a little one).</para>
<para>Ha! I made you say "underwear".</para>
</document>
在两者中,脚注的结束标记/结束括号都丢失了。一旦解析器看到</para>
,xml 版本就完全无效。 S-Expression 仅在文档末尾无效,并且仅当您在其他地方没有不需要的结束括号时才无效。因此,在某些情况下,冗余确实有助于理解作者的意思(并指出他表达方式中的错误)。
【讨论】:
感谢您提供非常完整的答案。你知道我可以在哪里开始寻找关于解析器如何实际实现这种事情的更多技术/理论讨论吗?我想很容易弄清楚 XML 解析器在它期望 时看到 时如何专门识别错误,但是,一般来说,解析器如何查找列、行和错误的类型?有没有类似“傻瓜错误处理的一般理论”? 据了解,根本没有“错误处理的一般理论”。它似乎更像是一门艺术。 我同意BCS,我不认为有这样的事情。但我认为(一些)通常的编译器资源(其中大部分主要集中在前端,即解析器)包括 一种 这样做的方式。【参考方案2】:汇编语言(大多数汇编语言,无论如何)根本不是那样——它们有相当严格的语法,大多数随机字符串都会被诊断为错误。
机器码更接近。由于不涉及从“源”代码到“对象”代码的转换,所有错误都是语义上的,而不是句法上的。大多数处理器确实有各种他们会拒绝的输入(例如,执行“错误操作码”陷阱/中断)。您可能会争辩说,在某些情况下,这将是句法(例如,根本无法识别的操作码),而其他情况则是语义(例如,该指令不允许的一组操作数)。
对于那些记得它的人来说,东元以赋予几乎所有可能的输入某种意义而闻名(臭名昭著?),所以它的方式几乎相同。一个有趣的挑战是弄清楚如果你输入(例如)你的名字会发生什么。
【讨论】:
回复。 TECO:有一个类似的挑战,涉及预测按住 Control 或 Meta 并在 Emacs 中输入你的名字的效果。 +1 为它带来了老派。谢谢,这正是我想知道的事情。 是的,我也花了一分钟才找到它(不断在谷歌上搜索 Tampa Electric):en.wikipedia.org/wiki/Text_Editor_and_Corrector【参考方案3】:nglsh nclds ll srts of xtr ltrs t mk it ezr t read
【讨论】:
事实上,在 lseat smoe snsee 学习除 fsrit 和 lsat 之外的所有内容都是 psibolse。 And dha rizn fonetisaizd Ingglish haznt cot on iz dhat it's notoriasly mor difikalt tu rid widhaut dha vizhual kyuz uv dha kurent orthografi。【参考方案4】:好吧,使用 C# 中的示例(因为我不知道 D)。如果您有一个带有抽象方法的类,则该类本身必须标记为抽象:
public abstract class MyClass
public abstract MyFunc();
现在,编译器自动将 MyClass 标记为抽象(这是 C++ 处理它的方式)是微不足道的,但在 C# 中,您必须明确地这样做,这样您的意图就很清楚了。
与virtual
方法类似。在 C++ 中,如果在基类中声明 virtual,则方法在所有派生类中自动为 virtual。然而,在 C# 中,该方法必须显式标记为 override
,这样就不会混淆您想要什么。
【讨论】:
但是这些例子并不能帮助解析器/编译器提供更多的洞察力,对吧?我的意思是它在这两种情况下都清楚地知道发生了什么,并且可以以完全相同的方式做出反应。 编译器知道它认为代码是什么意思;它不知道你认为代码是什么意思。 对不起,我的意思是:如果你实现了 MyClass 的一个子类并且没有实现 MyFunc,那么编译器可以在这两种情况下告诉你到底出了什么问题:你没有实现抽象方法。如果您尝试实例化 MyClass,编译器可以准确地告诉您出了什么问题:MyClass 是抽象的(它甚至可以说“由于 MyFunc 是抽象的”)。这里的冗余与 Bright 所说的没有任何关系:解析/编译提示和返回给用户的通信。对于那些查看不同代码的人来说,这只是冗余。 顺便说一句,具有讽刺意味的是,您在片段中拼错了“抽象”:-)。 @Mark:具有讽刺意味的是,您在观察中拼错了“sn-p”。扮演纳粹语法角色的人自己犯语法或拼写错误的可能性似乎非常高,因此最好忽略除少数例外情况之外的所有情况。【参考方案5】:我认为他在谈论语言中的句法结构以及如何解释它们。例如,考虑用多种语言呈现的不起眼的“if”语句。
在 bash(shell 脚本)中,它看起来像这样:
if [ cond ]; then
stmts;
elif [ other_cond ]; then
other_stmts;
else
other_other_stmts;
fi
在 C 中(带单个语句,没有花括号):
if (cond)
stmt;
else if (other_cond)
other_stmt;
else
other_other_stmt;
您可以看到,在 bash 中,if 语句的语法结构比 C 中的要多得多。事实上,bash 中的所有控制结构都有自己的结束分隔符(例如 if/then/fi
、for/do/done
、 case/in/esac
,...),而在 C 语言中,花括号无处不在。这些独特的分隔符消除了代码含义的歧义,从而提供了解释器/编译器可以诊断错误情况并将其报告给用户的上下文。
然而,有一个权衡。与冗长的语法(la Pascal、Ada 等)相比,程序员通常更喜欢简洁的语法(la C、Lisp 等)。但是,他们也更喜欢包含行号/列号和建议解决方案的描述性错误消息。这些目标当然是相互矛盾的——你不能一边吃一边吃蛋糕(至少,同时保持编译器/解释器的内部实现简单)。
【讨论】:
考虑所有这些语言的布局和语法基础的所有这些设计权衡是非常巧妙的。它真的让你意识到,将语言的某些方面,例如,它的语法选择,作为任意的或仅仅是“风格”的功能,是有点幼稚的。所有这些选择都是有意识地并且(我猜至少在好的语言的情况下)理性地做出的。在所有这些相互冲突的目标之间进行权衡的想法也有助于解释为什么会有 1000 多种语言存在 :) 还有while;do;done
。我也不认为这真的是设计使然。只是传统sh的神器。然后,当然还有 Java,它有非常冗长的标识符和一些大括号。
@tc:同意——我并不是说它是故意引入冗余的,只是它产生了这种效果。老实说,考虑到它通常的简洁性(一个字符的特殊变量等),我对选择 sh/bash 的冗余感到有点内疚。这只是结构(if/else)的冗长版本的一个明显示例,可以用另一种语言更简洁(更少冗余)表达。【参考方案6】:
这意味着语法包含的信息比编码工作程序所需的信息多。一个例子是函数原型。正如 K&R C 向我们展示的那样,它们是多余的,因为编译器可以让调用者推送您想要的任何参数,然后让函数弹出正确的参数。但是 C++ 和其他语言要求它们,因为它们帮助编译器检查您是否以正确的方式调用函数。
另一个例子是需要在使用变量之前声明它们。有些语言有这个,而另一些则没有。这显然是多余的,但它通常有助于防止错误(例如拼写错误、使用已删除的变量)。
【讨论】:
基本正确,虽然这个例子不是最好的:头文件只是“帮助”编译器,因为它们使单遍编译器成为可能(因为禁止前向引用)。 @delnan,我不关注。在 C 语言中,没有标头,编译器根本无法检查您的函数调用。充其量,你会得到一个链接器错误。因此,头文件中的冗余信息使编译器可以保护程序员免受某些错误的影响。 转发声明 != 标头。前向声明使单遍编译器成为可能。当类型信息不是目标文件格式的一部分时,标头可以进行单独编译。 啊,是的,当然它需要其他方法来获取类型信息。愚蠢的我,只是认为这是理所当然的。猜猜现代语言是如何处理的;)编译器在提供实现的文件中查找。是的,这可以防止单独编译(如果语言更容易解析,或者使用类似 D 接口的预编译头等价物,则问题不大)并且不考虑已经编译的静态库(即没有源)。顺便说一句,标头也不能保证太多 - 如果源和标头是异步的,编译器仍然很高兴。 @Matthew:我认为你是对的:多通道编译器在这里无关紧要。您需要的是保留类型信息的“对象”格式(例如,Java 类文件),或者消除单独编译,而是使用一次查看所有内容的“全局”编译器。【参考方案7】:我认为更好的冗余示例是 int a[10] =
。在这一点上,编译器知道接下来应该发生什么,一个 int 数组初始化程序,并且如果后面的不是一个 int 数组初始化程序,则可以提出适当的错误消息。如果语言语法说任何东西都可以跟随int a[10]
,那么编译器就很难找出其中的问题。
【讨论】:
【参考方案8】:那么任何随机的字符序列都是有效的程序。
虽然不完全是“任何随机序列都是有效的”,但请考虑 Perl 和正则表达式。它们非常短的语法使无效字符更容易通过语法和语义分析。
【讨论】:
以上是关于Walter Bright 使用了“冗余”这个词……或者“这到底是啥意思?”的主要内容,如果未能解决你的问题,请参考以下文章
我写springboot定制启动的banner.txt文件时使用 $AnsiColor.BRIGHT_YELLOW代码,为啥颜色没有改变
The Datawarehouse's future is bright
Russell Westbrook and Kawhi Leonard's Bright Jordan 30s
lunrjs - A bit like Solr, but much smaller and not as bright.