【问题标题】:Why does a programming language need keywords?为什么编程语言需要关键字?
【发布时间】:2010-03-16 05:49:44
【问题描述】:

例如(在 C 中):

int break = 1;
int for = 2;

为什么编译器在推断breakfor 是这里的变量时会有任何问题?


所以,我们需要关键字,因为

  • 我们希望程序可读
  • 我们不想让当今已经很复杂的编译器的工作过于复杂
  • 但最重要的是,如果为某些特殊操作保留一些“关键字”,则语言会更强大。然后,该语言可以考虑在更高的层次上发挥作用,而不是死在尝试以明确的方式实现 for 循环。

【问题讨论】:

  • 编译器只是不想做努力:)
  • 仅供参考,有些语言没有关键字(Lisp 和 Smalltalk,我想不到)。我想关键字使语言更易于解析。而且我敢打赌,在某些情况下需要使用关键字来消除歧义。
  • @musicfreak: car 和 cdr 是什么?
  • 我的另一辆车是 cdr:stackoverflow.com/questions/1864795/…
  • @musicfreak:以下是 Lisp 中的关键字:and、begin、case、cond、define、delay、do、else、if、lambda、let、letrec 或 quasiquote、quote、set、 unquote, unquote-splicing。我认为 car 和 cdr 是关键字,但我发现它们和 cons 不是。但是,由于它们是由内部函数实现的,因此每个实现要么保留 car、cdr、cons,要么可能首先保留、rest、concat。

标签: c keyword identifier


【解决方案1】:

没必要——Fortran 没有保留任何字词,所以像:

if if .eq. then then if = else else then = if endif

完全合法。这不仅使 编译器 难以解析该语言,而且人们通常几乎不可能阅读或发现错误。例如,考虑经典的 Fortran(比如说,一直到 Fortran 77——我最近没有使用它,但至少希望他们在最近的标准中修复了一些类似的东西)。 Fortran DO 循环如下所示:

DO 10 I = 1,10

如果没有他们并排,您可能会看到您会怀念这有何不同:

DO 10 I = 1.10

不幸的是,后者根本不是 DO 循环——它只是将值 1.10 简单地赋值给名为 DO 10 I 的变量(是的,它还允许名称中有空格)。由于 Fortran 还支持隐式(未声明的)变量,所以这是(或曾经)完全合法的,一些编译器甚至会在没有警告的情况下接受它!

【讨论】:

  • 我不知道它已经发布在这里,但这并不让我感到惊讶。要记住的另一件事是“,”和“。”彼此相邻,所以它甚至不是什么罕见的问题(当我编写 Fortran 时,你大约每三个月左右遇到一次 - 不像错误计算的 Hollerith 常数那样频繁,但仍然经常需要检查循环是否行为不端。
  • 另一个令人困惑的好例子,假设有一个 3 维数组 if,并且你正在做一个算术 if:if(if(1,2,3))1,2,3
  • 别忘了 Python(3.0 之前):true = false
【解决方案2】:

那么当计算机遇到如下语句时会做什么:

while(1) {
  ...
  if (condition)
    break;
}

它真的应该破裂吗?还是应该将其视为1;

语言在某些情况下会变得模棱两可,或者您必须创建一个非常智能的解析器来推断微妙的语法,而这只是不必要的额外工作。

【讨论】:

  • 如果你声明了一个名为while的函数指针(或函数),那就更糟了...
  • @Mac:目前尚不清楚(至少对我而言)您打算通过观察来传达什么,而在给出的示例中发生在中断之前。你这是什么意思?
  • @Heath Hunnicutt:我认为 caf 只是展示了一个“他打算传达”的例子。
  • @Mac,C中没有true这样的关键字。
【解决方案3】:

他们没有。众所周知,PL/1 没有关键字;每个“关键字”(BEGIN、DO、...)也可以用作变量名。但允许这意味着您可以编写非常晦涩的代码: IF DO>BEGIN THEN PRINT:=CALL-GOTO; 将“语句关键字”保留为语言通常不会造成损失 的名字是谦虚的(因为它是我见过的除了 PL/1 之外的每一种语言 :-)。

APL 也以没有关键字着称。但它有一组大约 200 个令人惊叹的标志性符号,可用于编写复杂的运算符。 (“多米诺”运算符[不要问!] 是一个中间有一个计算器分隔符号的方框)在这种情况下,语言设计者只是使用图标而不是关键字。结果是 APL 享有“只写”语言的美誉。

底线:不是必需的,但如果关键字是程序员已知的一小部分保留标识符,它往往会使程序更具可读性。 (一些语言坚持“关键字”以“.”之类的特殊标点符号开头,以允许使用所有可能的标识符,但这不值得额外的打字麻烦或页面上的混乱;它很容易当关键字集较小时,远离匹配关键字的“标识符”。

【讨论】:

  • APL 最初是一种描述算法的数学语法。在一些多语种机构(例如 ESA 或 CERN)中,缺少关键字或文本运算符实际上成为了优势。但是,很容易完全混淆。
  • 优秀的 APL 程序员不会对运算符是什么或做什么感到困惑。他们确实对 APL 语句试图完成的任务感到完全困惑。
  • 实际上,APL 确实有关键字,或者被大多数 APL 编码器视为关键字的东西,系统函数和变量。当然,这些都是以特殊字符 {quad} 开头的,它在变量名或定义函数名中是无效的。许多方言也使用关键字实现控制结构,但这些都以冒号开头,这也不是变量或函数的有效名称。
  • IIRC, APL\360 有很多隐藏在 后面的特殊功能。您可能会将这些视为“关键字”,但它们不是传统的。我不知道“现代”APL(更不用说它的后代,“J”)去了哪里。很高兴有美好的回忆,就这样吧。
【解决方案4】:

由于它被标记为 C,因此原始 C 语言默认情况下任何变量都定义为类型 int

这意味着foo; 将声明一个int 类型的变量。

假设您使用break;。那么编译器怎么知道你是要声明一个名为break的变量还是使用关键字break呢?

【讨论】:

    【解决方案5】:

    几个原因:

    • 您的示例中的关键字可能看起来很明确。但这不是您使用变量“break”或变量“for”的唯一地方。

    • 编写解析器会更加困难,而且容易出错,而且收益甚微。

    • 在库中使用关键字作为函数或过程名称可能会产生不良的、可能与安全相关的副作用。

    【讨论】:

    • 给出的示例很弱,因为它不包含三元素 arglist,或者就此而言,任何可以被视为任何类型的 arglist 的东西。分号毁了它。尝试用idwhile 重写您的示例,因为它们只采用一个带括号的表达式
    【解决方案6】:

    正如其他人所说,这使编译器更容易解析您的源代码。 但我还想说一点:它还可以让你的源代码更具可读性;考虑这个例子:

    if (if > 0) then = 10 end if

    第二个“if”和第二个“then”是变量,而其他不是。我认为这种代码不可读。 :)

    【讨论】:

    • 除了编译器的人,谁在乎编译器的工作量?恕我直言,他每次都在这个话题上被否决,这是正确的;他的工作是让每个人的工作更轻松,而不是相反。这个问题实际上是关于程序员的可用性。
    • @Ira Baxter:不仅仅是编译器。如果你想写一个程序分析工具或重构工具什么的,你必须解析语言。此外,如果您让构建解析器变得昂贵,那么您在编译器的其他方面工作的资源就会减少。
    • @Thornley:没错,如果可以避免的话,您不希望“前端”更加努力地工作。但是,关键字与否不会改变平均解析成本;对于 GLR 解析器和几乎所有现代语言,解析成本是线性的,具有较小的常数; (我已经使用 GLR 解析器实现了数十个前端 semanticdesigns.com/Products/DMS/FrontEnds.html 并通过经验验证了这一点)。分析步骤的成本通常要高得多,因为它需要更多的推理,至少在做任何有趣的事情时是这样。
    【解决方案7】:

    如果我们谈论 C++ - 它已经有非常复杂的语法。例如,允许使用关键字作为变量名会使它变得更加复杂。

    【讨论】:

      【解决方案8】:

      如果你这样写,编译器会出现问题:

      while(*s++);
      return(5);
      

      这是一个循环还是对名为while 的函数的调用?您是想从当前函数返回值 5,还是要调用名为 return 的函数?

      如果具有特殊含义的结构仅具有可用于明确引用它们的特殊名称,则通常可以简化事情。

      【讨论】:

      • 许多不保留关键字的语言通过将这些关键字视为上下文相关关键字来处理此类情况(甚至 C# 也这样做)。也就是说,如果该词可能被特别解释(例如,作为“while 语句”),它被视为这样;哪里没有,这不是问题。您上面的 while 语句没有问题;它显然不能是一个 while 语句,因为没有要执行的主体。您给出的返回语句示例很好;如果没有上下文关键字规则,它显然会模棱两可,这将使它成为一个返回语句。
      • @Ira:至少在 C 语言中(问题已被标记),这是一个有效的 while 语句。 while 的主体为空没有问题,它不会在那里执行任何操作。
      • 是的,你是对的,因为 C 的空语句语法。你仍然可以在没有真正的关键字的情况下解决它的上下文依赖性:这现在显然是一个“while”循环:-}你必须使用 (*(&while))(*s++);强制函数调用。
      【解决方案9】:

      因为我们想保留我们所拥有的一点点理智:

      void myfunction(bool) { .. };
      
      funcp while = &myfunction;
      while(true); 
      

      【讨论】:

      • 您忘记定义 true = false。在相关说明中,“#define while myfunction”在 c 中工作
      • define 与关键字不同。
      • 只是认为如果您可以重新定义关键字,它们不会有助于保持清醒。如果预处理器指令算作语言的一部分,那么只有那些被保留,所有其他关键字都可以重新定义。
      【解决方案10】:

      我想编写解析器看起来很奇怪,如果不是不可能的话。例如

      int break = 1;
      while (true) {
         // code to change break
         if (!break) break;   // not very readable code.
      }
      

      【讨论】:

        【解决方案11】:

        根据语言定义,编译器可能需要也可能不需要关键字。当它不知道该做什么时,它可以尝试应用优先规则或失败。
        一个例子:

        void return(int i){printf("%d",i);}
        public int foo(int a)
        {
          if(a > 2)return (a+1)*2;
          return a + 3;
        }
        

        如果 a 大于 2 会发生什么?

        • 语言规范可能 要求编译器失败
        • 语言规范可能要求编译器使用返回 功能
        • 语言规范可能要求编译器返回

        您可以定义一种不使用关键字的语言。您甚至可以定义一种允许您替换所有符号的语言(因为它们本身只是非常短的关键字)。
        问题不在于编译器,如果您的规范完整且没有错误,它将起作用。问题是 PEBCAD,使用这种语言特性的程序将难以阅读,因为您必须跟踪符号定义。

        【讨论】:

          【解决方案12】:

          FWIW,Tcl 没有任何保留字。您可以拥有名为“if”、“break”等的变量和函数。标记的解释完全取决于上下文。同一个标记可以在一个上下文中表示一个命令,在另一个上下文中表示一个变量,或者在另一个上下文中表示一个文字字符串。

          【讨论】:

            【解决方案13】:

            在许多情况下,编译器可以将关键字解释为普通标识符,例如您的示例:

            int break = 1;
            int for = 2;
            

            事实上,我只是为一种简单的类似汇编的玩具语言编写了一个编译器,它可以做到这一点,但在这种情况下会警告用户。

            但有时语法以关键字和标识符不明确的方式定义:

            int break;
            
            while(...)
            {
                break; // <-- treat this as expression or statement?
            }
            

            最明显的原因是编辑器会强调关键字,这样代码对人类来说更具可读性。允许将关键字视为标识符会使代码高亮变得更加困难,并且还会导致代码的可读性差。

            【讨论】:

              猜你喜欢
              • 2019-02-19
              • 1970-01-01
              • 2015-04-26
              • 2014-09-23
              • 1970-01-01
              • 2018-11-02
              • 1970-01-01
              • 2011-04-09
              • 2020-05-20
              相关资源
              最近更新 更多