1.设 Σ = 0 , 1 \Sigma = {0, 1} Σ=0,1 ,请给出 Σ \Sigma Σ 中的下列语言的文法
(1)所有以 0 开头的串。
解1:
S → 0 ∣ S 0 ∣ S 1 S \rightarrow 0 | S0 | S1 S→0∣S0∣S1
S 先生成任意的 0,1 串,最后在这个 0,1 串之前生成一个 0,从而保证生成的串是以 0 开头的串。
解2:
S → 0 A ∣ 0 S \rightarrow 0A | 0 S→0A∣0
A → 0 ∣ 1 ∣ 0 A ∣ 1 A A \rightarrow 0 | 1 | 0A | 1A A→0∣1∣0A∣1A
S 先生成 0A,然后“将任务交给变量 A",由 A 生成 0 后面的任意 0,1 符号串。
(2)所有以 0 开头,以 1 结尾的串。
解1:
S → 0 A 1 S \rightarrow 0A1 S→0A1
A → ϵ ∣ 0 A ∣ 1 A A \rightarrow \epsilon | 0A | 1A A→ϵ∣0A∣1A
先由 S 生成以 0 开头以 1 结尾的句型,然后由 0,1 之间的 A 生成中间部分。由于 01 本身也是满足要求的串,所以 A 可以产生 ϵ \epsilon ϵ。
解 2:
S → 0 A S \rightarrow 0A S→0A
A → 1 ∣ 0 A ∣ 1 A A \rightarrow 1 | 0A | 1A A→1∣0A∣1A
用产生式 S → 0 A S \rightarrow 0A S→0A 保证产生的字符串是以 0 开头的,产生式 A → 0 A ∣ 1 A A \rightarrow 0A | 1A A→0A∣1A 被用来生成开头字符 0 之后,结尾字符 1 之前的所有 0,1 子串,产生式 A → 1 A \rightarrow 1 A→1 是使除了 S 之外的所有其他句型中的唯一变量 A 变成终极符号的唯一产生式,该产生式保证了在句型的尾部生成一个 1 → 句子是以1结尾的。
(3)所有以 11 开头,以 11 结尾的串。
解:本题的解法与上一题类似,只不过是用 11 分别代替了字符串的首字符 0 和字符串的尾字符 1,其他位置的字符不变。另外,它还有两个特殊的句子 11 和 111,这是由开头的 11 与结尾的 11 一样所决定的,这里将它们作为特例处理。因此,相应的文法如下。对比本题的解法与上题的解法,读者可以进一步理解文法是经过对语言的结构的描述来定义语言的。所以,要构造一个给定语言的文法,最重要的是找出该语言的结构特征。
解1:
S → 11 A 11 ∣ 111 ∣ 11 S \rightarrow 11A11 | 111 | 11 S→11A11∣111∣11
A → ϵ ∣ 0 A ∣ 1 A A \rightarrow \epsilon | 0A | 1A A→ϵ∣0A∣1A
解2:
S → 11 A ∣ 111 ∣ 11 S \rightarrow 11A | 111 | 11 S→11A∣111∣11
A → 11 ∣ 0 A ∣ 1 A A \rightarrow 11 | 0A | 1A A→11∣0A∣1A
(4)所有 0 和 1 构成的但不含 00 的串。
文法规则:
S -> A | B
A -> 1A | 1B | 0
B -> 1B | 0B | 1 | 0
(5)所有 0 和 1 构成的含有形如 10110 的子串。
文法规则:
S -> A10110B
A -> 0A | ε
B -> 1B | ε
(6)含偶数个 0 的二进制数组成的串。
文法规则:
S -> AA
A -> 00A | 11A | ε
2.指出下列 Lex 正则式所匹配的字符串:
(1)" { " [ ^ { ] * " } "
该正则表达式匹配以 "{ " 开头,以 " } " 结尾,中间可以包含任意不是 “{” 的字符。
例如,匹配的字符串可以是:
- “{ }”
- “{ abc }”
- “{ 123 }”
(2)[ ^ 0-9] | [ \r \n ]
该正则表达式匹配任何不是数字(0-9)的字符,或者回车符(\r)或换行符(\n)。
例如,匹配的字符串可以是:
- “a”
- “$”
- “\r”
- “\n”
(3)\ ’ ( [ ^ ’ \n ] | \’ \’ ) + \’
该正则表达式匹配由单引号包围的字符串,字符串内部可以包含任意不是单引号的字符,或者两个连续的单引号(表示转义的单引号)。
例如,匹配的字符串可以是:
- ‘hello’
- ‘I’m’
- ‘John’s’
(4)\ " ([ ^ " \n ] | [ " \n ] ) * \"
该正则表达式匹配由双引号包围的字符串,字符串内部可以包含任意不是双引号的字符,或者双引号和换行符。
例如,匹配的字符串可以是:
- “hello”
- “I"m”
- “John’s”
3.已知文法 G [ E ] G[E] G[E]
(1) E → a A ∣ b B E \rightarrow aA | bB E→aA∣bB
(2) A → c A ∣ d A \rightarrow cA | d A→cA∣d
(3) B → c B ∣ d B \rightarrow cB | d B→cB∣d
构造该文法的 L R ( 0 ) LR(0) LR(0) 分析表
4.设有文法 G [ S ] G[S] G[S]
S → a ∣ ( T ) ∣ ∗ S \rightarrow a | (T) | * S→a∣(T)∣∗
T → T , S ∣ S T \rightarrow T , S | S T→T,S∣S
① 构造此文法的 L R ( 0 ) LR(0) LR(0) 项目集规范族,并给出识别活前缀的 DFA。
② 构造其 L R ( 0 ) LR(0) LR(0) 分析表。
5.请利用代码优化的思想(代码外提和强度削弱等),改写下面程序中的循环,得到优化后的 C 语言程序。
Main( )
{
int i, j;
int S[20][50];
for( i = 0; i < 20; i++)
{
for(j = 0; j < 50; j++)
S[i][j] = 10 * i * j;
}
}
【解答】 本题用到的代码优化技术主要有代码外提和强度消弱。 10 ∗ i 10 * i 10∗i在内循环是不变计算,可以外提,于是在外循环有 k = 10 ∗ i k = 10 * i k=10∗i,在内循环有 r [ i ] [ j ] = k ∗ j r[i][j] = k * j r[i][j]=k∗j;然后把这两个乘法进行轻度消弱,在循环中使用加法,在外循环每次加
10 10 10, r [ i ] [ j ] r[i][j] r[i][j] 在内循环每次加 k k k。
C语言中允许使用指针访问数组的首地址,二维数组在存储区域是按行排列。这样,通过指针的加法运算就可以访问数组元素,避免在循环内部寻址的重复运算。优化后的C语言程序如下:Main() { int i, j, m, n, k; int* p; int r[20][10]; p = &r[0][0]; n = 0; for(i = 0; i < 20; i++) { k = n; n + =10; m = 0; for(j = 0; j < 10; j++) { * p = m; p++; m + = k; } } }