编译原理陈火旺 第二章 习题分析

练习

1.写出下列程序设计语言所采用的输人字母表并进行比较:

(1)Pascal(2)C(3)Ada(4)FORTRAN(5)Java

我的答案:

各种程序设计语言所采用的输入字母表通常指的是这些语言在源代码中能够接受和识别的字符集。这些字符集包括字母、数字、标点符号以及其他特殊字符。不同的编程语言设计时会基于各自的语法规则、目的以及兼容性需求选择支持的字符集。

  1. Pascal:Pascal 语言设计中采用了ASCII字符集,它包括英文大写和小写字母(A-Z, a-z)、数字(0-9)以及特定的标点符号和控制字符。

  2. C:C 语言同样基于ASCII字符集,支持英文大写和小写字母、数字以及多种标点符号和控制字符。C语言的特性使得它能够处理各种类型的数据,包括字符数据。

  3. Ada:Ada 语言支持的输入字母表较为广泛,它不仅包括了ASCII字符集,还可能支持扩展字符集以满足国际化需求。Ada语言特别注重程序的可读性和可维护性,因此它在字符集支持上可能比其他更早期的语言有更多的考虑。

  4. FORTRAN:FORTRAN 语言最初设计于20世纪50年代,最早版本主要支持ASCII字符集中的大写字母(A-Z)、数字(0-9)以及一些特殊字符。随着标准的更新,现代FORTRAN语言版本支持了更完整的ASCII字符集,包括小写字母。

  5. Java:Java 语言设计时考虑了国际化需求,支持Unicode字符集,这使得它能够处理几乎所有语言的文本数据。Java的这一特性使其在全球范围内的软件开发中非常有用。

比较

  • 字符集宽度:Java 显然在这方面是最广泛的,因为它支持Unicode,能够处理全球绝大多数语言的字符。Ada也可能支持扩展字符集。而Pascal、C和早期的FORTRAN主要限于ASCII字符集。
  • 国际化支持:Java 和 Ada 在设计时更加考虑了国际化的需求,Java通过支持Unicode来实现这一点,而Ada则可能通过支持扩展的字符集来实现。
  • 历史背景:FORTRAN 和 Pascal 作为较早期的语言,其设计中支持的字符集相对较为有限。C语言虽然也是基于ASCII,但它的设计使其在处理字符数据方面有更广泛的应用。
  • 兼容性与扩展性:随着时间的推移和技术的发展,旧的编程语言标准也会更新来支持更广泛的字符集。例如,FORTRAN 和 Pascal 在最新的标准中可能已经增加了对小写字母以及其他字符的支持。

2.关于变量、数组说明,练习1中所列的各种语言有何相同或不同之处?

变量和数组的声明在不同编程语言中有着各自的规范和语法,但它们共同的目的是为了在程序中存储数据。下面是针对Pascal、C、Ada、FORTRAN和Java这几种语言变量和数组声明的一些相同点和不同点的总结:

相同之处

  • 变量声明:所有这些语言都需要在使用变量之前进行声明,这是为了指定变量的类型和有时候是初始值。这有助于编译器进行类型检查和内存分配。
  • 数据类型:它们都支持基本的数据类型,如整数、浮点数、字符等。
  • 数组使用:在所有这些语言中,数组用于存储相同类型的多个数据项,并且通常通过索引来访问数组的元素。

不同之处

  • 语法差异:虽然它们的目标相同,但声明变量和数组的具体语法在各个语言之间有所不同。
    • Pascal:变量和数组声明较为严格,需要在特定的部分(如var部分)内进行声明。数组声明时需指定范围。
      var
        i: integer;
        a: array[1..10] of integer;
      
    • C:提供了更多的灵活性,变量可以在函数的开始处或在代码中首次使用前声明。数组的大小必须是常量表达式。
      int i;
      int a[10];
      
    • Ada:类似于Pascal,但提供了更多的类型安全和抽象级别。Ada支持类型声明,可以创建新的类型或子类型。
      i : Integer;
      A : array (1 .. 10) of Integer;
      
    • FORTRAN:变量和数组声明具有唯一的风格,通常使用类型前缀来声明变量。
      INTEGER I
      INTEGER A(10)
      
    • Java:支持面向对象的概念,变量和数组的声明同时支持基本类型和对象类型。数组可以动态或静态分配。
      int i;
      int[] a = new int[10];
      
  • 类型系统和安全性:Ada和Java在类型系统和安全性方面提供了更多的功能和检查,以防止类型错误。C语言虽然灵活,但类型安全性较低,易于出错。
  • 动态数组和内存管理:Java提供了动态数组(例如ArrayList),自动管理内存和垃圾回收。C语言需要手动管理内存,使用诸如mallocfree的函数来操作。Pascal、Ada和FORTRAN在这方面介于两者之间,提供了不同程度的内存管理功能和安全性。

总的来说,尽管所有这些语言都支持变量和数组的概念,但它们在声明语法、类型安全性、内存管理以及支持的数据类型方面各有特色。这些差异反映了每种语言的设计哲学、应用领域和历史背景。

3何谓“标识符”,何谓“名字”,两者的区别是什么?

4.令+、*和1代表加、乘和乘幂,按如下的非标准优先级和结合性质的约定,计算1+1*212*1+2的值:

(1)优先顺序(从高至低)为+,*和1,同级优先采用左结合。

(2)优先顺序为1,+,*,同级优先采用右结合。

 

5.对你所熟悉的某种语言中的某种基本数据类型:

(1)措述这种类型数据对象可以包含的值:

(2)确定这种类型的值的存储表示:

(3)定义这种类型的常量的语法表示;

(4)确定对这种类型的数据对象可以进行哪些运算;

(5)对每一种运算,确定它的实现是通过软件仿真还是仅用一条简单的硬件指令?

(6)对这种类型的数据进行运算的合法性是静态确定还是必须动态确定?

6.令文法G6为

N->DIND

D-0111213141516171819讲放-啡

(1)G6的语言L(G6)是什么?

(2)给出句子0127、34和568的最左推导和最右推导。

7.写一个文法,使其语言是奇数集,且每个奇数不以0开头。

我的答案:

为了定义一个文法,使其语言是不以0开头的奇数集,我们需要首先明确奇数的特征:奇数是以1、3、5、7、9结尾的数字。同时,不以0开头的要求意味着第一个数字可以是1-9中的任何一个。我们可以使用下面的上下文无关文法(CFG)来描述这种语言:

设文法 G=(V,Σ,R,S),其中:

  • V 是变量集合。
  • Σ 是终结符集合,这里为 {1,2,3,4,5,6,7,8,9,0}{1,2,3,4,5,6,7,8,9,0}。
  • R 是规则集合。
  • S 是开始符号。

我们可以定义 V={S,O,E,N},其中:

  • S 是开始符号,代表任何不以0开头的奇数。
  • O 代表奇数位(1,3,5,7,9)。
  • E 代表偶数位(0,2,4,6,8)。
  • N 代表任意数字(0-9)。

然后,Σ={0,1,2,3,4,5,6,7,8,9}Σ={0,1,2,3,4,5,6,7,8,9}。

规则 R 包含:

  1. S→[1−9]N∗O
  2. O→1∣3∣5∣7∣9
  3. E→0∣2∣4∣6∣8
  4. N→0∣1∣2∣3∣4∣5∣6∣7∣8∣9
  5. N∗→NN∗∣ϵ

这里,N∗ 表示数字 N 可以出现0次或多次,ϵ 表示空字符串。

简单来说,这个文法的含义是:

  • 一个不以0开头的奇数由一个1到9之间的数字开始,后面跟着任意数量的数字(包括0),并以1、3、5、7、9其中一个数字结束。
  • O 确保数字以奇数结束,N∗ 允许在开头数字和结束的奇数位之间插入任意数量的数字,包括0。
  • 通过限制开始符号不包含0,确保了不以0开头的条件。

正确答案:

这个给出的文法 G(S) 是为了生成不以 0 开头的奇数集。规则如下:

  • O→1∣3∣5∣7∣9:奇数位可以是 1, 3, 5, 7, 或 9。
  • N→2∣4∣6∣8∣0:偶数位可以是 2, 4, 6, 8, 或 0。
  • D→0∣N:数字可以是 0 或任何偶数位。
  • S→O∣AO:一个奇数可以是一个单独的奇数位,或者由其他部分后跟一个奇数位组成。
  • A→AD∣N:可以是任何数字后跟一个数字,或者是一个单独的数字。

从这个文法生成的语言确实是所有的奇数集合,而且没有一个数字是以 0 开头的。由于在规则 S 中,奇数的生成要么是直接由一个奇数位 O 开始,要么是以非零的 A 作为开头后跟一个奇数位 O,这确保了所有生成的数字都不会以 0 开始。

8.令文法为

E-TIE+TIE-T

T-→FIT*FIT/F

F-(E) li

(1)给出i+i*i、i*(i+i)的最左推导和最右推导;

 

(2)给出i+i+i.i+i*和;-i-i的语法树。

 

 总结:语法树的画法

(1)首先我们先把目标的式子进行最左推导

(2)第二根据最左推导我们就可以按顺序画树

(9)证明下面的文法是二义的:

SiSeslisli

我的答案:

一个文法被称为是二义的(或者说有歧义),如果存在至少一个字符串,对于该字符串有两棵或更多不同的解析树(或者等价地,有两个或更多不同的最左推导或最右推导)。对于给出的文法:

S → iSeS | iS | i

我们可以证明它是二义的,通过找到一个字符串,并为该字符串构造两个不同的最左推导或者解析树。

考虑字符串 "iiei"。这个字符串可以由给出的文法生成,并且存在两个不同的推导过程。

推导过程一:

  1. S → iSeS
  2. S → iS (第二个 S 的替换)
  3. S → i (第二个 S 的替换)
  4. 整个过程就是 S → iSeS → iiSeS → iieiS → iiei

推导过程二:

  1. S → iSeS
  2. S → iSeS (第一个 S 的替换)
  3. S → i (第一个 S 的替换)
  4. 整个过程就是 S → iSeS → iSeSeS → iieSeS → iieiS → iiei

在这两个推导过程中,字符串 "iiei" 的 'ei' 部分是由不同的 S 替换得到的。在推导一中,它是由第二个 S → iSeS 的第一个 S 生成的;而在推导二中,它是由第一个 S → iSeS 的第二个 S 生成的。这意味着对于字符串 "iiei" 存在两个不同的最左推导,从而构成了语言的歧义。

因此,由于我们能够为同一个字符串构造两个不同的推导过程,我们可以证明给定的文法是二义性的。

10.把下面文法改写为无二义的:

S->SS|(S)|( )

要将给出的文法改写为无二义性的文法,我们首先要识别出导致二义性的部分。给定的文法:

S -> SS S -> (S) S -> ( )

这个文法描述了一种只包含括号的语言,但是由于递归的定义方式,它允许一个表达式以多种方式分析,从而导致二义性。例如,字符串 "(( ))(( ))" 可以有多种分析方式,这导致了二义性。

为了消除二义性,我们可以引入更多的非终结符来区分不同的结构。一个常见的方法是分别定义“最小的”单元和如何从这些单元构建更大的结构。这样可以确保每个字符串只有一种唯一的分析方式。

改写后的无二义文法如下:

S -> S S S -> (T) T -> S T -> ε

在这里,我们引入了一个新的非终结符 T,它可以是 S(即另一个括号序列)或者是 ε(即空序列,表示括号内没有内容)。这种方式确保了括号内的内容总是被视为一个单元,从而消除了二义性。

然而,这个解决方案仍可能不完全消除原文法的所有二义性,因为它依赖于具体实现和解析策略。完全消除给定文法的二义性可能需要更复杂的重写策略,包括更细致的分解和可能的语法制约。不过,上述改写是朝着消除二义性迈出的一步,具体实施时可能还需要进一步细化。

正确答案:

您提供的转换是一种将原始给定的文法:

S -> SS | (S) | ( )

改写为无二义性的形式:

S -> TS | T T -> (S) | ( )

这个改写的目的是消除原始文法中的二义性,使得每个字符串都有唯一的派生树。在上述无二义性版本中,S 代表可以由一个或多个 T 构成的序列,而 T 代表了基本结构:要么是括号内的 S,要么是一对空括号。通过这种方式,改写确保了生成的语言中的每个句子都可以通过一个唯一的解析方式来解释,这有助于语法分析的过程,并使得由该文法生成的语言更加清晰和易于处理。

11.给出下面语言的相应文法

L,=[a"b"e'Ina1,i=0]

Lz=[a'b|n=1,i≥0

L,=|a"b'a"b"|n,m≥0]

L,=1"0"1"0"|n,m≥0

A-BC

A-→a

S-e

其中,A.B.C、S为非终结符;S为开始符号。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值