Compiler Jozve
Compiler Jozve
٢
ﺷﻤﺎره ﺻﻔﺤﻪ ﻋﻨﻮان
اﻧﻮاع ﺗﺪاﺧﻞ در ﺗﺠﺰﻳﻪ اﻧﺘﻘﺎل – ﻛﺎﻫﺶ 55.....................................................................
روش ﺗﺠﺰﻳﻪ ﺗﻘﺪم – ﻋﻤﻠﮕﺮ 57.....................................................................................
ﻣﻌﺎﻳﺐ روش ﭘﺎرس ﺗﻘﺪم – ﻋﻤﻠﮕﺮ 57...........................................................................
ﻧﺤﻮه ﻳﺎﻓﺘﻦ رواﺑﻂ ﺗﻘﺪم 60 ...........................................................................................
اﺻﻼح ﺧﻄﺎ در روش ﺗﻘﺪم – ﻋﻤﻠﮕﺮ 63 .......................................................................
روش ﺗﺠﺰﻳﻪ ﺗﻘﺪم ﺳﺎده 65 ...........................................................................................
ﻣﺸﻜﻼت ﭼﭗ ﮔﺮدي و راﺳﺖ ﮔﺮدي در روش ﺗﻘﺪم ﺳﺎده 67 .......................................
روﺷﻬﺎي ﺗﺠﺰﻳﻪ 70 ............................................................................................... LR
اﻟﮕﻮرﻳﺘﻢ ﺗﺠﺰﻳﻪ 71 ................................................................................................ LR
ﻧﺤﻮه ﺗﻬﻴﻪ ﺟﺪول )74 .................................................................................... SLR(1
رﺳﻢ دﻳﺎﮔﺮام اﻧﺘﻘﺎل 75 ....................................................................................... SLR
دﻳﺎﮔﺮام ﻫﺎي ﺟﺪول ﺗﺠﺰﻳﻪ CLRو 79 ......................................................... LALR
ﻃﺮﻳﻘﻪ رﺳﻢ دﻳﺎﮔﺮام 79 ..................................................................................... CLR
رﺳﻢ دﻳﺎﮔﺮام و ﺟﺪول ﺗﺠﺰﻳﻪ 81 ................................................................... LALR
٣
اﺻﻮل ﻃﺮاﺣﻲ و ﺳﺎﺧﺖ ﻛﺎﻣﭙﺎﻳﻠﺮﻫﺎ
ﺗﻌﺮﻳﻒ ﻛﺎﻣﭙﺎﻳﻠﺮ : 1-1
ﻛﺎﻣﭙﺎﻳﻠﺮ ) ( Compilerﺑﺮﻧﺎﻣﻪ اي اﺳﺖ ﻛﻪ ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﻧﻮﺷﺘﻪ ﺷﺪه در ﻳﻚ زﺑﺎن ﺑﻪ ﻧﺎم
زﺑﺎن ﻣﻨﺒﻊ ) ( Source Languageرا ﺑﻪ ﺑﺮﻧﺎﻣﻪ اي ﻣﻌﺎدل ﺑﻪ زﺑﺎﻧﻲ دﻳﮕﺮ ﺑﻪ ﻧﺎم زﺑﺎن
ﻣﻘﺼﺪ ) ( Target Languageﺗﺮﺟﻤﻪ ﻣﻲ ﻛﻨﺪ.
Error Messages
2-1ﻣﺮاﺣﻞ ﻛﺎﻣﭙﺎﻳﻞ
ﻋﻤﻠﻴﺎت ﻛﺎﻣﭙﺎﻳﻞ در ﺷﺶ ﻣﺮﺣﻠﻪ زﻳﺮ ﺻﻮرت ﻣﻲ ﮔﻴﺮد :
-1ﺗﺤﻠﻴﻞ واژه اي ) ( Lexical Analysis
-2ﺗﺤﻠﻴﻞ ﻧﺤﻮي ) ( Syntax Analysis
-3ﺗﺤﻠﻴﻞ ﻣﻌﻨﺎﻳﻲ ) ( Semantic Analysis
-4ﺗﻮﻟﻴﺪ ﻛﺪ ﺑﻴﻨﺎﺑﻴﻨﻲ ) ( Intermediate Code Generation
-5ﺑﻬﻴﻨﻪ ﺳﺎزي ﻛﺪ ) ( Code Optimization
-6ﺗﻮﻟﻴﺪ ﻛﺪ ﻧﻬﺎﻳﻲ ) ( Code Generation
٤
در ﻛﻨﺎر ﺷﺶ ﻣﺮﺣﻠﻪ اﺻﻠﻲ.ارﺗﺒﺎط ﺑﻴﻦ اﻳﻦ ﻣﺮاﺣﻞ در ﺷﻜﻞ زﻳﺮ ﻧﺸﺎن داده ﺷﺪه اﺳﺖ
) ( و ﺟﺪول ﻋﻼﺋﻢError Handler ) ﻛﺎﻣﭙﺎﻳﻠﺮ دو ﺑﺨﺶ دﻳﮕﺮ ﺑﻨﺎم ﺧﻄﺎﭘﺮداز
. ( ﻧﻴﺰ وﺟﻮد داردSymbol Table
Source Program
Lexical Analysis
Lexical Analysis
Syntax Analysis
Semantic Analysis
Code Optimization
Code Generation
Target Program
٥
در ﻣﺮﺣﻠﻪ اول ﻛﺎﻣﭙﺎﻳﻞ ﻳﻌﻨﻲ ﺗﺤﻠﻴﻞ واژه اي ﺑﺮﻧﺎﻣﻪ ورودي ﻧﻮﻳﺴﻪ ﺑﻪ ﻧﻮﻳﺴﻪ ﺧﻮاﻧﺪه ﺷﺪه و ﺑﻪ
دﻧﺒﺎﻟﻪ اي از ﻧﺸﺎﻧﻪ ﻫﺎ ) ( Tokensﺗﺒﺪﻳﻞ ﻣﻲ ﮔﺮدد.اﻧﻮاع ﻣﺨﺘﻠﻒ ﻧﺸﺎﻧﻪ ﻫﺎ ﻋﺒﺎرﺗﻨﺪ از :
ﻛﻠﻤﺎت ﻛﻠﻴﺪي ) , ( Keywordsﻋﻤﻠﮕﺮﻫﺎ ) , ( Operatorsﺟﺪاﻛﻨﻨﺪه ﻫﺎ )
, ( Delimetersﺛﺎﺑﺖ ﻫﺎ ) , ( Literalsﺷﻨﺎﺳﻪ ﻫﺎ ) ( Identifiersﻛﻪ ﺑﻪ اﺳﺎﻣﻲ
ﻣﺘﻐﻴﺮﻫﺎ و ﺗﻮاﺑﻊ و روﻳﻪ ﻫﺎ و ﺑﻄﻮر ﻛﻠﻲ اﺳﺎﻣﻲ ﻛﻪ ﻛﺎرﺑﺮ اﻧﺘﺨﺎب ﻣﻲ ﻛﻨﺪ ﮔﻔﺘﻪ ﻣﻲ ﺷﻮد.در
اﻏﻠﺐ زﺑﺎﻧﻬﺎي ﺑﺮﻧﺎﻣﻪ ﺳﺎزي ﻛﻠﻤﺎت ﻛﻠﻴﺪي رزرو ﺷﺪه اﻧﺪ ﺑﺪﻳﻦ ﻣﻌﻨﻲ ﻛﻪ ﻛﺎرﺑﺮ ﻣﺠﺎز ﻧﻴﺴﺖ
از ﻫﻴﭽﻴﻚ از آﻧﻬﺎ ﺑﻪ ﻋﻨﻮان اﺳﻢ ﻳﻚ ﻣﺘﻐﻴﺮ ،ﺗﺎﺑﻊ و ﻳﺎ روﻳﻪ اﺳﺘﻔﺎده ﻧﻤﺎﻳﺪ.اﻣﺎ در ﺑﺮﺧﻲ از
زﺑﺎﻧﻬﺎ ﻣﺜﻞ PL/1اﻳﻦ ﻣﺤﺪودﻳﺖ وﺟﻮد ﻧﺪارد.
در ﻣﺮﺣﻠﻪ دوم ﺑﺮﻧﺎﻣﻪ از ﻧﻈﺮ ﺧﻄﺎﻫﺎي ﻧﺤﻮي ﻣﻮرد ﺑﺮرﺳﻲ ﻗﺮار ﻣﻲ ﮔﻴﺮد و ﺑﺎ اﺳﺘﻔﺎده از
ﻧﺸﺎﻧﻪ ﻫﺎي ﺗﻮﻟﻴﺪ ﺷﺪه در ﻣﺮﺣﻠﻪ ﺗﺤﻠﻴﻞ واژه اي ﻳﻚ درﺧﺖ ﻧﺤﻮ )( Syntax Tree
اﻳﺠﺎد ﻣﻲ ﮔﺮدد.
در ﻣﺮﺣﻠﻪ ﺳﻮم ﺑﺎ اﺳﺘﻔﺎده از درﺧﺖ ﻧﺤﻮ ﺗﻮﻟﻴﺪ ﺷﺪه در ﻣﺮﺣﻠﻪ ﻗﺒﻠﻲ ﺑﺮﻧﺎﻣﻪ ورودي از ﻧﻈﺮ
ﺧﻄﺎﻫﺎي ﻣﻔﻬﻮﻣﻲ اﺣﺘﻤﺎﻟﻲ ﺑﺮرﺳﻲ ﻣﻲ ﺷﻮد.
در ﻣﺮﺣﻠﻪ ﺗﻮﻟﻴﺪ ﻛﺪ ﺑﻴﻨﺎﺑﻴﻨﻲ ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﻛﻪ ﻣﻌﺎدل ﺑﺮﻧﺎﻣﻪ اﺻﻠﻲ اﺳﺖ ﺑﺎ ﻳﻚ زﺑﺎن ﺑﻴﻨﺎﺑﻴﻨﻲ
ﺗﻮﻟﻴﺪ ﻣﻲ ﺷﻮد.ﺑﺎ اﻳﺠﺎد اﻳﻦ ﻛﺪ ﺑﻴﻨﺎﺑﻴﻨﻲ ﻋﻤﻠﻴﺎت ﺑﻌﺪي ﻛﻪ ﻛﺎﻣﭙﺎﻳﻠﺮ ﺑﺎﻳﺪ اﻧﺠﺎم دﻫﺪ آﺳﺎن ﻣﻲ
ﮔﺮدد.در اﻧﺘﺨﺎب زﺑﺎن ﺑﻴﻨﺎﺑﻴﻨﻲ ﺑﺎﻳﺪ ﻣﻮارد زﻳﺮ در ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﺷﻮﻧﺪ :
.1ﺗﻮﻟﻴﺪ و ﺑﻬﻴﻨﻪ ﺳﺎزي ﻛﺪ ﺑﻴﻨﺎﺑﻴﻨﻲ ﺑﺎﻳﺪ ﺳﺎده ﺑﺎﺷﺪ.
.2ﺗﺮﺟﻤﻪ آن ﺑﻪ ﺑﺮﻧﺎﻣﻪ ﻣﻘﺼﺪ ﻧﻴﺰ ﺑﻪ راﺣﺘﻲ ﺻﻮرت ﭘﺬﻳﺮد.
در ﻣﺮﺣﻠﻪ ﺑﻬﻴﻨﻪ ﺳﺎزي ﻛﻮﺷﺶ ﻣﻲ ﺷﻮد ﺗﺎ ﻛﺪ ﺑﻴﻨﺎﺑﻴﻨﻲ ﺗﻮﻟﻴﺪ ﺷﺪه در ﻣﺮﺣﻠﻪ ﻗﺒﻠﻲ ﺑﻪ ﻧﺤﻮي
ﺑﻬﺒﻮد داده ﺷﻮد ﺑﻄﻮرﻳﻜﻪ اﻳﻦ ﻛﺎر ﻣﺴﺒﺐ ﺗﻮﻟﻴﺪ ﻛﺪي ﻣﻲ ﺷﻮد ﻛﻪ از ﻟﺤﺎظ اﺟﺮاﻳﻲ ﺳﺮﻳﻌﺘﺮ
ﻣﻲ ﺑﺎﺷﺪ.
ﺳﺮاﻧﺠﺎم در ﺑﺨﺶ ﺗﻮﻟﻴﺪ ﻛﺪ ﻧﻬﺎﻳﻲ ﻛﺪ ﻣﻮردﻧﻈﺮ ﺑﺼﻮرت ﺑﺮﻧﺎﻣﻪ ﻣﻘﺼﺪ ﺗﻮﻟﻴﺪ ﻣﻲ
ﺷﻮد.ﺑﻌﺒﺎرت دﻳﮕﺮ ﻫﺮ ﻛﺪام از ﻛﺪﻫﺎي ﺑﻴﻨﺎﺑﻴﻨﻲ ﺑﻬﺒﻮد ﻳﺎﻓﺘﻪ ﺑﻪ ﻣﺠﻤﻮﻋﻪ اي از دﺳﺘﻮرات
ﻣﺎﺷﻴﻦ ﻛﻪ ﻛﺎر ﻣﺸﺎﺑﻬﻲ اﻧﺠﺎم ﻣﻲ دﻫﻨﺪ ﺗﺒﺪﻳﻞ ﻣﻲ ﮔﺮدﻧﺪ.
٦
ﺧﻄﺎﭘﺮداز ) ( Error Handler
ﻫﺮ ﺑﺎر ﻛﻪ ﺧﻄﺎﻳﻲ در ﻳﻜﻲ از ﻣﺮﺣﻠﻪ ﻫﺎ ﭘﻴﺶ ﺑﻴﺎﻳﺪ روﻳﻪ اي ﺑﻨﺎم ﺧﻄﺎﭘﺮداز ﻓﺮاﺧﻮاﻧﺪه ﻣﻲ
ﺷﻮد .اﻳﻦ ﺑﺨﺶ ﺳﻌﻲ ﻣﻲ ﻛﻨﺪ ﺧﻄﺎ را ﺑﻪ ﻧﺤﻮي ﺑﺮﻃﺮف ﻛﻨﺪ ﻛﻪ در ﻧﺘﻴﺠﻪ ﻛﺎﻣﭙﺎﻳﻠﺮ ﺑﺘﻮاﻧﺪ
ﺧﻄﺎﻫﺎي ﺑﻴﺸﺘﺮي را در ﺑﺮﻧﺎﻣﻪ ﺗﺸﺨﻴﺺ دﻫﺪ و ﺑﺎ اوﻟﻴﻦ ﺧﻄﺎي ﻣﻮﺟﻮد در ﺑﺮﻧﺎﻣﻪ ﻋﻤﻞ
ﻛﺎﻣﭙﺎﻳﻞ ﻣﺘﻮﻗﻒ ﻧﮕﺮدد.ﻣﻌﻤﻮﻻ ﭘﺎرﺳﺮ و اﺳﻜﻨﺮ ﺑﻴﺸﺘﺮ ﺧﻄﺎﻫﺎﻳﻲ را ﻛﻪ در ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﻣﻤﻜﻦ
اﺳﺖ وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﺪ ﺗﺸﺨﻴﺺ ﻣﻲ دﻫﻨﺪ.
ﻳﻜﻲ از ﻛﺎرﻫﺎي ﻣﻬﻢ و اﺳﺎﺳﻲ ﻳﻚ ﻛﺎﻣﭙﺎﻳﻠﺮ ﺛﺒﺖ ﺷﻨﺎﺳﻪ ﻫﺎي اﺳﺘﻔﺎده ﺷﺪه در ﺑﺮﻧﺎﻣﻪ
ورودي ) ﻣﻨﺒﻊ ( و ﺟﻤﻊ آوري اﻃﻼﻋﺎت درﺑﺎره ﻣﺸﺨﺼﺎت ﻫﺮ ﺷﻨﺎﺳﻪ اﺳﺖ.اﻳﻦ ﻣﺸﺨﺼﺎت
ﻣﻲ ﺗﻮاﻧﻨﺪ ﺷﺎﻣﻞ :آدرس ﺣﺎﻓﻈﻪ اﺧﺘﺼﺎص داده ﺷﺪه ﺑﻪ ﺷﻨﺎﺳﻪ ,ﻧﻮع آن ,ﻣﺤﻠﻲ از ﺑﺮﻧﺎﻣﻪ
ﻛﻪ اﻳﻦ ﺷﻨﺎﺳﻪ در آن ﺗﻌﺮﻳﻒ ﺷﺪه اﺳﺖ ) , ( Scopeو در راﺑﻄﻪ ﺑﺎ روﻳﻪ ﻫﺎ اﺳﻢ آﻧﻬﺎ ,
ﺗﻌﺪاد و ﻧﻮع آرﮔﻮﻣﺎﻧﻬﺎي آﻧﻬﺎ ,روﺷﻲ ﻛﻪ ﺑﻪ آن ﻃﺮﻳﻖ آرﮔﻮﻣﺎﻧﻬﺎ ﺑﻪ روﻳﻪ ﻫﺎ ﻓﺮﺳﺘﺎده ﻣﻲ
ﺷﻮﻧﺪ.ﻣﺜﻼ Call by Referenceﻳﺎ Call by Valueو ﻧﻮع ﻧﺘﻴﺠﻪ اي ﻛﻪ روﻳﻪ ﻫﺎ
ﺑﺎز ﻣﻲ ﮔﺮداﻧﻨﺪ ﺑﺎﺷﺪ.
در ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ ﺑﻪ ازاي ﻫﺮ ﺷﻨﺎﺳﻪ ﻳﻚ رﻛﻮرد وﺟﻮد دارد ﻛﻪ اﻳﻦ رﻛﻮردﻫﺎ ﺷﺎﻣﻞ
ﻣﺸﺨﺼﺎت ﺷﻨﺎﺳﻪ ﻫﺎ ﻣﻲ ﺑﺎﺷﻨﺪ.اﻳﻦ ﺟﺪول اﻣﻜﺎن دﺳﺘﻴﺎﺑﻲ ﺳﺮﻳﻊ ﺑﻪ ﺷﻨﺎﺳﻪ ﻫﺎ و ﻣﺸﺨﺼﺎت
آﻧﻬﺎ را ﺑﻪ ﻣﺎ ﻣﻲ دﻫﺪ.در ﻛﺎﻣﭙﺎﻳﻠﺮ و در ﻣﺮﺣﻠﻪ ﺗﺤﻠﻴﻞ ﻟﻐﻮي ﻛﻠﻴﻪ ﺷﻨﺎﺳﻪ ﻫﺎي ﻣﻮﺟﻮد در
ﺑﺮﻧﺎﻣﻪ اﺻﻠﻲ وارد ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ ﻣﻲ ﺷﻮﻧﺪ.در ﻣﺮﺣﻠﻪ ﻫﺎي دﻳﮕﺮ ﻛﺎﻣﭙﺎﻳﻞ اﻳﻦ اﻃﻼﻋﺎت ﺑﻪ
ﺟﺪول اﺿﺎﻓﻪ ﺧﻮاﻫﻨﺪ ﺷﺪ و ﺳﭙﺲ از آﻧﻬﺎ در ﻣﻮارد ﻣﺨﺘﻠﻒ اﺳﺘﻔﺎده ﺧﻮاﻫﺪ ﺷﺪ.
٧
p := i + r * 60
Lexical Analyzer
Syntax Analyzer
:=
id1 +
id2
*
id3 60
Semantic Analyzer
:=
id1 +
٨
id2 *
id3 inttoreal
60
t1 := inttoreal(60)
t2 := id3 * t1
t3 := id2 + t2
id1 := t3
Code Optimizer
t1 := id3 * 60.0
id1 := id2 + t1
Code Generation
movF id3,R2
mulF #60.0,R2
٩
movF id2,R1
ADDF R2,R1
movF R1,id1
Symbol
Table
ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل در ﺻﻮرﺗﻲ ﻛﻪ رﺷﺘﻪ ورودي A := B + Cﺑﺎﺷﺪ ﺗﻮﻛﻦ ﻫﺎي زﻳﺮ ﺗﺸﺨﻴﺺ
داده ﺧﻮاﻫﻨﺪ ﺷﺪ :
١٠
آدرس < id , Cو > < add. Op.و > آدرس < id , Bو > < ass. Op.و > آدرس < id , A
ﺑﻨﺎﺑﺮاﻳﻦ اﺳﻜﻨﺮ ﻋﻼوه ﺑﺮ اﻳﻦ ﻛﻪ ﺗﺸﺨﻴﺺ ﻣﻲ دﻫﺪ ﻛﻪ ﺗﻮﻛﻦ ﻳﻚ ﺷﻨﺎﺳﻪ اﺳﺖ آدرس آن
در ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ را ﻧﻴﺰ ﺑﺮاي ﭘﺎرﺳﺮ ﻣﻲ ﻓﺮﺳﺘﺪ.ﻋﻼوه ﺑﺮ اﻳﻦ اﺳﻜﻨﺮ ﻣﻲ ﺗﻮاﻧﺪ ﻣﺤﻞ ﻫﺎي
ﺧﺎﻟﻲ و ﺗﻮﺿﻴﺤﺎت ) ( Commentsﻣﻮﺟﻮد در ﺑﺮﻧﺎﻣﻪ اﺻﻠﻲ را ﺿﻤﻦ ﺧﻮاﻧﺪن ﺑﺮﻧﺎﻣﻪ
ﺣﺬف ﻧﻤﺎﻳﺪ.
ﺑﻪ آﺧﺮﻳﻦ ﺗﻮﻛﻨﻲ ﻛﻪ اﺳﻜﻨﺮ ﻳﺎﻓﺘﻪ اﺳﺖ ﻋﻼﻣﺖ ﭘﻴﺶ ﺑﻴﻨﻲ ) ( Lookahead Symbolو
ﻳﺎ ﺗﻮﻛﻦ ﺟﺎري ﮔﻔﺘﻪ ﻣﻲ ﺷﻮد.
١١
در ﺑﻌﻀﻲ وﺿﻌﻴﺖ ﻫﺎ اﺳﻜﻨﺮ ﻗﺒﻞ از اﻳﻨﻜﻪ ﺗﺼﻤﻴﻢ ﺑﮕﻴﺮد ﭼﻪ ﺗﻮﻛﻨﻲ را ﺑﻪ ﭘﺎرﺳﺮ ﺑﻔﺮﺳﺘﺪ ﻧﻴﺎز
دارد ﻛﻪ ﭼﻨﺪ ﻛﺎراﻛﺘﺮ دﻳﮕﺮ ﻧﻴﺰ از ورودي ﺑﺨﻮاﻧﺪ.ﺑﻌﻨﻮان ﻣﺜﺎل اﺳﻜﻨﺮ ﺑﺎ دﻳﺪن ﻋﻼﻣﺖ ' > '
در ورودي ﻧﻴﺎز دارد ﻛﻪ ﻛﺎراﻛﺘﺮ ورودي ﺑﻌﺪي را ﻧﻴﺰ ﺑﺨﻮاﻧﺪ در ﺻﻮرﺗﻲ ﻛﻪ اﻳﻦ ﻛﺎراﻛﺘﺮ =
ﺑﺎﺷﺪ ﺗﻮﻛﻦ ' => ' و در ﻏﻴﺮ اﻳﻨﺼﻮرت ﺗﻮﻛﻦ ' > ' ﺗﺸﺨﻴﺺ داده ﻣﻲ ﺷﻮد.در ﻣﻮرد آﺧﺮ
ﺑﺎﻳﺪ ﻛﺎراﻛﺘﺮ اﺿﺎﻓﻲ ﺧﻮاﻧﺪه ﺷﺪه دوﺑﺎره ﺑﻪ ورودي ﺑﺎزﮔﺮداﻧﺪه ﺷﻮد.
ﻳﻜﻲ دﻳﮕﺮ از ﻣﺸﻜﻼﺗﻲ ﻛﻪ اﺳﻜﻨﺮ ﺑﺎ آن روﺑﺮو اﺳﺖ در زﺑﺎﻧﻬﺎﻳﻲ ﻧﻈﻴﺮ Fortranاﺳﺖ.در
اﻳﻦ ﻗﺒﻴﻞ زﺑﺎﻧﻬﺎ ﻣﺤﻞ ﺧﺎﻟﻲ ) ( Blankﺑﺠﺰ در رﺷﺘﻪ ﻫﺎي ﻛﺎراﻛﺘﺮي ﻧﺎدﻳﺪه ﮔﺮﻓﺘﻪ ﻣﻲ
ﺷﻮد.ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل ﻛﻠﻤﻪ Doدر ﻓﺮﺗﺮن در دﺳﺘﻮر زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ :
Do 5 I = 1.25ﺗﺎ زﻣﺎﻧﻲ ﻛﻪ ﺑﻪ ﻧﻘﻄﻪ اﻋﺸﺎر در 1.25ﻧﺮﺳﻴﺪه ﺑﺎﺷﻴﻢ ﻧﻤﻲ ﺗﻮان ﮔﻔﺖ ﻛﻪ
Doدر اﻳﻦ دﺳﺘﻮر ﻛﻠﻤﻪ ﻛﻠﻴﺪي ﻧﻴﺴﺖ ﺑﻠﻜﻪ ﺑﺨﺸﻲ از ﻣﺘﻐﻴﺮ Do5Iاﺳﺖ.ﺑﻪ ﻫﻤﻴﻦ ﺗﺮﺗﻴﺐ
در دﺳﺘﻮر Do 5 I = 1 , 25ﺗﺎ زﻣﺎﻧﻲ ﻛﻪ ﻋﻼﻣﺖ ﻛﺎﻣﺎ دﻳﺪه ﻧﺸﻮد ﻧﻤﻲ ﺗﻮان ﮔﻔﺖ ﻛﻪ
اﻳﻦ ﻳﻚ ﺣﻠﻘﻪ Doاﺳﺖ.
در زﺑﺎﻧﻬﺎﻳﻲ ﻛﻪ در آﻧﻬﺎ ﻛﻠﻤﺎت ﻛﻠﻴﺪي ) ( Keywordﺟﺰو ﻛﻠﻤﺎت رزرو ﺷﺪه ﻧﻴﺴﺘﻨﺪ
ﻧﻈﻴﺮ PL/1اﺳﻜﻨﺮ ﻧﻤﻲ ﺗﻮاﻧﺪ ﻛﻠﻤﺎت ﻛﻠﻴﺪي را از ﺷﻨﺎﺳﻪ ﻫﺎي ﻫﻤﻨﺎم آﻧﻬﺎ ﺗﺸﺨﻴﺺ دﻫﺪ.ﺑﻪ
ﻋﻨﻮان ﻣﺜﺎل در دﺳﺘﻮر زﻳﺮ :
; IF Then THEN Then = Else ; ELSE Else = Then
ﺟﺪاﻛﺮدن ﻛﻠﻤﻪ ﻛﻠﻴﺪي THENاز ﻣﺘﻐﻴﺮ Thenﺑﺴﻴﺎر ﻣﺸﻜﻞ اﺳﺖ.در اﻳﻦ ﻣﻮارد ﻣﻌﻤﻮﻻ
ﭘﺎرﺳﺮ ﺗﺸﺨﻴﺺ ﻧﻬﺎﻳﻲ را ﺧﻮاﻫﺪ داد.
١٢
از آﻧﺠﺎﻳﻲ ﻛﻪ fiﻳﻚ ﻣﺘﻐﻴﺮ ﻣﺠﺎز اﺳﺖ اﺳﻜﻨﺮ اﻳﻦ ﺗﻮﻛﻦ را ﺑﻪ ﻋﻨﻮان ﻳﻚ ﺷﻨﺎﺳﻪ ﺑﻪ ﭘﺎرﺳﺮ
ﻣﻲ ﻓﺮﺳﺘﺪ ﺗﺎ اﻳﻨﻜﻪ ﭘﺎرﺳﺮ در اﻳﻦ ﻣﻮرد ﺗﺼﻤﻴﻢ ﺑﮕﻴﺮد.اﻣﺎ ﻣﻤﻜﻦ اﺳﺖ ﺧﻄﺎﻫﺎﻳﻲ ﭘﻴﺶ ﺑﻴﺎﻳﺪ
ﻛﻪ اﺳﻜﻨﺮ ﻗﺎدر ﺑﻪ اﻧﺠﺎم ﻫﻴﭻ ﻋﻤﻠﻲ ﻧﺒﺎﺷﺪ.در اﻳﻦ ﺣﺎﻟﺖ ﺑﺮﻧﺎﻣﻪ ﺧﻄﺎﭘﺮداز )Error-
( Handlerﻓﺮاﺧﻮاﻧﺪه ﻣﻲ ﺷﻮد ﺗﺎ آن ﺧﻄﺎ را ﺑﻪ ﻧﺤﻮي ﺑﺮﻃﺮف ﻛﻨﺪ.روﺷﻬﺎي ﻣﺨﺘﻠﻔﻲ
ﺑﺮاي اﻳﻨﻜﺎر وﺟﻮد دارد ﻛﻪ ﺳﺎده ﺗﺮﻳﻦ آﻧﻬﺎ روﺷﻲ ﻣﻮﺳﻮم ﺑﻪ " " Panic Mode
اﺳﺖ.در اﻳﻦ روش آﻧﻘﺪر از رﺷﺘﻪ ورودي ﺣﺬف ﻣﻲ ﺷﻮد ﺗﺎ اﻳﻨﻜﻪ ﻳﻚ ﺗﻮﻛﻦ درﺳﺖ
ﺗﺸﺨﻴﺺ داده ﺷﻮد.
ﺳﺎﻳﺮ روﺷﻬﺎي ﺗﺼﺤﻴﺢ ﺧﻄﺎ ) ( Error-Recoveryﻋﺒﺎرﺗﻨﺪ از :
-1ﺣﺬف ﻳﻚ ﻛﺎراﻛﺘﺮ ﻏﻴﺮﻣﺠﺎز ) ﺗﺒﺪﻳﻞ = :$ﺑﻪ =( :
-2وارد ﻛﺮدن ﻳﻚ ﻛﺎراﻛﺘﺮ ﮔﻢ ﺷﺪه ) ﺗﺒﺪﻳﻞ :ﺑﻪ =( :
-3ﺗﻌﻮﻳﺾ ﻛﺮدن ﻳﻚ ﻛﺎراﻛﺘﺮ ﻏﻠﻂ ﺑﺎ ﻳﻚ ﻛﺎراﻛﺘﺮ درﺳﺖ ) ﺗﺒﺪﻳﻞ ::ﺑﻪ =( :
-4ﺟﺎﺑﺠﺎ ﻛﺮدن دو ﻛﺎراﻛﺘﺮ ﻣﺠﺎز ) ﺗﺒﺪﻳﻞ =:ﺑﻪ =( :
١٣
وارد ﺑﺎﻓﺮ ﻣﻲ ﮔﺮدد.ﻛﺎراﻛﺘﺮ eofﺑﻴﺎﻧﮕﺮ ﭘﺎﻳﺎن ﻓﺎﻳﻞ ﻣﻨﺒﻊ ﺑﻮده و ﺑﺎ ﺳﺎﻳﺮ ﻛﺎراﻛﺘﺮﻫﺎي ورودي
ﺑﻪ ﻧﻮﻋﻲ ﺗﻔﺎوت دارد.
Forward
Lexeme-Beginning
در ﺑﺎﻓﺮ ورودي از دو ﻧﺸﺎﻧﻪ رو اﺳﺘﻔﺎده ﻣﻲ ﺷﻮد.رﺷﺘﻪ ﻛﺎراﻛﺘﺮي ﺑﻴﻦ اﻳﻦ دو ﻧﺸﺎﻧﻪ رو ﻣﻌﺮف
Lexemeﺟﺎري ﻣﻲ ﺑﺎﺷﺪ.در اﺑﺘﺪا ﻫﺮ دو ﻧﺸﺎﻧﻪ رو ﺑﻪ اوﻟﻴﻦ ﻛﺎراﻛﺘﺮ Lexemeﺑﻌﺪي ﻛﻪ
ﺑﺎﻳﺪ ﭘﻴﺪا ﺷﻮد اﺷﺎره ﻣﻲ ﻛﻨﻨﺪ.ﻧﺸﺎﻧﻪ روي Forwardﭘﻴﺶ ﻣﻲ رود ﺗﺎ اﻳﻨﻜﻪ ﻳﻚ ﺗﻮﻛﻦ
ﺗﺸﺨﻴﺺ داده ﺷﻮد.
اﻳﻦ ﻧﺤﻮه اﺳﺘﻔﺎده از ﺑﺎﻓﺮ در ﺑﻴﺸﺘﺮ ﻣﻮارد ﻛﺎﻣﻼ ﺧﻮب ﻋﻤﻞ ﻣﻲ ﻛﻨﺪ.ﺑﺎ اﻳﻦ وﺟﻮد در ﻣﻮاردي
ﻛﻪ ﺟﻬﺖ ﺗﺸﺨﻴﺺ ﻳﻚ ﺗﻮﻛﻦ ,ﻧﺸﺎﻧﻪ رو Forwardﻧﺎﭼﺎر اﺳﺖ ﺑﻴﺸﺘﺮ از ﻃﻮل ﺑﺎﻓﺮ ﺟﻠﻮ
دﺳﺘﻮر ﻣﺜﺎل ﻋﻨﻮان ﻛﻨﺪ.ﺑﻪ ﻧﻤﻲ ﻛﺎر روش اﻳﻦ ﺑﺮود
) DECLARE (ARG1,ARG2,…,ARGnرا در ﻳﻚ ﺑﺮﻧﺎﻣﻪ PL/1در ﻧﻈﺮ
ﺑﮕﻴﺮﻳﺪ.در اﻳﻦ دﺳﺘﻮر ﺗﺎ زﻣﺎﻧﻲ ﻛﻪ ﻛﺎراﻛﺘﺮ ﺑﻌﺪ از ﭘﺮاﻧﺘﺰ ﺳﻤﺖ راﺳﺖ را ﺑﺮرﺳﻲ ﻧﻜﻨﻴﻢ ﻧﻤﻲ
ﺗﻮان ﮔﻔﺖ ﻛﻪ DECLAREﻳﻚ ﻛﻠﻤﻪ ﻛﻠﻴﺪي اﺳﺖ و ﻳﺎ ﻳﻚ اﺳﻢ آراﻳﻪ.
١٤
ﺑﺮاي ﻛﻨﺘﺮل ﺣﺮﻛﺖ ﻧﺸﺎﻧﻪ روي Forwardو ﻫﻤﭽﻨﻴﻦ ﻛﻨﺘﺮل ﺑﺎﻓﺮ ﻣﻲ ﺗﻮان ﺑﺼﻮرت زﻳﺮ
ﻋﻤﻞ ﻛﺮد :
If Forward is at end of first half Then begin
; reload Second-half
; Forward := Forward + 1
end
در اﻳﻦ ﻗﺴﻤﺖ اﮔﺮ ﻧﺸﺎﻧﻪ روي Forwardﺑﻪ اﻧﺘﻬﺎي ﻧﻴﻤﻪ اول ﺑﺎﻓﺮ رﺳﻴﺪ ,ﻧﻴﻤﻪ دوم ﺑﺎ N
ﻛﺎراﻛﺘﺮ ﺟﺪﻳﺪ ﭘﺮ ﺧﻮاﻫﺪ ﺷﺪ .
else if Forward is at end of second-half Then begin
; reload first-half
move Forward to beginning of first-half
end
; else Forward := Forward + 1
در ﺻﻮرﺗﻴﻜﻪ ﻧﺸﺎﻧﻪ روي Forwardﺑﻪ اﻧﺘﻬﺎي ﻧﻴﻤﻪ دوم ﺑﺎﻓﺮ ﺑﺮﺳﺪ ﻧﻴﻤﻪ اول ﺑﺎﻓﺮ را ﺑﺎ N
ﻛﺎراﻛﺘﺮ ﺟﺪﻳﺪ ﭘﺮ ﻣﻲ ﻛﻨﻴﻢ و ﻧﺸﺎﻧﻪ روي Forwardرا ﺑﻪ آﻏﺎز ﺑﺎﻓﺮ Setﻣﻲ ﻛﻨﻴﻢ.
E = M * eof C * * 2 eof
ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﺑﺮاي ﻛﻨﺘﺮل ﺣﺮﻛﺖ ﻧﺸﺎﻧﻪ روي Forwardﻣﻲ ﺗﻮاﻧﻴﻢ از اﻟﮕﻮرﻳﺘﻢ زﻳﺮ
اﺳﺘﻔﺎده ﻛﻨﻴﻢ :
١٥
; Forward := Forward + 1
if Forward = eof Then begin
if Forward is at end of first-half Then begin
; reload second-half
Forward := Forward + 1
end
else if Forward is at end of second-half Then begin
; reload first-half
move Forward to beginning of first-half
end
else
/* eof within a buffer signifying end of input */
Terminate Lexical Analysis
end.
a
١٦
a b b
Start S0 S1 S2 S3
b
١٧
a a
b
a
b
ﺑﺮﻧﺎﻣﻪ اي ﻛﻪ از ﻳﻚ دﻳﺎﮔﺮام ﻗﻄﻌﻲ اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ ﭘﻴﺎده ﺳﺎزي راﺣﺖ ﺗﺮي ﻧﺴﺒﺖ ﺑﻪ ﺑﺮﻧﺎﻣﻪ
ﻣﺒﺘﻨﻲ ﺑﺮ ﻳﻚ دﻳﺎﮔﺮام ﻏﻴﺮﻗﻄﻌﻲ دارد.ﺑﺮﻧﺎﻣﻪ ﻣﺒﺘﻨﻲ ﺑﺮ ﻳﻚ دﻳﺎﮔﺮام ﻏﻴﺮﻗﻄﻌﻲ ﺑﺎﻳﺴﺘﻲ داراي
ﻗﺎﺑﻠﻴﺖ ﭘﻲ ﺟﻮﻳﻲ ﻳﺎ Backtrackingﺑﺎﺷﺪ.از ﻃﺮف دﻳﮕﺮ دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل ﻗﻄﻌﻲ
ﻣﻌﻤﻮﻻ ﺗﻌﺪاد وﺿﻌﻴﺖ ﺑﻴﺸﺘﺮي ﻧﺴﺒﺖ ﺑﻪ دﻳﺎﮔﺮام ﻏﻴﺮﻗﻄﻌﻲ ﻣﻌﺎدل ﺧﻮد دارﻧﺪ .ﺑﻨﺎﺑﺮاﻳﻦ ﺑﺮاي
ﭘﻴﺎده ﺳﺎزي ﻳﻚ اﺳﻜﻨﺮ اﺑﺘﺪا دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل ﻣﻌﺮف اﻟﮕﻮي ﺗﻮﻛﻦ ﻫﺎي زﺑﺎن ﻣﻮردﻧﻈﺮ
رﺳﻢ ﻣﻲ ﮔﺮدد.اﻳﻦ دﻳﺎﮔﺮام ﻫﺎ ﺑﺮاي ﺑﺪﺳﺖ آوردن اﻃﻼﻋﺎت در ﻣﻮرد ﻛﺎراﻛﺘﺮﻫﺎﻳﻲ ﻛﻪ
ﺑﻮﺳﻴﻠﻪ ﻧﺸﺎﻧﻪ روي Forwardدر ورودي ﺑﺎﻳﺪ دﻳﺪه ﺷﻮﻧﺪ اﺳﺘﻔﺎده ﻣﻲ ﮔﺮدد.ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ
ﻛﻪ ﻫﻤﺎﻧﻄﻮر ﻛﻪ ﻛﺎراﻛﺘﺮﻫﺎي ورودي ﺧﻮاﻧﺪه ﻣﻲ ﺷﻮﻧﺪ از ﻳﻚ وﺿﻌﻴﺖ در دﻳﺎﮔﺮام ﺑﻪ
وﺿﻌﻴﺘﻲ دﻳﮕﺮ ﺣﺮﻛﺖ ﻣﻲ ﻛﻨﻴﻢ ﺗﺎ اﻳﻨﻜﻪ ﺑﻪ ﻳﻚ وﺿﻌﻴﺖ ﻧﻬﺎﻳﻲ ﺑﺮﺳﻴﻢ.ﭘﻴﻤﺎﻳﺶ دﻳﺎﮔﺮام از
وﺿﻌﻴﺖ ﺷﺮوع ) ( Startآﻏﺎز ﻣﻲ ﺷﻮد.
a c
b
١٨
ﻫﻨﮕﺎﻣﻴﻜﻪ در وﺿﻌﻴﺖ ﻓﻌﻠﻲ ﻟﺒﻪ اي ﻛﻪ ﺑﺮﭼﺴﺐ آن ﻣﺴﺎوي ﻛﺎراﻛﺘﺮ ورودي اﺳﺖ ﻗﺮار
داﺷﺘﻪ ﺑﺎﺷﻴﻢ ،از آن ﺣﺎﻟﺖ ﺑﻮﺳﻴﻠﻪ آن ﻟﺒﻪ ﺑﻪ ﺣﺎﻟﺖ ﺑﻌﺪي ﻣﻲ روﻳﻢ و در ﻏﻴﺮ اﻳﻨﺼﻮرت
ﺗﻮﻛﻦ ﺗﻮﺳﻂ اﻳﻦ دﻳﺎﮔﺮام ﻗﺎﺑﻞ ﺗﺸﺨﻴﺺ ﻧﺨﻮاﻫﺪ ﺑﻮد.
ﺑﺮﭼﺴﺐ " " otherدر روي ﻟﺒﻪ ﻳﻚ وﺿﻌﻴﺖ ﺑﻴﺎﻧﮕﺮ ﻫﺮ ﻛﺎراﻛﺘﺮي اﺳﺖ ﻛﻪ ﺗﻮﺳﻂ ﻟﺒﻪ
ﻫﺎي دﻳﮕﺮ آن وﺿﻌﻴﺖ ذﻛﺮ ﻧﺸﺪه اﻧﺪ.
ﻣﺜﺎل -دﻳﺎﮔﺮام ﺗﺸﺨﻴﺺ ﺗﻮﻛﻦ " => " ﺑﺼﻮرت زﻳﺮ اﺳﺖ :
> =
Start S2
S0 S1
other *
S3
letter other
*
Start S0 S1 S2
Letter / digit
ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﻳﻨﻜﻪ ﻛﻠﻤﺎت ﻛﻠﻴﺪي از ﻳﻚ دﻧﺒﺎﻟﻪ ﻛﺎراﻛﺘﺮي ﺗﺸﻜﻴﻞ ﺷﺪه اﻧﺪ ﻟﺬا ﻣﻲ ﺗﻮان از
دﻳـﺎﮔﺮام ﻓﻮق ﺑﺮاي ﺗﺸـﺨﻴﺺ ﻛﻠﻤﺎت ﻛﻠﻴـﺪي ﻧﻴﺰ اﺳـﺘﻔﺎده ﻧﻤﻮد.ﺗـﺸﺨﻴﺺ ﻛﻠـﻤﺎت ﻛﻠﻴﺪي
١٩
و ﺷﻨﺎﺳﻪ ﻫﺎ ﺗﻮﺳﻂ ﻳﻚ دﻳﺎﮔﺮام ﺑﺎﻋﺚ ﻛﺎﻫﺶ ﺗﻌﺪاد وﺿﻌﻴﺖ ﻫﺎي دﻳﺎﮔﺮام اﻧﺘﻘﺎل اﺳﻜﻨﺮ
ﻣﻲ ﮔﺮدد.ﺑﺮاي آﻧﻜﻪ ﻛﻠﻤﺎت ﻛﻠﻴﺪي را از ﺷﻨﺎﺳﻪ ﻫﺎي ﻫﻤﻨﺎﻣﺸﺎن ﺟﺪا ﺳﺎزﻳﻢ ﻳﻜﻲ از ﺳﺎده
ﺗﺮﻳﻦ روﺷﻬﺎ اﻳﻦ اﺳﺖ ﻛﻪ در اﺑﺘﺪا در ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ ﻛﻠﻤﺎت ﻛﻠﻴﺪي را وارد ﻛﻨﻴﻢ.ﺑﻪ اﻳﻦ
ﺗﺮﺗﻴﺐ ﺑﺎ رﺟﻮع ﺑﻪ ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ ﻣﻲ ﺗﻮان درﻳﺎﻓﺖ ﻛﻪ ﺗﻮﻛﻦ ﻣﻮرد ﻧﻈﺮ ﺷﻨﺎﺳﻪ اﺳﺖ ﻳﺎ
ﻛﻠﻤﻪ ﻛﻠﻴﺪي.
ﺟﺪول if ..... K
ﻧﺸﺎﻧﻪ هﺎ
٢٠
ﻛﻠﻴﺪي ﺗﻐﻴﻴﺮ ﻛﻨﺪ دﻳﺎﮔﺮام اﻧﺘﻘﺎل ﺑﺪون ﺗﻐﻴﻴﺮ ﺑﺎﻗﻲ ﺧﻮاﻫﺪ ﻣﺎﻧﺪ و ﺑﻪ راﺣﺘﻲ ﻣﻲ ﺗﻮان اﻳﻦ ﺗﻐﻴﻴﺮ
را در ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ اﻳﺠﺎد ﻛﺮد.
در اﻳﻨﺠﺎ ذﻛﺮ ﭼﻨﺪ ﻧﻜﺘﻪ در ﻣﻮرد ﻧﺤﻮه ﻗﺮار دادن دﻳﺎﮔﺮام اﻧﺘﻘﺎل ﺗﻮﻛﻦ ﻫﺎي ﻣﺨﺘﻠﻒ
ﺿﺮوري اﺳﺖ.اول آﻧﻜﻪ اﺳﻜﻨﺮ ﺑﺎﻳﺪ ﻫﻤﻮاره ﺳﻌﻲ ﻛﻨﺪ ﻃﻮﻻﻧﻲ ﺗﺮﻳﻦ ﺗﻮﻛﻦ ﻣﻤﻜﻦ را
ﺗﺸﺨﻴﺺ دﻫﺪ.ﻣﺜﻼ دﻳﺎﮔﺮام ﺗﺸﺨﻴﺺ اﻋﺪاد اﻋﺸﺎري ﺑﺎﻳﺴﺘﻲ ﻗﺒﻞ از دﻳﺎﮔﺮاﻣﻲ ﺑﺎﺷﺪ ﻛﻪ اﻋﺪاد
ﺻﺤﻴﺢ را ﺗﺸﺨﻴﺺ ﻣﻲ دﻫﺪ.ﻫﻤﭽﻨﻴﻦ دﻳﺎﮔﺮام ﺗﺸﺨﻴﺺ ﺗﻮﻛﻦ ﻫﺎﻳﻲ ﻛﻪ ﻣﻮرد اﺳﺘﻔﺎده
ﺑﻴﺸﺘﺮي در ﺑﺮﻧﺎﻣﻪ ﻫﺎ دارﻧﺪ ) ﻣﺜﻼ (... , tab , spaceﺑﺎﻳﺪ ﻗﺒﻞ از دﻳﺎﮔﺮام اﻧﺘﻘﺎل ﺗﻮﻛﻦ ﻫﺎي
ﻛﻤﻴﺎب ﺗﺮ ﻗﺮار ﮔﻴﺮد ﺗﺎ در ﻧﻬﺎﻳﺖ ﺗﺴﺖ ﻛﻤﺘﺮي ﺑﺮاي ﺗﺸﺨﻴﺺ ﺗﻮﻛﻦ ﻫﺎ اﻧﺠﺎم ﺷﻮد.ﻣﺜﻼ
ﺷﻜﻞ زﻳﺮ ﺗﺮﺗﻴﺐ ﻗﺮار ﮔﺮﻓﺘﻦ دﻳﺎﮔﺮام ﻫﺎ ﺑﺮاي ﺗﺸﺨﻴﺺ اﻋﺪاد را ﻧﺸﺎن ﻣﻲ دﻫﺪ.ﺷﻤﺎره
وﺿﻌﻴﺖ ﻫﺎ ﺑﻴﺎﻧﮕﺮ ﺗﺮﺗﻴﺐ دﻳﺎﮔﺮام ﻫﺎ اﺳﺖ.
digit
digit . digit E + or -
E digit
digit
*
7 6
other
digit
digit digit
digit
other
digit *
٢١
ﺑﻌﺪ از اﻳﻨﻜﻪ دﻳﺎﮔﺮام ﺗﺸﺨﻴﺺ ﺗﻮﻛﻦ ﻫﺎ رﺳﻢ ﺷﺪ ﺑﻪ راﺣﺘﻲ ﻣﻲ ﺗﻮان آﻧﺮا ﺑﺎ دﺳﺘﻮر Case
ﭘﻴﺎده ﺳﺎزي ﻧﻤﻮد.
ﻫﺮ دﻳﺎﮔﺮام اﻧﺘﻘﺎل ﻏﻴﺮﻗﻄﻌﻲ را ﻣﻲ ﺗﻮان ﺑﻪ ﻳﻚ ﮔﺮاﻣﺮ ﻣﺴﺘﻘﻞ از ﻣﺘﻦ /ﻣﻨﻈﻢ ﺗﺒﺪﻳﻞ ﻛﺮد.ﺑﺮاي
اﻳﻨﻜﺎر ﺑﺎﻳﺪ ﻣﺮاﺣﻞ زﻳﺮ را اﻧﺠﺎم داد :
.1ﺑﻪ ازاي ﻫﺮ ﺣﺎﻟﺖ iﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ Aiدر ﻧﻈﺮ ﻣﻲ ﮔﻴﺮﻳﻢ.
.2اﮔﺮ از ﺣﺎﻟﺖ iﺑﺎ ورودي αﺑﻪ ﺣﺎﻟﺖ jﻣﻲ روﻳﻢ ﻗﺎﻋﺪه اي ﺑﺼﻮرتAi → αAj
ﺗﻮﻟﻴﺪ ﻣﻲ ﻛﻨﻴﻢ.
.3اﮔﺮ از ﺣﺎﻟﺖ iﺑﺎ ورودي €ﺑﻪ ﺣﺎﻟﺖ jﻣﻲ روﻳﻢ ﻗﺎﻋﺪه اي ﺑﻪ ﻓﺮم Ai → Ajﺗﻮﻟﻴﺪ
ﻣﻲ ﻛﻨﻴﻢ.
.4اﮔﺮ iﻳﻚ ﺣﺎﻟﺖ ﻧﻬﺎﻳﻲ ﺑﺎﺷﺪ ﻗﺎﻋﺪه اي ﺑﻪ ﻓﺮم Ai → εﺗﻮﻟﻴﺪ ﻣﻲ ﻛﻨﻴﻢ.
.5اﮔﺮ iﺣﺎﻟﺖ ﺷﺮوع ﺑﺎﺷﺪ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ Aiرا ﺑﻪ ﻋﻨﻮان ﻋﻼﻣﺖ ﺷﺮوع ﮔﺮاﻣﺮ در ﻧﻈﺮ ﻣﻲ
ﮔﻴﺮﻳﻢ.
ﻣﺜﻼ ﮔﺮاﻣﺮ ﻣﺴﺘﻘﻞ از ﻣﺘﻦ دﻳﺎﮔﺮام ﻏﻴﺮﻗﻄﻌﻲ (a|b)*abbﺑﺼﻮرت زﻳﺮ ﺧﻮاﻫﺪ ﺑﻮد :
A0 → aA1 | aA0 |bA0
A1 → bA2
A2 → bA3
A3 → ε
اﮔﺮﭼﻪ ﻣﻲ ﺗﻮان ﻗﻮاﻋﺪ ﻟﻐﻮي زﺑﺎﻧﻬﺎ را ﺗﻮﺳﻂ ﮔﺮاﻣﺮﻫﺎي ﻣﺴﺘﻘﻞ از ﻣﺘﻦ ﻧﻴﺰ ﺑﻴﺎن ﻧﻤﻮد ﻟﻴﻜﻦ
دﻻﻳﻠﻲ وﺟﻮد دارد ﻛﻪ ﺑﻬﺘﺮ اﺳﺖ ﻗﻮاﻋﺪ ﻟﻐﻮي ﺗﻮﺳﻂ ﻋﺒﺎرات ﻣﻨﻈﻢ ﺗﻮﺻﻴﻒ ﺷﻮﻧﺪ :
.1ﻗﻮاﻋﺪ ﻟﻐﻮي زﺑﺎﻧﻬﺎ اﻏﻠﺐ ﺧﻴﻠﻲ ﺳﺎده ﻫﺴﺘﻨﺪ و ﺑﺮاي ﺗﻮﺻﻴﻒ آﻧﻬﺎ ﻧﻴﺎزي ﺑﻪ ﻧﻤﺎﻳﺶ
ﻗﻮي ﺗﺮ ﮔﺮاﻣﺮ ﻣﺴﺘﻘﻞ از ﻣﺘﻦ ﻧﻴﺴﺖ.
.2ﻋﺒﺎرات ﻣﻨﻈﻢ ﺑﻄﻮر ﻛﻠﻲ وﺳﻴﻠﻪ اي ﻓﺸﺮده ﺗﺮ و ﮔﻮﻳﺎﺗﺮ از ﮔﺮاﻣﺮﻫﺎي ﻣﺴﺘﻘﻞ از ﻣﺘﻦ
ﻫﺴﺘﻨﺪ.
.3اﺳﻜﻨﺮﻫﺎي ﺳﺮﻳﻌﺘﺮي را ﻣﻲ ﺗﻮان ﺑﺼﻮرت ﺧﻮدﻛﺎر از روي ﻋﺒﺎرات ﻣﻨﻈﻢ ﺗﻮﻟﻴﺪ
ﻛﺮد.
٢٢
.4ﺟﺪاﻛﺮدن ﺳﺎﺧﺘﺎر دﺳﺘﻮري ﻳﻚ زﺑﺎن ﺑﻪ دو ﺑﺨﺶ ﻟﻐﻮي و ﻏﻴﺮﻟﻐﻮي ﻛﺎر ﭘﻴﺎده
ﺳﺎزي ﻗﺴﻤﺖ Front-Endﻛﺎﻣﭙﺎﻳﻠﺮﻫﺎ ﺑﺼﻮرت ﭘﻴﻤﺎﻧﻪ اي را راﺣﺖ ﺗﺮ ﻣﻲ ﺳﺎزد.
token
Symbol-Table
٢٣
روﺷﻬﺎي ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ,درﺧﺖ ﺗﺠﺰﻳﻪ ) ( Parse Treeرا از ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ﻣﻲ ﺳﺎزﻧﺪ در
ﺣﺎﻟﻴﻜﻪ روﺷﻬﺎي ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﺑﺮﻋﻜﺲ ﻋﻤﻞ ﻣﻲ ﻛﻨﻨﺪ ﻳﻌﻨﻲ درﺧﺖ ﺗﺠﺰﻳﻪ را از ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ
ﺗﻮﻟﻴﺪ ﻣﻲ ﻛﻨﻨﺪ.در ﻫﺮ دو روش ورودي از ﭼﭗ ﺑﻪ راﺳﺖ و در ﻫﺮ ﻗﺪم ﻓﻘﻂ ﻳﻚ ﺗﻮﻛﻦ
ﺑﺮرﺳﻲ ﻣﻲ ﺷﻮد.
ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ.
1,2 E → E + T | T
3,4 T → T * F | F
5,6 F → ( E ) | id
درﺧﺖ ﺗﺠﺰﻳﻪ ﺟﻤﻠﻪ id + idﺑﺼﻮرت زﻳﺮ ﺧﻮاﻫﺪ ﺑﻮد ﻛﻪ در آن رﻳﺸﻪ درﺧﺖ ﻋﻼﻣﺖ
ﺷﺮوع ﮔﺮاﻣﺮ اﺳﺖ ).ﺗﻮﺿﻴﺢ اﻳﻨﻜﻪ از اﻳﻦ ﭘﺲ اﮔﺮ ﻋﻼﻣﺖ ﺷﺮوع ﮔﺮاﻣﺮي ﺑﺼﻮرت ﺻﺮﻳﺢ
ﻣﺸﺨﺺ ﻧﺸﻮد ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺳﻤﺖ ﭼﭗ اوﻟﻴﻦ ﻗﺎﻋﺪه ﮔﺮاﻣﺮ ﺑﻌﻨﻮان ﻋﻼﻣﺖ ﺷﺮوع آن در ﻧﻈﺮ
ﮔﺮﻓﺘﻪ ﻣﻲ ﺷﻮد(.
E
T F
F id
id
٢٤
در روش ) LL(1ﻣﻨﻈﻮر از ' ' Lاول اﻳﻦ اﺳﺖ ﻛﻪ ورودي از ﺳﻤﺖ ﭼﭗ ﺑﻪ راﺳﺖ
ﺧﻮاﻧﺪه ﺷﺪه و ﺑﺮرﺳﻲ ﻣﻲ ﮔﺮدد و ' ' Lدوم ﻳﻌﻨﻲ ﭘﺎرﺳﺮ از ﺑﺴﻂ ﭼﭗ )Left-Most-
( Derivationاﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ و ' ' 1ﻧﻴﺰ ﺑﻴﺎﻧﮕﺮ اﻳﻦ اﺳﺖ ﻛﻪ در ﻫﺮ ﻗﺪم از ﺗﺠﺰﻳﻪ ﻓﻘﻂ
ﻳﻚ ﺗﻮﻛﻦ ﺑﺮرﺳﻲ ﺧﻮاﻫﺪ ﺷﺪ.
ﺗﻤﺎﻣﻲ روﺷﻬﺎي ﻓﻮق ﺑﻪ ﻧﻮﻋﻲ از ﻳﻚ روش ﻛﻠﻲ ﺑﻪ ﻧﺎم اﻧﺘﻘﺎل – ﻛﺎﻫﺶ ) Shift-
( Reduceﭘﻴﺮوي ﻣﻲ ﻛﻨﻨﺪ.اﮔﺮﭼﻪ روﺷﻬﺎي ﺗﺠﺰﻳﻪ ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ﺑﺮاي ﭘﻴﺎده ﺳﺎزي دﺳﺘﻲ
ﻣﻨﺎﺳﺐ ﺗﺮﻧﺪ اﻣﺎ اﺑﺰارﻫﺎﻳﻲ وﺟﻮد دارد ﻛﻪ ﺑﺎ ﻛﻤﻚ آﻧﻬﺎ ﻣﻲ ﺗﻮان ﺑﻄﻮر ﺧﻮدﻛﺎر ﭘﺎرﺳﺮﻫﺎي
ﻗﻮي ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﺗﻮﻟﻴﺪ ﻧﻤﻮد.در ) LR(1ﻣﻨﻈﻮر از ' ' Lﻳﻌﻨﻲ ﭘﺎرﺳﺮ ورودي را از ﭼﭗ ﺑﻪ
راﺳﺖ ﻣﻲ ﺧﻮاﻧﺪ و ﻣﻨﻈﻮر از ' ' Rاﻳﻦ اﺳﺖ ﻛﻪ ﭘﺎرﺳﺮ از ﻋﻜﺲ ﺑﺴﻂ راﺳﺖ ) Right-
( Most-Derivationاﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ.
ﺑﺮاي ﺗﺠﺰﻳﻪ رﺷﺘﻪ ورودي cadﭘﺎرﺳﺮ ﺑﺼﻮرت زﻳﺮ ﻋﻤﻞ ﻣﻲ ﻛﻨﺪ :
٢٥
S
a b
ﭼﻮن dﺑﺎ bﺑﺮاﺑﺮ ﻧﻴﺴﺖ ﭘﺎرﺳﺮ ﻧﻴﺎز دارد ﻛﻪ ﻳﻚ ﻣﺮﺣﻠﻪ ﺑﻪ ﻋﻘﺐ ﺑﺎزﮔﺮدد و ﻗﺎﻋﺪه دﻳﮕﺮ را
ﻣﻮرد ﺑﺮرﺳﻲ ﻗﺮار دﻫﺪ.
S
a
در اﻳﻨﺠﺎ ﭘﺎرﺳﺮ ﻧﺘﻴﺠﻪ ﻣﻲ ﮔﻴﺮد ﻛﻪ رﺷﺘﻪ ورودي رﺷﺘﻪ ﻗﺎﺑﻞ ﻗﺒﻮل در ﮔﺮاﻣﺮ ﻣﻲ ﺑﺎﺷﺪ.
اﮔﺮﭼﻪ ﻫﻤﺎﻧﮕﻮﻧﻪ ﻛﻪ ﺗﻮﺿﻴﺢ داده ﺷﺪ ﻋﻤﻞ ﻧﺤﻮي در ﺣﺎﻟﺖ ﻛﻠﻲ ﻣﻲ ﺗﻮاﻧﺪ ﺑﻪ روش
آزﻣﺎﻳﺶ و ﺧﻄﺎ اﺟﺮا ﮔﺮدد ﻟﻴﻜﻦ ﺑﻬﺘﺮ اﺳﺖ ﭘﺎرﺳﺮﻫﺎ ﺑﮕﻮﻧﻪ اي ﻃﺮاﺣﻲ و ﭘﻴﺎده ﺳﺎزي ﺷﻮﻧﺪ
ﻛﻪ ﻧﻴﺎزي ﺑﻪ ﭘﻴﺠﻮﺋﻲ ﻧﺪاﺷﺘﻪ ﺑﺎﺷﻨﺪ.ﺑﻪ ﭘﺎرﺳﺮﻫﺎﻳﻲ ﻛﻪ ﻋﻤﻞ ﻋﻘﺒﮕﺮد اﻧﺠﺎم ﻧﻤﻲ دﻫﻨﺪ ﭘﺎرﺳﺮ
ﭘﻴــﺸﮕﻮ ) ( Predictiveﻣﻲ ﮔﻮﻳــﻨﺪ.از ﻃـﺮﻓـﻲ ﭘﺎرﺳـﺮﻫﺎ ﻣﻌــﻤﻮﻻ ﺑﻪ ﺻﻮرت ﺣﺮﻳﺼﺎﻧﻪ ،
) ( greedyﻋﻤﻞ ﻣﻲ ﻛﻨﻨﺪ ﻳﻌﻨﻲ ﺑﺎ درﻳﺎﻓﺖ ﻫﺮ ﺗﻮﻛﻦ درﺧﺖ ﺗﺠﺰﻳﻪ را ﺗﺎ ﺣﺪ اﻣﻜﺎن
ﮔﺴﺘﺮش ﻣﻲ دﻫﻨﺪ و ﺗﻨﻬﺎ ﻫﻨﮕﺎﻣﻲ ﻛﻪ دﻳﮕﺮ اﻣﻜﺎن ﮔﺴﺘﺮش درﺧﺖ ﭘﺎرس وﺟﻮد ﻧﺪاﺷﺘﻪ
ﺑﺎﺷﺪ ﺗﻮﻛﻦ ﺑﻌﺪي را درﺧﻮاﺳﺖ ﻣﻲ ﻛﻨﻨﺪ.
٢٦
ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل ﮔﺮاﻣﺮ زﻳﺮ را ﻛﻪ ﻣﻌﺮف ﮔﻮﻧﻪ ) ( Typeدر زﺑﺎن ﭘﺎﺳﻜﺎل اﺳﺖ در ﻧﻈﺮ
ﺑﮕﻴﺮﻳﺪ.
Type → Simple
| ↑ id
| array [ Simple ] of Type
Simple → integer
| char
| num dot dot num
در روﺷﻬﺎي ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ﺗﻮﻟﻴﺪ درﺧﺖ ﺗﺠﺰﻳﻪ از رﻳﺸﻪ درﺧﺖ ﻛﻪ ﻫﻤﺎن ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺷﺮوع
ﮔﺮاﻣﺮ اﺳﺖ آﻏﺎز و در اداﻣﻪ ﻛﺎر ﻣﺮاﺣﻞ زﻳﺮ ﺑﻄﻮر ﻣﻜﺮر اﻧﺠﺎم ﻣﻲ ﮔﻴﺮد و درﺧﺖ ﺗﺠﺰﻳﻪ
ﺑﺼﻮرت ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ و از ﭼﭗ ﺑﻪ راﺳﺖ ﺳﺎﺧﺘﻪ ﻣﻲ ﺷﻮد :
.1در ﮔﺮه nﺑﺎ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ Aﻳﻜﻲ از ﻗﻮاﻋﺪ ﮔﺮاﻣﺮ ﻛﻪ Aدر ﺳﻤﺖ ﭼﭗ آن
ﻗﺮار دارد را اﻧﺘﺨﺎب ﻛﺮده و ﺳﻤﺖ راﺳﺖ اﻳﻦ ﻗﺎﻋﺪه را ﺑﻌﻨﻮان ﻓﺮزﻧﺪان ﮔﺮه
nدر درﺧﺖ ﭘﺎرس ﻗﺮار ﻣﻲ دﻫﺪ.
.2ﮔﺮه ﺑﻌﺪي را ﻛﻪ از آﻧﺠﺎ ﻳﻚ زﻳﺮدرﺧﺖ دﻳﮕﺮ ﺑﺎﻳﺪ اﻳﺠﺎد ﺷﻮد را ﭘﻴﺪا ﻣﻲ
ﻛﻨﺪ.
ﻣﺮاﺣﻞ ﻓﻮق در ﺣﻴﻦ آﻧﻜﻪ رﺷﺘﻪ ورودي از ﭼﭗ ﺑﻪ راﺳﺖ ﺧﻮاﻧﺪه ﻣﻲ ﺷﻮد اﻧﺠﺎم ﻣﻲ
ﮔﻴﺮد.ﺗﻮﻛﻨﻲ ﻛﻪ ﭘﺎرﺳﺮ در ﺣﺎل ﺑﺮرﺳﻲ آن اﺳﺖ را ﺗﻮﻛﻦ ﺟﺎري ﻣﻲ ﮔﻮﻳﻨﺪ.ﻓﺮض ﻛﻨﻴﺪ
رﺷﺘﻪ ورودي ﺑﺼﻮرت " " array [ num dot dot num ] of integer
ﺑﺎﺷﺪ.ﻣﺮاﺣﻞ ﺗﺸﻜﻴﻞ درﺧﺖ ﭘﺎرس ﺑﺼﻮرت زﻳﺮ ﺧﻮاﻫﺪ ﺑﻮد :
٢٧
array [ num dot dot num ] of integer type
٢٨
if Lookahead = t Then
Lookahead := nexttoken
else Error
end
اﺳﺘﻔﺎده ﻣﻲ ﺷﻮد و ﻣﺘﻐﻴﺮSimple وType روﻳﻪ ﻓﻮق ﺑﺮاي راﺣﺘﻲ ﻛﺎر روﻳﻪ ﻫﺎي
. را ﺗﻐﻴﻴﺮ ﻣﻲ دﻫﺪLookahead
Procedure Type ;
begin
if Lookahead is in {integer,char,num} Then Simple
else if Lookahead = array then begin
match ( array ) ;
match ( ' [ ' ) ;
Simple ;
match ( ' ] ' ) ;
match (of) ;
Type
end
else if Lookahead = ' ↑ ' Then Begin
match ( ' ↑ ' ) ;
match( ' id ' )
end
else Error
end ;
Procedure Simple ;
begin
if Lookahead = integer Then match ( integer )
else if Lookahead = char Then match ( char )
else if Lookahead = num Then begin
match ( num ) ; match ( dot dot ) ; match ( num )
end
else Error
end ;
" ﺑﺮ روي رﺷﺘﻪ ﺳﻤﺖFirst " ﭘﺎرﺳﺮ ﭘﺎﺋﻴﻨﮕﺮد ﺑﺎ اﺳﺘﻔﺎده از ﺣﺎﺻﻞ اﻋﻤﺎل ﺗﺎﺑﻌﻲ ﺑﻨﺎم
ﺗﺎﺑﻊ. راﺳﺖ ﻗﻮاﻋﺪ ﺗﻌﻴﻴﻦ ﻣﻲ ﻛﻨﺪ ﻛﻪ از ﻛﺪاﻣﻴﻚ از ﻗﻮاﻋﺪ ﮔﺮاﻣﺮ ﺑﺎﻳﺪ اﺳﺘﻔﺎده ﺷﻮد
٢٩
Firstروي رﺷﺘﻪ اي از ﭘﺎﻳﺎﻧﻪ ﻫﺎ و ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎ ﻋﻤﻞ ﻣﻲ ﻛﻨﺪ.ﺣﺎﺻﻞ ﺗﺎﺑﻊ )First(α
ﻣﺠﻤﻮﻋﻪ اي از ﭘﺎﻳﺎﻧﻪ ﻫﺎ اﺳﺖ ﻛﻪ در ﺳﻤﺖ ﭼﭗ ﺗﺮﻳﻦ ﻗﺴﻤﺖ از رﺷﺘﻪ ﻫﺎي ﺗﻮﻟﻴﺪﺷﺪه از
رﺷﺘﻪ αﻗﺮار ﻣﻲ ﮔﻴﺮﻧﺪ.ﺗﻌﺮﻳﻒ رﺳﻤﻲ ﺗﺮ اﻳﻦ ﺗﺎﺑﻊ ﺑﺼﻮرت زﻳﺮ اﺳﺖ :
*
} *)First(α) = {a | α ═› aβ , a Є T , β Є (N U T
اﻟﮕﻮرﻳﺘﻢ ﺑﺪﺳﺖ آوردن ﺗﺎﺑﻊ Firstﻳﻚ رﺷﺘﻪ در ﺑﺨﺶ ﻫﺎي ﺑﻌﺪي آورده ﺷﺪه
اﺳﺖ.ﺑﻌﻨﻮان ﻣﺜﺎل ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ.
S → cAd
A → ab | a
}First(cAd) = {c
}First(S) = {c
}First(A) = {a
٣٠
3-3اﺳﺘﻔﺎده از ﻗﺎﻋﺪه اﭘﺴﻴﻠﻮن ) ( A → ε
ﻫﺮﮔﺎه در ﮔﺮاﻣﺮي ﻗﺎﻋﺪه اﭘﺴﻴﻠﻮن وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﺪ ﭘﺎرﺳﺮ ﭘﺎﺋﻴﻨﮕﺮد از آن ﺑﻌﻨﻮان ﻗﺎﻋﺪه
ﭘﻴﺶ ﻓﺮض ) ( defaultاﺳﺘﻔﺎده ﻣﻲ ﻛﺘﺪ.ﺑﺪﻳﻦ ﻣﻌﻨﻲ ﻛﻪ اﮔﺮ ﻫﻴﭻ ﻗﺎﻋﺪه دﻳﮕﺮي در ﮔﺮاﻣﺮ
ﻣﻨﺎﺳﺐ ﺗﺸﺨﻴﺺ داده ﻧﺸﺪ ﭘﺎرﺳﺮ از ﻗﺎﻋﺪه اﭘﺴﻴﻠﻮن ﺑﺮاي اداﻣﻪ ﻋﻤﻞ ﺗﺠﺰﻳﻪ اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ.
ﻣﺜﺎل :
ST → begin STS end
STS → STL ε
STL → a
ﻓﺮض ﻛﻨﻴﺪ ﺟﻤﻠﻪ ورودي begin endﺑﺎﺷﺪ.درﺧﺖ ﺗﺠﺰﻳﻪ ﭘﺲ از ﺧﻮاﻧﺪن ﺗﻮﻛﻦ
beginو ﺑﺴﻂ ﻋﻼﻣﺖ ﺷﺮوع ﮔﺮاﻣﺮ ﺑﺼﻮرت زﻳﺮ ﺧﻮاﻫﺪ ﺑﻮد :
ST
begin end
STS
ﭘﺲ از ﺗﻄﺒﻴﻖ ﺗﻮﻛﻦ , beginﺗﻮﻛﻦ ﺑﻌﺪي ﻳﻌﻨﻲ endاز اﺳﻜﻨﺮ درﻳﺎﻓﺖ ﻣﻲ ﮔﺮددو ﻧﻮﺑﺖ
ﺑﻪ ﺑﺴﻂ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ STSﻣﻲ رﺳﺪ.ﭼﻮن endﻣﺘﻌﻠﻖ ﺑﻪ ﻣﺠﻤﻮﻋﻪ ) First(STSﻧﻴﺴﺖ ﻟﺬا
از ﻗﺎﻋﺪه STS → εﺑﻌﻨﻮان ﻗﺎﻋﺪه ﭘﻴﺶ ﻓﺮض اﺳﺘﻔﺎده ﻣﻲ ﺷﻮد.درﺧﺖ ﺗﺠﺰﻳﻪ ﺑﺼﻮرت
زﻳﺮ درﺧﻮاﻫﺪ آﻣﺪ :
ST
ε
ﺳﭙﺲ ﺗﻮﻛﻦ endﻧﻴﺰ ﺑﺎ ﭘﺎﻳﺎﻧﻪ endدر درﺧﺖ ﺗﺠﺰﻳﻪ ﺗﻄﺒﻴﻖ ﻣﻲ ﺷﻮد و ﻋﻤﻞ ﺗﺠﺰﻳﻪ ﺧﺎﺗﻤﻪ
ﻣﻲ ﻳﺎﺑﺪ.
٣١
ST
ε
ﺑﺎﻳﺪ ﺗﻮﺟﻪ داﺷﺖ ﻛﻪ اﻧﺘﺨﺎب ﻗﺎﻋﺪه εﺑﺮاي ﺑﺴﻂ STSﺗﻨﻬﺎ در ﺻﻮرﺗﻲ ﻛﻪ ﺗﻮﻛﻦ ﺟﺎري در
آن ﻟﺤﻈﻪ endﺑﺎﺷﺪ اﻧﺘﺨﺎب درﺳﺘﻲ ﺧﻮاﻫﺪ ﺑﻮد.
٣٢
در ﻗﻮاﻋﺪ ﻓﻮق ﻓﺮض ﺑﺮ اﻳﻦ اﺳﺖ ﻛﻪ βiﻫﺎ ﻧﺒﺎﻳﺪ ﺑﺎ Aﺷﺮوع ﺷﻮﻧﺪ و ﻫﻴﭽﻜﺪام از αiﻫﺎ
ﻧﺒﺎﻳﺪ εﺑﺎﺷﻨﺪ.در اﻳﻨﺼﻮرت ﻣﻲ ﺗﻮان ﻗﻮاﻋﺪ زﻳﺮ را ﺑﺠﺎي ﻗﻮاﻋﺪ ﭼﭗ ﮔﺮدي ﻓﻮق ﺑﻜﺎر ﺑﺮد.
٣٣
ﺣﺎل ﺑﻌﻨﻮان ﻧﻤﻮﻧﻪ اﻟﮕﻮرﻳﺘﻢ ﻓﻮق را ﺑﺮاي ﮔﺮاﻣﺮ زﻳﺮ ﺑﻜﺎر ﻣﻲ ﺑﺮﻳﻢ :
S → Aa | b
A → Ac | Sd
اﺑﺘﺪا ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎي ﮔﺮاﻣﺮ را ﺑﻪ ﺗﺮﺗﻴﺐ Aو ) Sاز ﭼﭗ ﺑﻪ راﺳﺖ ( ﻣﺮﺗﺐ ﻣﻲ ﻛﻨﻴﻢ.از
روي ﻗﺎﻋﺪه A → Sdﺧﻮاﻫﻴﻢ داﺷﺖ :
A → Aad | bd
ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﻗﻮاﻋﺪ Aﺑﺼﻮرت زﻳﺮ درﺧﻮاﻫﻨﺪ آﻣﺪ ﻛﻪ داراي ﭼﭗ ﮔﺮدي آﺷﻜﺎر ﻫﺴﺘﻨﺪ :
A → Aad
A → Ac
A → bd
ﺑﺎ ﺣﺬف ﭼﭗ ﮔﺮدي آﺷﻜﺎر ﻗﻮاﻋﺪ ﻓﻮق ﮔﺮاﻣﺮ زﻳﺮ ﺑﺪﺳﺖ ﻣﻲ آﻳﺪ :
S → Aa | b
'A → bdA
A' → cA' | adA' | ε
٣٤
'A → αA
A' → β1 | β2
٣٥
اﻳﻦ زﺑﺎن رﺷﺘﻪ ﻫﺎﻳﻲ ﺑﺼﻮرت aabcaabﺗﻮﻟﻴﺪ ﻣﻲ ﻛﻨﺪ.اﻳﻦ رﺷﺘﻪ ﻫﺎ را ﻣﻲ ﺗﻮان ﻣﺸﺎﺑﻪ اﻳﻦ
ﻣﺤﺪودﻳﺖ در زﺑﺎﻧﻬﺎي ﺑﺮﻧﺎﻣﻪ ﺳﺎزي در ﻧﻈﺮ ﮔﺮﻓﺖ ﻛﻪ ﺗﻌﺮﻳﻒ ﻣﺘﻐﻴﺮﻫﺎ ﺑﺎﻳﺴﺘﻲ ﻗﺒﻞ از
اﺳﺘﻔﺎده از آﻧﻬﺎ ﻗﺮار ﮔﻴﺮد.ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﻛﻪ wاول در wcwﺑﻴﺎﻧﮕﺮ ﺗﻌﺮﻳﻒ ﻣﺘﻐﻴﺮ ﺑﻮده و
wدوم ﻧﺸﺎن دﻫﻨﺪه اﺳﺘﻔﺎده از ﻣﺘﻐﻴﺮ ﻣﻲ ﺑﺎﺷﺪ.
declaration aab
begin w
.
c .
.
end
… = aab
w
٣٦
ﻣﺜﺎل – 3زﺑﺎن } L3 = { anbncn | n >= 1ﻧﻴﺰ ﻣﺴﺘﻘﻞ از ﻣﺘﻦ ﻧﻴﺴﺖ.اﻳﻦ زﺑﺎن رﺷﺘﻪ
ﻫﺎﻳﻲ ﺑﻔﺮم a+b+c+ﺗﻮﻟﻴﺪ ﻣﻲ ﻛﻨﺪ ﻛﻪ در آﻧﻬﺎ ﺗﻌﺪاد c , b , aﺑﺮاﺑﺮ اﺳﺖ.اﻳﻦ زﺑﺎن ﻣﺸﺎﺑﻪ
ﻣﺴﺎﻟﻪ اﻳﺠﺎد ﻛﻠﻤﺎﺗﻲ ﻛﻪ در زﻳﺮ آﻧﻬﺎ ﺧﻂ ﻛﺸﻴﺪه ﺷﺪه ﺑﺎﺷﺪ )( Underlined Word
اﺳﺖ .اﻳﻨﮕﻮﻧﻪ ﻛﻠﻤﺎت ﺑﻪ اﻳﻦ ﺻﻮرت ﭼﺎپ ﻣﻲ ﺷﻮﻧﺪ ﻛﻪ اﺑﺘﺪا ﻛﺎراﻛﺘﺮﻫﺎي ﻳﻚ ﻛﻠﻤﻪ ﭼﺎپ
ﺷﺪه و ﺑﺪﻧﺒﺎل آن ﺑﻪ ﺗﻌﺪاد ﻛﺎراﻛﺘﺮﻫﺎي آن ﻛﻠﻤﻪ ﺑﻪ ﻋﻘﺐ ﺑﺮﮔﺸﺘﻪ ) ﺑﺎ ﻛﻤﻚ ﻛﺎراﻛﺘﺮ
( BackSpaceو ﺳﭙﺲ ﺑﻪ ﻫﻤﺎن ﺗﻌﺪاد ﻛﺎراﻛﺘﺮ " _ " ﭼﺎپ ﻣﻲ ﺷﻮد.ﻣﺜﻼ ﻛﻠﻤﻪ Read
.در زﺑﺎن L3اﮔﺮ aﺑﻴﺎﻧﮕﺮ ﺣﺮوف b ,ﺑﻴﺎﻧﮕﺮ ﻛﺎراﻛﺘﺮ BackSpaceو cﻧﻴﺰ ﺑﻴﺎﻧﮕﺮ
ﻛﺎراﻛﺘﺮ " _ " ﺑﺎﺷﺪ آﻧﮕﺎه اﻳﻦ زﺑﺎن ﻛﻠﻤﺎت Underlinedرا ﺗﻮﻟﻴﺪ ﻣﻲ ﻛﻨﺪ.
زﺑﺎن } L2' = { anbncn1dn1 | n >= 1 and n1 >= 1ﻧﻴﺰ ﻣﺴﺘﻘﻞ از ﻣﺘﻦ اﺳﺖ و ﺑﺎ
ﮔﺮاﻣﺮ زﻳﺮ ﺗﻮﺻﻴﻒ ﻣﻲ ﺷﻮد :
S → AB
A → aAb | ab
B → cBd | cd
و ﺳﺮاﻧﺠﺎم زﺑﺎن } L3' = { anbn | n >= 1ﻧﻴﺰ ﻣﺴﺘﻘﻞ از ﻣﺘﻦ و ﮔﺮاﻣﺮ ﺗﻮﻟﻴﺪ آن
ﺑﺼﻮرت زﻳﺮ اﺳﺖ :
S → aSb | ab
٣٧
8 -3اﺳﺘﻔﺎده از دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل ﺑﺮاي ﭘﻴﺎده ﺳﺎزي ﭘﺎرﺳﺮﻫﺎي ﭘﻴﺸﮕﻮ
ﺑﺮاي ﻃﺮاﺣﻲ ﭘﺎرﺳﺮﻫﺎي ﭘﻴﺸﮕﻮ ﻧﻴﺰ ﻣﻲ ﺗﻮاﻧﻴﻢ از دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل اﺳﺘﻔﺎده ﻛﻨﻴﻢ.ﺑﻪ اﻳﻦ
ﺗﺮﺗﻴﺐ ﻛﻪ اﺑﺘﺪا ﺳﺎﺧﺘﺎر ﻧﺤﻮي زﺑﺎن را ﺑﺎ اﺳﺘﻔﺎده از ﮔﺮاﻣﺮ اﻳﺠﺎد ﻣﻲ ﻛﻨﻴﻢ.ﺳﭙﺲ از روي
ﮔﺮاﻣﺮ دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل را رﺳﻢ ﻣﻲ ﻛﻨﻴﻢ.در ﭘﺎرﺳﺮﻫﺎ ﺑﺮاي ﻫﺮ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻳﻚ دﻳﺎﮔﺮام
اﻧﺘﻘﺎل دارﻳﻢ .ﺑﺮﭼﺴﺐ ﻫﺎي ﻟﺒﻪ ﻫﺎي اﻳﻦ دﻳﺎﮔﺮام ﻫﺎ ﻣﻲ ﺗﻮاﻧﻨﺪ ﭘﺎﻳﺎﻧﻪ و ﻳﺎ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ
ﺑﺎﺷﻨﺪ.اﻧﺘﻘﺎل از ﻃﺮﻳﻖ ﻟﺒﻪ ﻫﺎي ﺑﺎ ﺑﺮﭼﺴﺐ ﭘﺎﻳﺎﻧﻪ ﺑﻪ ﻣﻨﺰﻟﻪ اﻳﻨﺴﺖ ﻛﻪ در ورودي ﻧﻴﺰ ﻧﺸﺎﻧﻪ اي
ﻣﻌﺎدل ﺑﺎ ﺑﺮﭼﺴﺐ ﻣﺸﺎﻫﺪه ﺷﺪه اﺳﺖ.در اﻧﺘﻘﺎل از ﻃﺮﻳﻖ ﻟﺒﻪ اي ﺑﺎ ﺑﺮﭼﺴﺐ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻣﺎﻧﻨﺪ A
ﺑﻪ ﻣﻨﺰﻟﻪ ﻓﺮاﺧﻮاﻧﻲ روﻳﻪ اي ﺑﺮاي Aﻣﻲ ﺑﺎﺷﺪ.ﺑﺮاي اﻳﺠﺎد دﻳﺎﮔﺮام ﻳﻚ ﮔﺮاﻣﺮ اﺑﺘﺪا ﺑﺎﻳﺪ
ﭼﭗ ﮔﺮدي ﮔﺮاﻣﺮ را در ﺻﻮرت وﺟﻮد ﺑﺮﻃﺮف ﻛﺮد.ﻫﻤﭽﻨﻴﻦ ﻫﺮﻛﺠﺎ ﻛﻪ ﻻزم ﺑﺎﺷﺪ ﺑﺎﻳﺴﺘﻲ
ﻋﻤﻞ ﻓﺎﻛﺘﻮرﮔﻴﺮي از ﭼﭗ اﻧﺠﺎم ﺷﻮد.ﺳﭙﺲ ﺑﺮاي رﺳﻢ دﻳﺎﮔﺮام ﻣﺮاﺣﻞ زﻳﺮ را ﺑﺮاي ﻫﺮ
ﻏﻴﺮﭘﺎﻳﺎﻧﻪ اﻧﺠﺎم ﻣﻲ دﻫﻴﻢ :
-1ﻳﻚ ﺣﺎﻟﺖ ﺷﺮوع و ﻳﻚ ﺣﺎﻟﺖ ﻧﻬﺎﻳﻲ اﻳﺠﺎد ﻛﻨﻴﺪ.
-2ﺑﺮاي ﻫﺮ ﻗﺎﻋﺪه ﺑﻪ ﻓﺮم A → X1X2…Xmﻳﻚ ﻣﺴﻴﺮ از ﺣﺎﻟﺖ ﺷﺮوع ﺑﻪ ﺣﺎﻟﺖ
ﻧﻬﺎﻳﻲ رﺳﻢ ﻣﻲ ﻛﻨﻴﻢ و ﺑﻪ ﻟﺒﻪ ﻫﺎ ﺑﺮﭼﺴﺐ ﻫﺎي X1X2…Xmﻣﻲ دﻫﻴﻢ.
ﺣﺎل ﺑﻌﻨﻮان ﻧﻤﻮﻧﻪ دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل ﮔﺮاﻣﺮ زﻳﺮ را رﺳﻢ ﻣﻲ ﻛﻨﻴﻢ.
'E → TE
E' → +TE' | ε
'T → FT
T' → *FT' | ε
F → (E) | id
ε
٣٨
F 'T
T : 7 8 9
ε
id
ﮔﺎﻫﻲ اوﻗﺎت دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل را ﻣﻲ ﺗﻮان ﺳﺎده ﺗﺮ ﻛﺮد.ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل دﻳﺎﮔﺮام ﻫﺎي ﻓﻮق
را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ.
ε
+ T
E' : 3 4 5
ε
ε
6
T
ε
6
٣٩
و ﺑﺎ ﺟﺎﮔﺬاري اﻳﻦ دﻳﺎﮔﺮام ﺳﺎده ﺷﺪه در دﻳﺎﮔﺮام Eﺧﻮاﻫﻴﻢ داﺷﺖ :
'E
E : T
2
0 1
T
T
E : 0 3 4
+
ε
6
+
ﺑﻪ ﻫﻤﻴﻦ ﺗﺮﺗﻴﺐ ﺑﺮاي دﻳﺎﮔﺮام ﻫﺎي ' Tو Tﺧﻮاﻫﻴﻢ داﺷﺖ :
ε
ε
F
*
T' : 10 11 12
ε
13
F
ε
13
٤٠
F T'
T : 7 8 9
F *
T : 7 10 11
* 13
F
T : 7 10
13
٤١
9 -3ﺗﺠﺰﻳﻪ ﭘﻴﺸﮕﻮﻳﺎﻧﻪ ﻏﻴﺮﺑﺎزﮔﺸﺘﻲ ) ( Non-Recursion Predictive Parsing
در اﻳﻦ روش ﺗﺠﺰﻳﻪ از ﻳﻚ اﻧﺒﺎره ) ( Parse Stackو ﻳﻚ ﺟﺪول ﺑﻪ ﻧﺎم ﺟـﺪول ﺗـﺠــﺰﻳﻪ
) ( Parsing Tableاﺳﺘﻔﺎده ﻣﻲ ﮔﺮدد.ﺑﺎﻓﺮ ورودي ﺷﺎﻣﻞ رﺷﺘﻪ اي اﺳﺖ ﻛﻪ ﺑﺎﻳﺪ ﺗﺠﺰﻳﻪ
ﺷﻮد.در اﻧﺘﻬﺎي رﺷﺘﻪ ورودي ﻋﻼﻣﺘﻲ ﻣﺜﻼ $ﻗﺮار ﻣﻲ ﮔﻴﺮد.ﺳﺎﺧﺘﺎر ﻛﻠﻲ اﻳﻦ ﻧﻮع ﭘﺎرﺳﺮ
ﺑﻔﺮم زﻳﺮ اﺳﺖ :
X
Z
$
ﺟﺪول ﭘﺎرس
در اﺑﺘﺪاي ﭘﺎرس ﻋﻼﻣﺖ $وارد اﻧﺒﺎره ﻣﻲ ﺷﻮد و روي آن ﻋﻼﻣﺖ ﺷﺮوع ﮔﺮاﻣﺮ ﻗﺮار ﻣﻲ
ﮔﻴﺮد.در اﻧﺘﻬﺎي ﭘﺎرس ﻫﻢ در اﻧﺒﺎره ﺑﺎﻓﺮ ﻫﻢ در ورودي ﺗﻨﻬﺎ ﻋﻼﻣﺖ $ﺑﺎﻗﻲ ﻣﻲ
ﻣﺎﻧﺪ.ﺟﺪول ﭘﺎرس ﻳﻚ آراﻳﻪ دوﺑﻌﺪي ﺑﺼﻮرت ] M [ A , aاﺳﺖ ﻛﻪ در آن Aﻳﻚ
ﻏﻴﺮﭘﺎﻳﺎﻧﻪ و aﻳﻚ ﭘﺎﻳﺎﻧﻪ و ﻳﺎ ﻋﻼﻣﺖ $اﺳﺖ.ﻓﺮم ﻛﻠﻲ ﺟﺪول ﺑﺼﻮرت زﻳﺮ اﺳﺖ :
ﭘﺎﻳﺎﻧﻪ هﺎ و ﻋﻼﻣﺖ $
......
ﻏﻴﺮﭘﺎﻳﺎﻧﻪ
ﺑﺮﺧﻲ از ﺧﺎﻧﻪ ﻫﺎي ﺟﺪول ﺣﺎوي ﺷﻤﺎره ﻳﻚ ﻗﺎﻋﺪه از ﮔﺮاﻣﺮ و ﺑﺮﺧﻲ از آﻧﻬﺎ ﺧﺎﻟﻲ اﺳﺖ.
٤٢
در ﻫﺮ ﻗﺪم از ﭘﺎرس ﺑﺮﻧﺎﻣﻪ ﭘﺎرس ﻋﻼﻣﺖ Xﺑﺎﻻي اﻧﺒﺎره و Tokenﺟﺎري aدر ورودي
را ﻣﻮرد ﺑﺮرﺳﻲ ﻗﺮار ﻣﻲ دﻫﺪ و ﺑﺼﻮرت زﻳﺮ ﺗﺼﻤﻴﻢ ﻣﻲ ﮔﻴﺮد :
-1اﮔﺮ X = a = $ﺑﺎﺷﺪ ﭘﺎرﺳﺮ ﭘﺎﻳﺎن ﻣﻮﻓﻘﻴﺖ آﻣﻴﺰ ﭘﺎرس را ﮔﺰارش ﻣﻲ دﻫﺪ.
-2اﮔﺮ X = a ≠ $ﺑﺎﺷﺪ ﭘﺎرﺳﺮ Xرا از ﺑﺎﻻي اﻧﺒﺎره ﺣﺬف و ﺗﻮﻛﻦ ﺑﻌﺪي را
درﻳﺎﻓﺖ ﻣﻲ ﻛﻨﺪ.اﮔﺮ Xﭘﺎﻳﺎﻧﻪ ﺑﺎﺷﺪ و ﺑﺎ aﻣﻄﺎﺑﻘﺖ ﻧﻜﻨﺪ ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي رخ
داده اﺳﺖ.
-3اﮔﺮ Xﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﺎﺷﺪ ﺑﺮﻧﺎﻣﻪ ﺑﻪ ﺧﺎﻧﻪ ] M [ X , aﻣﺮاﺟﻌﻪ ﻣﻲ ﻛﻨﺪ ﻛﻪ در
آن ﻳﺎ ﺷﻤﺎره ﻗﺎﻋﺪه اي ﺑﻪ ﻓﺮم A → ABCﻗﺮار دارد و ﻳﺎ ﺧﺎﻟﻲ اﺳﺖ.در ﺻﻮرت
اول ,ﭘﺎرﺳﺮ Xرا از ﺑﺎﻻي اﻧﺒﺎره ﺣﺬف و ﺑﺠﺎي آن ABCرا وارد اﻧﺒﺎره ﻣﻲ ﻛﻨﺪ
ﺑﻪ ﻧﺤﻮي ﻛﻪ Aﺑﺎﻻي اﻧﺒﺎره ﻗﺮار ﮔﻴﺮد.در ﺻﻮرﺗﻴﻜﻪ ﺧﺎﻧﻪ ] M [ X , aﺧﺎﻟﻲ
ﺑﺎﺷﺪ ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي رخ داده اﺳﺖ.
٤٣
-3اﮔﺮ ﻗﺎﻋﺪه اي ﺑﻪ ﻓﺮم X → y1y2 … ykدر ﮔﺮاﻣﺮ ﻣﻮﺟﻮد ﺑﺎﺷﺪ اﺑﺘﺪا
) First(y1) – εﻳﻌﻨﻲ ﻣﺠﻤﻮﻋﻪ ) First(y1ﻣﻨﻬﺎي ( εرا ﺑﻪ ) First(Xاﺿﺎﻓﻪ
ﻣﻲ ﻛﻨﻴﻢ.درﺻﻮرﺗﻴﻜﻪ First(y2) – ε , y1 ═›* εرا ﻧﻴﺰ ﺑﻪ ) First(Xاﺿﺎﻓﻪ
ﻣﻲ ﻛﻨﻴﻢ ).در ﻏﻴﺮ اﻳﻨﺼﻮرت ﻛﺎر ﻳﺎﻓﺘﻦ ) First(Xاز ﻃﺮﻳﻖ ﻗﺎﻋﺪه ﻓﻮق ﺧﺎﺗﻤﻪ ﻣﻲ
ﻳﺎﺑﺪ(.در ﺻﻮرﺗﻴﻜﻪ First(y3) – ε , y2 ═›* εرا ﻫﻢ ﺑﻪ ) First(Xﻣﻲ
اﻓﺰاﺋﻴﻢ.اﻳﻦ ﻛﺎر آﻧﻘﺪر اداﻣﻪ ﻣﻲ ﻳﺎﺑﺪ ﺗﺎ آﻧﻜﻪ εﻋﻀﻮ ﻣﺠﻤﻮﻋﻪ ) , First(yiﺑﻪ ازاي
1 <= i <= kﻧﺒﺎﺷﺪ.در ﺻﻮرﺗﻴﻜﻪ yk-1 ═›* εﺑﺎﻳﺪ ) First(ykرا ﻧﻴﺰ ﺑﻪ
) First(Xاﺿﺎﻓﻪ ﺑﻜﻨﻴﻢ.
ﺑﺮاي ﺑﺪﺳﺖ آوردن ) A ) Follow(Aﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ اﺳﺖ ( اﻋﻤﺎل زﻳﺮ را آﻧﻘﺪر اداﻣﻪ
ﻣﻲ دﻫﻴﻢ ﺗﺎ اﻳﻨﻜﻪ دﻳﮕﺮ ﭼﻴﺰي ﺑﻪ ﻣﺠﻤﻮﻋﻪ ) Follow(Aاﺿﺎﻓﻪ ﻧﺸﻮد :
$ .1را در ) Follow(Sﻗﺮار ﻣﻲ دﻫﻴﻢ S ).ﻋﻼﻣﺖ ﺷﺮوع ﮔﺮاﻣﺮ اﺳﺖ (
.2اﮔﺮ ﻗﺎﻋﺪه اي ﺑﺼﻮرت X → αAβداﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻫﺮﭼﻪ در )First(β
ﻗﺮار دارد ) ﺑﻐﻴﺮ از ( εرا ﺑﻪ ﻣﺠﻤﻮﻋﻪ ) Follow(Aاﺿﺎﻓﻪ ﻣﻲ ﻛﻨﻴﻢ.
.3اﮔﺮ ﻗﺎﻋﺪه اي ﺑﻔﺮم X → αAداﺷﺘﻪ ﺑﺎﺷﻴﻢ و ﻳﺎ آﻧﻜﻪ ﻗﺎﻋﺪه اي ﺑﻔﺮم X
X → αAβو , β ═›* εﻫﺮﭼﻪ در ) Follow(Xﻗﺮار دارد را ﺑﻪ
ﻣﺠﻤﻮﻋﻪ ) Follow(Aاﺿﺎﻓﻪ ﻣﻲ ﻛﻨﻴﻢ.
٤٤
2–3 E' → + T E' | ε
4 'T → F T
5–6 T' → * F T' | ε
7–8 F → ( E ) | id
٤٥
: را ﺗﺠﺰﻳﻪ ﻣﻲ ﻛﻨﻴﻢid + id * id ﺣﺎل ﺑﺎ اﺳﺘﻔﺎده از ﺟﺪول ﻓﻮق ﻋﺒﺎرت
٤٦
ﮔﺮاﻣﺮﻫﺎي )LL(1
در ﺻﻮرﺗﻴﻜﻪ از روش ﻓﻮق ﺑﺮاي اﻳﺠﺎد ﺟﺪول ﭘﺎرس ﮔﺮاﻣﺮﻫﺎي ﮔﻨﮓ و ﻳﺎ ﭼﭗ ﮔﺮد
اﺳﺘﻔﺎده ﺷﻮد در ﺑﺮﺧﻲ از ﺧﺎﻧﻪ ﻫﺎي ﺟﺪول ﭘﺎرس ﺑﻴﺶ از ﻳﻚ ﺷﻤﺎره ﻗﺎﻋﺪه ﺧﻮاﻫﻴﻢ
داﺷﺖ.ﺑﻌﺒﺎرت دﻳﮕﺮ اﮔﺮ در ﺧﺎﻧﻪ ﻫﺎي ﺟﺪول ﭘﺎرس ﻳﻚ ﮔﺮاﻣﺮ ﻣﺴﺘﻘﻞ از ﻣﺘﻦ ﺣﺪاﻛﺜﺮ
ﻳﻚ ﺷﻤﺎره ﻗﺎﻋﺪه ﺑﺎﺷﺪ ﮔﺮاﻣﺮ ﻣﺮﺑﻮﻃﻪ را ) LL(1ﮔﻮﻳﻨﺪ.
ﻣﺜﺎل – ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ :
1–2 S → i E t S S' | a
3–4 S' → e S | ε
5 E→b
اﻳﻦ ﮔﺮاﻣﺮ ) LL(1ﻧﻴﺴﺖ زﻳﺮا در ﺧﺎﻧﻪ ] M[S',eﺟﺪول ﺗﺠﺰﻳﻪ آن دو ﺷﻤﺎره ﻗﺎﻋﺪه ﻗﺮار
دارد.
ﺑﺮاي ﭘﻲ ﺑﺮدن ﺑﻪ ) LL(1ﺑﻮدن ﻳﻚ ﮔﺮاﻣﺮ ﻻزم ﻧﻴﺴﺖ ﻛﻪ ﺣﺘﻤﺎ ﺟﺪول ﺗﺠﺰﻳﻪ آن ﺑﺪﺳﺖ
آﻳﺪ.ﺑﺎ ﺑﺮرﺳﻲ ﺷﺮاﻳﻂ زﻳﺮ ﻧﻴﺰ ﻣﻲ ﺗﻮان ) LL(1ﺑﻮدن ﻳﻚ ﮔﺮاﻣﺮ را ﺑﺮرﺳﻲ ﻧﻤﻮد.ﺑﻌﺒﺎرت
دﻳﮕﺮ ﮔﺮاﻣﺮي ) LL(1اﺳﺖ ﻛﻪ ﺷﺮاﻳﻂ زﻳﺮ در ﻣﻮرد ﻗﻮاﻋﺪ ﺑﺼﻮرت A → α | βآن
ﺻﺪق ﻛﻨﺪ :
٤٧
ﺑﻌﻨﻮان ﻣﺜﺎل ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ :
1 'E → T E
2–3 E' → + T E' | ε
4 'T → F T
5–6 T' → * F T' | ε
7–8 F → ( E ) | id
ﺷﺮاﻳﻂ ) LL(1ﺑﻮدن را ﺑﺮاي ﮔﺮاﻣﺮ ﻓﻮق ﭼﻚ ﻣﻲ ﻛﻨﻴﻢ.از آﻧﺠﺎ ﻛﻪ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎي Eو
Tﺗﻨﻬﺎ ﻳﻚ ﻗﺎﻋﺪه دارﻧﺪ ﻧﻴﺎزي ﺑﻪ ﺑﺮرﺳﻲ ﻧﺪارﻧﺪ.در ﻣﻮرد ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ': E
ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ آﻧﻜﻪ εﻋﻀﻮ )' First(Sاﺳﺖ اﮔﺮ ﺷﺮط ﺳﻮم را در ﻣﻮرد ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ' Sﭼﻚ
ﻛﻨﻴﻢ ﻣﺸﺨﺺ ﺧﻮاﻫﺪ ﺷﺪ ﻛﻪ اﻳﻦ ﮔﺮاﻣﺮ ) LL(1ﻧﻴﺴﺖ.
First(eS) = { e } , Follow(S') = { e,$ } , { e,$ } ∩ { e } ≠ ф
٤٨
ﻓﺎﻛﺘﻮرﮔﻴﺮي و ﺣﺬف ﭼﭗ ﮔﺮدي ﺑﺎﻋﺚ از ﺑﻴﻦ رﻓﺘﻦ ﺧﻮاﻧﺎﻳﻲ ﮔﺮاﻣﺮﻫﺎ ﻣﻲ ﺷﻮﻧﺪ.در ﺿﻤﻦ
ﺗﻮﻟﻴﺪ ﻛﺪ را ﻧﻴﺰ ﻣﺸﻜﻞ ﺗﺮ ﻣﻲ ﻧﻤﺎﻳﻨﺪ.
از ﻣﻬﻤﺘﺮﻳﻦ روﺷﻬﺎي اﺻﻼح ﺧﻄﺎي ﻗﺎﺑﻞ اﺳﺘﻔﺎده در ﺗﺠﺰﻳﻪ ) LL(1ﻋﺒﺎرﺗﻨﺪ از :
• روش Panic Mode
• روش Phrase Level
ﺑﻄﻮر ﻛﻠﻲ در روش ) LL(1زﻣﺎﻧﻲ ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي ﺗﺸﺨﻴﺺ داده ﻣﻲ ﺷﻮد ﻛﻪ ﻳﺎ ﭘﺎﻳﺎﻧﻪ
ﺑﺎﻻي اﻧﺒﺎره ﺑﺎ ﺗﻮﻛﻦ ﺟﺎري ﺗﻄﺒﻴﻖ ﻧﻜﻨﺪ و ﻳﺎ ﺑﺎ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﺎﻻي اﻧﺒﺎره Aو ﺗﻮﻛﻦ ﺟﺎري a
ﺧﺎﻧﻪ ] M [ A , aﺧﺎﻟﻲ ﺑﺎﺷﺪ.
در روش Panic Modeاﮔﺮ ﭘﺎرﺳﺮ ﺑﺎ ﻣﺮاﺟﻌﻪ ﺑﻪ ﻳﻚ ﺧﺎﻧﻪ ﺧﺎﻟﻲ ﺟﺪول ﺗﺠﺰﻳﻪ ﻳﻚ
ﺧﻄﺎي ﻧﺤﻮي ﺑﻴﺎﺑﺪ آﻧﻘﺪر از رﺷﺘﻪ ورودي ﺣﺬف ﻣﻲ ﻛﻨﺪ ﺗﺎ ﺑﻪ ﻳﻜﻲ از اﻋﻀﺎ ﻣﺠﻤﻮﻋﻪ اي
ﻣﻮﺳﻮم ﺑﻪ ﻣﺠﻤﻮﻋﻪ Synchronizingﺑﺮﺳﺪ.در روش Panic Modeﺑﻪ ازاي ﻫﺮ
ﻏﻴﺮﭘﺎﻳﺎﻧﻪ در ﮔﺮاﻣﺮ ﻳﻚ ﻣﺠﻤﻮﻋﻪ Synchronizingدر ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﻣﻲ ﺷﻮد.ﻛﺎراﻳﻲ
روش Panic Modeﻧﻴﺰ ﺑﺴﺘﮕﻲ ﺑﻪ اﻧﺘﺨﺎب ﻣﻨﺎﺳﺐ ﻣﺠﻤﻮﻋﻪ Synchronizing
دارد.اﻳﻦ ﻣﺠﻤﻮﻋﻪ ﺑﺎﻳﺪ ﻳﻪ ﮔﻮﻧﻪ اي ﺗﻌﻴﻴﻦ ﺷﻮد ﻛﻪ ﻋﻤﻞ ﺗﺠﺰﻳﻪ ﺑﺘﻮاﻧﺪ ﺑﺪون ﺣﺬف ﻗﺴﻤﺖ
زﻳﺎدي از ورودي ﺑﻪ ﻛﺎر ﺧﻮد اداﻣﻪ دﻫﺪ.ﻳﻚ اﻧﺘﺨﺎب ﻣﻨﺎﺳﺐ ,در ﻧﻈﺮﮔﺮﻓﺘﻦ ﻣﺠﻤﻮﻋﻪ
Followﻫﺮ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﻪ ﻋﻨﻮان ﻣﺠﻤﻮﻋﻪ Synchronizingآن ﻏﻴﺮﭘﺎﻳﺎﻧﻪ اﺳﺖ.ﺑﺎ اﻳﻦ
وﺟﻮد در ﻧﻈﺮﮔﺮﻓﺘﻦ ﻣﺠﻤﻮﻋﻪ Followﺗﻨﻬﺎ ﺑﺮاي Synchronizingﻛﺎﻓﻲ ﻧﻴﺴﺖ.ﺑﺮاي
اﻳﻨﻜﻪ ﺣﺬف ﻛﻤﺘﺮي در ﺑﺮﻧﺎﻣﻪ ورودي ﺻﻮرت ﺑﮕﻴﺮد ﻣﻲ ﺗﻮان ﻧﻤﺎدﻫﺎي ﺑﻴﺸﺘﺮي را ﺑﻪ اﻳﻦ
ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎ را ﻧﻴﺰ ﺑﻪ ﻣﺠﻤﻮﻋﻪ ﻣﺠﻤﻮﻋﻪ اﻓﺰود.ﻣﺜﻼ ﻣﻲ ﺗﻮان ﻣﺠﻤﻮﻋﻪ First
Synchronizingآﻧﻬﺎ اﻓﺰود.ﺑﻪ ﻋﻨﻮان ﻳﻚ ﻣﺜﺎل از ﻧﺤﻮه ﻋﻤﻞ ﭘﻴﺎده ﺳﺎزي روش
Panic Modeدر ﺗﺠﺰﻳﻪ ) LL(1ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ :
1 'E → T E
2–3 E' → + T E' | ε
4 'T → F T
٤٩
5–6 T' → * F T' | ε
7–8 F → ( E ) | id
٥٠
12 -ﺗﺠﺰﻳﻪ ﭘﺎﻳﻴﻦ ﺑﻪ ﺑﺎﻻ ) ( Bottom-Up Parsing 3
ﻳﻚ روش ﻛﻠﻲ ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﺑﻪ روش اﻧﺘﻘﺎل – ﻛﺎﻫﺶ ) ( Shift – Reduce
اﺳﺖ.در اﻳﻦ روش ﻋﻜﺲ ﺗﺠﺰﻳﻪ ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ﻋﻤﻞ ﻣﻲ ﺷﻮد.ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﻛﻪ از رﺷﺘﻪ
ورودي ﺷﺮوع ﻛﺮده و ﺳﺎﺧﺖ درﺧﺖ ﺗﺠــﺰﻳﻪ از ﺑﺮگ ﻫﺎ آﻏﺎز ﮔــﺸﺘﻪ و ﺑﻪ ﻃﺮف رﻳــﺸﻪ
) ﻋﻼﻣﺖ ﺷﺮوع ( ﭘﻴﺶ ﻣﻲ رود.
ﺗﺮﺗﻴﺐ ﺑﻜﺎرﮔﻴﺮي ﻗﻮاﻋﺪ در ﭘﺎرس ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ درﺳﺖ ﻣﻄﺎﺑﻖ ﺑﺴﻂ ﭼﭗ اﺳﺖ درﺣﺎﻟﻴﻜﻪ
ﺗﺮﺗﻴﺐ ﺑﻜﺎرﮔﻴﺮي ﻗﻮاﻋﺪ در اﻛﺜﺮ روﺷﻬﺎي ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ درﺳﺖ ﻋﻜﺲ ﺑﺴﻂ راﺳﺖ
اﺳﺖ.ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ :
1 S → aABe
2–3 A → Abc | b
4 B→d
ﺟﻤﻠﻪ abbcdeرا ﻣﻮرد ﺑﺮرﺳﻲ ﻗﺮار ﻣﻲ دﻫﻴﻢ.ﺑﺴﻂ راﺳﺖ اﻳﻦ ﺟﻤﻠﻪ ﺑﺼﻮرت زﻳﺮ اﺳﺖ :
٥١
ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﺟﻤﻠﻪ abbcdeﺑﻪ ﻋﻼﻣﺖ ﺷﺮوع ﮔﺮاﻣﺮ ﻛﺎﻫﺶ ﻣﻲ ﻳﺎﺑﺪ.ﺗﺮﺗﻴﺐ ﻋﻤﻠﻴﺎت در
اﻳﻦ ﻛﺎﻫﺶ درﺳﺖ ﺑﺮﻋﻜﺲ ﺑﺴﻂ راﺳﺖ ﺻﻮرت ﮔﺮﻓﺘﻪ اﺳﺖ.در ﻫﺮ ﻣﺮﺣﻠﻪ از ﻛﺎﻫﺶ در
ﭘﺎرس ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ اﻳﻦ ﻣﺸﻜﻞ وﺟﻮد دارد ﻛﻪ ﭘﺎرﺳﺮ ﻛﺪام زﻳﺮ رﺷﺘﻪ را ﺑﻪ ﻋﻨﻮان دﺳﺘﮕﻴﺮه
اﻧﺘﺨﺎب و ﺳﭙﺲ از ﻛﺪام ﻗﺎﻋﺪه ﺑﺮاي ﻛﺎﻫﺶ آن اﺳﺘﻔﺎده ﻧﻤﺎﻳﺪ.در اداﻣﻪ ﺑﻪ اراﺋﻪ ﭼﻨﺪ ﺗﻌﺮﻳﻒ
در ارﺗﺒﺎط ﺑﺎ ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﻣﻲ ﭘﺮدازﻳﻢ :
ﻋﺒﺎرت ) : ( Phraseﺑﺨﺸﻲ از ﻳﻚ ﻓﺮم ﺟﻤﻠﻪ اي اﺳﺖ ﻛﻪ از ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﻮﺟﻮد
آﻣﺪه ﺑﺎﺷﺪ.ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل در ﺑﺴﻂ زﻳﺮ βﻳﻚ ﻋﺒﺎرت ﻣﺤﺴﻮب ﻣﻲ ﺷﻮد.
* +
═S › αAγ ═› αβγ
اﮔﺮ ﮔﺮاﻣﺮ ﻣﻮرد اﺳﺘﻔﺎده ﮔﻨﮓ ﻧﺒﺎﺷﺪ در ﻫﺮ ﻣﺮﺣﻠﻪ از ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﺗﻨﻬﺎ ﻳﻚ دﺳﺘﮕﻴﺮه
وﺟﻮد دارد.ﻟﻴﻜﻦ در ﺻﻮرت اﺳﺘﻔﺎده از ﻳﻚ ﮔﺮاﻣﺮ ﮔﻨﮓ ﻣﻤﻜﻦ اﺳﺖ در ﺑﻌﻀﻲ از ﻗﺪم ﻫﺎ
ﺑﻴﺸﺘﺮ از ﻳﻚ دﺳﺘﮕﻴﺮه ﻣﻮﺟﻮد ﺑﺎﺷﺪ.ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ :
1–4 E → E + E | E * E | ( E ) | id
از آﻧﺠﺎ ﻛﻪ ﮔﺮاﻣﺮ ﻓﻮق ﮔﻨﮓ اﺳﺖ ﺑﺮاي ﺟﻤﻠﻪ id + id * idدو ﺑﺴﻂ راﺳﺖ و در ﻧﺘﻴﺠﻪ
دو ﻣﺴﻴﺮ ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ وﺟﻮد دارد.اﻳﻦ دو ﺑﺴﻂ در اداﻣﻪ ﻧﺸﺎن داده ﻣﻲ ﺷﻮد.ﻫﻤﺎﻧﮕﻮﻧﻪ
ﻛﻪ ﻣﺸﺎﻫﺪه ﻣﻲ ﺷﻮد در ﻗﺪم ﺳﻮم ﺗﺠﺰﻳﻪ دو اﻧﺘﺨﺎب ﺑﺮاي دﺳﺘﮕﻴﺮه وﺟﻮد دارد.
٥٢
E ═› E + E E ═› E * E
٥٣
ﺑﻪ ﻋﻨﻮان ﻧﻤﻮﻧﻪ ﺗﺠﺰﻳﻪ رﺷﺘﻪ id + id * idﺑﻪ روش اﻧﺘﻘﺎل – ﻛﺎﻫﺶ ﺑﺼﻮرت زﻳﺮ اﺳﺖ :
در ﺗﺠﺰﻳﻪ ﺑﻪ روش اﻧﺘﻘﺎل – ﻛﺎﻫﺶ ﻣﺸﻜﻼت زﻳﺮ وﺟﻮد دارد :
-1ﺗﺼﻤﻴﻢ ﮔﻴﺮي در ﻣﻮرد اﻳﻨﻜﻪ ﻛﺪام زﻳﺮرﺷﺘﻪ ﺗﺸﻜﻴﻞ ﻳﻚ دﺳﺘﮕﻴﺮه ﻣﻲ دﻫﺪ.
-2اﻧﺘﺨﺎب ﻗﺎﻋﺪه اي ﻛﻪ ﺑﺎﻳﺪ ﺑﺮاي ﻛﺎﻫﺶ اﺳﺘﻔﺎده ﺷﻮد.اﻳﻦ ﻣﺸﻜﻞ زﻣﺎﻧﻲ ﺑﺮوز ﻣﻲ ﻛﻨﺪ
ﻛﻪ ﺳﻤﺖ راﺳﺖ ﺑﻴﺶ از ﻳﻚ ﻗﺎﻋﺪه ﺑﺎ دﺳﺘﮕﻴﺮه ﻣﻄﺎﺑﻘﺖ ﻣﻲ ﻛﻨﺪ.ﺑﻪ ﭼﻨﻴﻦ وﺿﻌﻴﺘﻲ "
" ﺗﺪاﺧﻞ ﻛﺎﻫﺶ– ﻛﺎﻫﺶ" ) ( Reduce / Reduce Conflictﮔﻔﺘﻪ ﻣﻲ ﺷﻮد.
٥٤
14 – 3اﻧﻮاع ﺗﺪاﺧﻞ در ﺗﺠﺰﻳﻪ اﻧﺘﻘﺎل – ﻛﺎﻫﺶ
در ﭘﺎرس اﻧﺘﻘﺎل – ﻛﺎﻫﺶ دو ﻧﻮع ﺗﺪاﺧﻞ ﻣﻲ ﺗﻮاﻧﺪ روي دﻫﺪ :
-1ﺗﺪاﺧﻞ اﻧﺘﻘﺎل – ﻛﺎﻫﺶ ) : ( Shift / Reduce Conflict
زﻣﺎﻧﻲ روي ﻣﻲ دﻫﺪ ﻛﻪ ﭘﺎرﺳﺮ ﻧﺘﻮاﻧﺪ ﺗﺼﻤﻴﻢ ﺑﮕﻴﺮد ﻛﻪ ﻋﻤﻞ اﻧﺘﻘﺎل ﺑﺎﻳﺪ اﻧﺠﺎم دﻫﺪ
ﻳﺎ ﻋﻤﻞ ﻛﺎﻫﺶ.
-2ﺗﺪاﺧﻞ ﻛﺎﻫﺶ – ﻛﺎﻫﺶ ) : ( Reduce / Reduce Conflict
اﮔﺮ ﺑﻴﺶ از ﻳﻚ ﻗﺎﻋﺪه ﺟﻬﺖ ﻛﺎﻫﺶ ﻣﻮﺟﻮد ﺑﺎﺷﺪ اﻳﻨﮕﻮﻧﻪ ﺗﺪاﺧﻞ روي ﺧﻮاﻫﺪ
داد.
ﺑﻪ ﻋﻨﻮان ﻣﺜﺎﻟﻲ از ﺗﺪاﺧﻞ ﻧﻮع اول ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ :
Stmt → if expr then Stmt
| if expr then Stmt else Stmt
| other
ﻓﺮض ﻛﻨﻴﺪ ﺗﻮﻛﻦ ﺟﺎري " " elseو رﺷﺘﻪ " " if expr then Stmtﺑﺎﻻي اﻧﺒﺎره
ﻗﺮار داﺷﺘﻪ ﺑﺎﺷﺪ.ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ وﺿﻌﻴﺖ اﻧﺒﺎره ,ﭘﺎرﺳﺮ ﻫﻢ ﻣﻲ ﺗﻮاﻧﺪ ﺑﺎ اﺳﺘﻔﺎده از ﻗﺎﻋﺪه اول
ﻋﻤﻞ ﻛﺎﻫﺶ اﻧﺠﺎم دﻫﺪ و ﻫﻢ ﻣﻲ ﺗﻮاﻧﺪ اﺑﺘﺪا ﺗﻮﻛﻦ ﺟﺎري را ﺑﻪ ﺑﺎﻻي اﻧﺒﺎره اﻧﺘﻘﺎل داده و در
زﻣﺎن ﻣﻨﺎﺳﺐ ﺑﺎ اﺳﺘﻔﺎده از ﻗﺎﻋﺪه دوم ﻋﻤﻞ ﻛﺎﻫﺶ را اﻧﺠﺎم دﻫﺪ.
ﺣﺎل ﺑﻪ ﻣﺜﺎﻟﻲ از ﺗﺪاﺧﻞ ﻧﻮع دوم ﺗﻮﺟﻪ ﻛﻨﻴﺪ.ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ :
1–2 Stmt → id ( Parameter-list ) | Expr := Expr
3–4 Parameter-list → Parameter-list , Parameter | Parameter
5 Parameter → id
6–7 Expr → id ( Expr-list ) | id
8–9 Expr-list → Expr-list , Expr | Expr
ﻗﺎﻋﺪه ﺷﻤﺎره 1اﻳﻦ ﮔﺮاﻣﺮ ﺟﻬﺖ ﺗﻮﺻﻴﻒ ﻓﺮاﺧﻮاﻧﻲ روﻳﻪ ﻫﺎ و ﻗﺎﻋﺪه 6ﮔﺮاﻣﺮ ﺟﻬﺖ
ﺗﻮﺻﻴﻒ ﻣﺮاﺟﻌﻪ ﺑﻪ آراﻳﻪ ﻫﺎ اﺳﺖ.ﻓﺮض ﻛﻨﻴﺪ ﺑﻪ ﻋﻨﻮان ﺑﺨﺸﻲ از ورودي رﺷﺘﻪ ")" A (I,J
آﻣﺪه ﺑﺎﺷﺪ.اﻳﻦ ﺑﺨﺶ از ورودي ﺑﻮﺳﻴﻠﻪ اﺳﻜﻨﺮ ﺑﺼﻮرت " ) " id(id,idﺗﺒﺪﻳﻞ ﻣﻲ
ﮔﺮدد.ﻫﻤﭽﻨﻴﻦ ﻓﺮض ﻛﻨﻴﺪ در وﺿﻌﻴﺘﻲ از ﺗﺠﺰﻳﻪ ﻗﺮار دارﻳﻢ ﻛﻪ ﺑﺎﻗﻴﻤﺎﻧﺪه ورودي ﺑﺼﻮرت
" " ,id)…$و زﻳﺮرﺷﺘﻪ " " id(idﺑﺎﻻي اﻧﺒﺎره ﻇﺎﻫﺮ ﺷﺪه ﺑﺎﺷﺪ.در اﻳﻦ ﺣﺎﻟﺖ از دو
٥٥
ﻗﺎﻋﺪه ﺟﻬﺖ ﻋﻤﻞ ﻛﺎﻫﺶ " " idﻣﻲ ﺗﻮان اﺳﺘﻔﺎده ﻧﻤﻮد ) ﻗﻮاﻋﺪ 5و .( 7ﻳﻌﻨﻲ ﻳﻚ ﺗﺪاﺧﻞ
ﻛﺎﻫﺶ /ﻛﺎﻫﺶ رخ داده اﺳﺖ.در اﻳﻦ ﻣﺜﺎل اﻧﺘﺨﺎب ﻗﺎﻋﺪه درﺳﺖ ﺑﺴﺘﮕﻲ ﺑﻪ ﻧﻮع ﻣﺘﻐﻴﺮ A
دارد.در ﺻﻮرﺗﻲ ﻛﻪ Aﻳﻚ روﻳﻪ ﺑﺎﺷﺪ ﺑﺎﻳﺪ از ﻗﺎﻋﺪه 5و اﮔﺮ Aﻳﻚ آراﻳﻪ ﺑﺎﺷﺪ ﺑﺎﻳﺪ از
ﻗﺎﻋﺪه 7ﺑﺮاي ﻋﻤﻞ ﻛﺎﻫﺶ اﺳﺘﻔﺎده ﺷﻮد.ﻳﻌﻨﻲ ﭘﺎرﺳﺮ ﺑﺎﻳﺪ ﺑﺎ ﻣﺮاﺟﻌﻪ ﺑﻪ ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ و ﭘﻲ
ﺑﺮدن ﺑﻪ ﻧﻮع Aاﻳﻦ ﺗﺪاﺧﻞ را ﺣﻞ ﻛﻨﺪ.راه ﺣﻞ دﻳﮕﺮي ﻧﻴﺰ ﺟﻬﺖ رﻓﻊ اﻳﻦ ﻧﻮع ﻣﺸﻜﻞ
وﺟﻮد دارد.اﮔﺮ اﺳﻜﻨﺮ در ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﻣﺘﻐﻴﺮ ورودي ﻳﻚ روﻳﻪ اﺳﺖ ﺑﺠﺎي ﺗﻮﻛﻦ " " id
ﺗﻮﻛﻦ دﻳﮕﺮي ﻣﺜﻼ " " procidﺑﻪ ﭘﺎرﺳﺮ اﻧﺘﻘﺎل دﻫﺪ دراﻳﻨﺼﻮرت در ﻣﻮﻗﻊ ﺑﺮﺧﻮرد ﺑﺎ
وﺿﻌﻴﺖ ﺗﺪاﺧﻞ ﻛﺎﻓﻴﺴﺖ ﭘﺎرﺳﺮ داﺧﻞ اﻧﺒﺎره و ﺗﻮﻛﻦ زﻳﺮ " ( " را ﺑﺮرﺳﻲ ﻛﻨﺪ.اﮔﺮ اﻳﻦ
ﺗﻮﻛﻦ procidﺑﺎﺷﺪ ﭘﺎرﺳﺮ از ﻗﺎﻋﺪه ﺷﻤﺎره 5و در ﻏﻴﺮ اﻳﻨﺼﻮرت از ﻗﺎﻋﺪه ﺷﻤﺎره 7
ﺟﻬﺖ ﻛﺎﻫﺶ idﺑﺎﻻي اﻧﺒﺎره اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ.ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ در اﻳﻨﺼﻮرت ﻗﺎﻋﺪه
ﺷﻤﺎره 1ﺑﺎﻳﺴﺘﻲ ﺑﺼﻮرت زﻳﺮ اﺻﻼح ﮔﺮدد :
1 )Stmt → procid( Parameter-list
٥٦
15 – 3روش ﺗﺠﺰﻳﻪ ﺗﻘﺪم – ﻋﻤﻠﮕﺮ ) ( Operator-Precedence Parsing
ﮔﺮاﻣﺮ ﻋﻤﻠﮕﺮ :ﮔﺮاﻣﺮي اﺳﺖ ﻛﻪ داراي ﺧﺼﻮﺻﻴﺎت زﻳﺮ ﺑﺎﺷﺪ :
.1ﻗﺎﻋﺪه εﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ.
.2در ﺳﻤﺖ راﺳﺖ ﻫﻴﭻ ﻗﺎﻋﺪه اي از آن دو ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻣﺠﺎور ﻧﺒﺎﺷﻨﺪ.
ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل ﮔﺮاﻣﺮ زﻳﺮ ﻳﻚ ﮔﺮاﻣﺮ ﻋﻤﻠﮕﺮ ﻧﻴﺴﺖ زﻳﺮا ﺳﻤﺖ راﺳﺖ ﻗﺎﻋﺪه E → EAE
دو ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻣﺠﺎور دارد.
E → EAE | (E) | -E | id
↑|A→+|-|*|/
اﮔﺮ اﻳﻦ ﮔﺮاﻣﺮ را ﺑﺼﻮرت زﻳﺮ ﺗﺒﺪﻳﻞ ﻛﻨﻴﻢ ﮔﺮاﻣﺮ ﻋﻤﻠﮕﺮ ﺧﻮاﻫﺪ ﺷﺪ :
E → E+E | E-E | E*E | E/E | E↑E | (E) | -E | id
ﺗﺬﻛﺮ :ﻳﻜﻲ از ﺷﺮاﻳﻄﻲ ﻛﻪ ﺑﺎﻳﺪ وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﺪ ﺗﺎ ﺑﺘﻮان از روش ﺗﺠﺰﻳﻪ ﺗﻘﺪم – ﻋﻤﻠﮕﺮ
اﺳﺘﻔﺎده ﻧﻤﻮد اﻳﻦ اﺳﺖ ﻛﻪ ﮔﺮاﻣﺮ ﺑﺎﻳﺪ ﻳﻚ ﮔﺮاﻣﺮ ﻋﻤﻠﮕﺮ ﺑﺎﺷﺪ.
٥٧
a = b -2ﻳﻌﻨﻲ ﭘﺎﻳﺎﻧﻪ aو ﭘﺎﻳﺎﻧﻪ bاز ﺗﻘﺪم ﻳﻜﺴﺎﻧﻲ ﺑﺮﺧﻮردارﻧﺪ.ﻣﺎﻧﻨﺪ ) = (
a > b -3ﻳﻌﻨﻲ ﭘﺎﻳﺎﻧﻪ aاز ﭘﺎﻳﺎﻧﻪ bﺗﻘﺪم ﺑﻴﺸﺘﺮي دارد.ﻣﺎﻧﻨﺪ * > id
رواﺑﻂ ﺗﻘﺪﻣﻲ ﻛﻪ در اﻳﻨﺠﺎ ﺑﻴﻦ ﭘﺎﻳﺎﻧﻪ ﻫﺎ ﺗﻮﺻﻴﻒ ﻣﻲ ﺷﻮد ﺑﺎ رواﺑﻂ < > , = ,ﻣﻌﻤﻮﻟﻲ ﻛﻪ
در ﺑﻴﻦ اﻋﺪاد ﻃﺒﻴﻌﻲ ﺑﺮﻗﺮار اﺳﺖ ﺗﻔﺎوت زﻳﺎدي دارﻧﺪ.ﺑﻪ ﻋﻨﻮان ﻧﻤﻮﻧﻪ در اﻳﻨﺠﺎ ﺑﺎ داﻧﺴﺘﻦ
اﻳﻨﻜﻪ راﺑﻄﻪ a < bﺑﺮﻗﺮار اﺳﺖ ﻧﻤﻲ ﺗﻮان ﻧﺘﻴﺠﻪ ﮔﺮﻓﺖ ﻛﻪ . b > aاز ﻃﺮﻓﻲ ﻣﻤﻜﻦ اﺳﺖ
در ﭘﺎﻳﺎﻧﻪ ﻫﻴﭽﻴﻚ از اﻳﻦ ﺳﻪ راﺑﻄﻪ ﺗﻘﺪم را ﺑﺎ ﻫﻢ ﻧﺪاﺷﺘﻪ ﺑﺎﺷﻨﺪ و ﻳﺎ اﻳﻨﻜﻪ دو ﭘﺎﻳﺎﻧﻪ دو راﺑﻄﻪ
ﺗﻘﺪم ﻣﺘﻔﺎوت داﺷﺘﻪ ﺑﺎﺷﻨﺪ.ﻣﺜﻼ داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻛﻪ * < -و ﻫﻢ * > . -
1 – 15 – 3اﻟﮕﻮرﻳﺘﻢ ﺗﺠﺰﻳﻪ ﺗﻘﺪم – ﻋﻤﻠﮕﺮ
ﺳﺎﺧﺘﺎر ﭘﺎرﺳﺮ در روش ﺗﻘﺪم – ﻋﻤﻠﮕﺮ ﻛﺎﻣﻼ ﻣﺸﺎﺑﻪ ﺳﺎﺧﺘﺎر ﻳﻚ ﭘﺎرﺳﺮ ) LL(1اﺳﺖ.در
اﻳﻦ ﺳﺎﺧﺘﺎر ﻛﻪ در ﺷﻜﻞ زﻳﺮ ﻧﺸﺎن داده ﺷﺪه اﺳﺖ ﻣﻮﻟﻔﻪ اﺻﻠﻲ ﭘﺎرﺳﺮ ﻳﻚ ﺑﺮﻧﺎﻣﻪ اﺳﺖ ﻛﻪ
از ﻳﻚ ﻃﺮف ورودي ﺧﻮد را از اﺳﻜﻨﺮ درﻳﺎﻓﺖ ﻣﻲ ﻛﻨﺪ و از ﻳﻚ اﻧﺒﺎره ﺑﺮاي ذﺧﻴﺮه
اﻃﻼﻋﺎت و از ﻳﻚ ﺟﺪول ﺗﺠﺰﻳﻪ ﺑﺮاي ﻫﺪاﻳﺖ ﻋﻤﻞ ﺗﺠﺰﻳﻪ اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ.در اﻳﻦ روش
ﭘﺎرﺳﺮ اﺑﺘﺪا ﻳﻚ ﻋﻼﻣﺖ $ﺑﻪ اﻧﺘﻬﺎي رﺷﺘﻪ ورودي اﺿﺎﻓﻪ ﻣﻲ ﻛﻨﺪ.اﻧﺒﺎره ﻧﻴﺰ در اﺑﺘﺪاي ﻛﺎر
ﻓﻘﻂ ﺷﺎﻣﻞ ﻳﻚ ﻋﻼﻣﺖ $اﺳﺖ.
ﺟﺪول ﭘﺎرس در اﻳﻦ روش ﻳﻚ ﺟﺪول دو ﺑﻌﺪي ﻣﺮﺑﻊ اﺳﺖ ﻛﻪ ﺑﻪ ﺗﻌﺪاد ﭘﺎﻳﺎﻧﻪ ﻫﺎي ﺑﻌﻼوه
ﻳﻚ ) ﺑﻪ ﺧﺎﻃﺮ ﻋﻼﻣﺖ ( $ﺳﻄﺮ و ﺳﺘﻮن دارد.در داﺧﻞ ﺟﺪول ﻧﻴﺰ در ﺑﺮﺧﻲ ﺧﺎﻧﻪ ﻫﺎي
ﺟﺪول ﻳﻜﻲ از ﻋﻼﻣﺖ ﻫﺎي < > ,و ﻳﺎ = ﻗﺮار دارد و ﻣﺎﺑﻘﻲ ﺧﺎﻧﻪ ﻫﺎي ﺟﺪول ﺧﺎﻟﻲ اﺳﺖ.
a
S
Stack
٥٨
در اﻳﻦ روش ﺑﺮﻧﺎﻣﻪ ﭘﺎرس در ﻫﺮ ﻗﺪم از ﭘﺎرس ﺑﺎ اﺳﺘﻔﺎده از ﺑﺎﻻﺗﺮﻳﻦ ﭘﺎﻳﺎﻧﻪ اﻧﺒﺎره ) (aو
ﺗﻮﻛﻦ ﺟﺎري ) ) (bﺑﻪ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﺎﻻي اﻧﺒﺎره ﺗﻮﺟﻬﻲ ﻧﺪارد ( ﺑﻪ ﻋﻨﻮان اﻧﺪﻳﺲ ﺟﺪول
ﺗﺠﺰﻳﻪ و ﻣﺮاﺟﻌﻪ ﺑﻪ اﻳﻦ ﺟﺪول ﻳﻜﻲ از اﻋﻤﺎل زﻳﺮ را اﻧﺠﺎم ﻣﻲ دﻫﺪ :
-1اﮔﺮ راﺑﻄﻪ ﭘﺎﻳﺎﻧﻪ ﺑﺎﻻي اﻧﺒﺎره و ﺗﻮﻛﻦ ﺟﺎري ﺑﺼﻮرت a < bﺑﺎﺷﺪ ﭘﺎرﺳﺮ اﺑﺘﺪا
ﻋﻼﻣﺖ < و ﺳﭙﺲ ﺗﻮﻛﻦ ﺟﺎري bرا ﺑﻪ ﺑﺎﻻي اﻧﺒﺎره اﻧﺘﻘﺎل ﻣﻲ دﻫﺪ.
-2اﮔﺮ راﺑﻄﻪ ﭘﺎﻳﺎﻧﻪ ﺑﺎﻻي اﻧﺒﺎره و ﺗﻮﻛﻦ ﺟﺎري ﺑﺼﻮرت a = bﺑﺎﺷﺪ ﭘﺎرﺳﺮ ﻓﻘﻂ
ﺗﻮﻛﻦ ﺟﺎري را ﺑﻪ ﺑﺎﻻي اﻧﺒﺎره اﻧﺘﻘﺎل ﻣﻲ دﻫﺪ.
-3اﮔﺮ راﺑﻄﻪ ﭘﺎﻳﺎﻧﻪ ﺑﺎﻻي اﻧﺒﺎره و ﺗﻮﻛﻦ ﺟﺎري ﺑﺼﻮرت a > bﺑﺎﺷﺪ ﭘﺎرﺳﺮ ﻋﻤﻞ
ﻛﺎﻫﺶ را اﻧﺠﺎم ﻣﻲ دﻫﺪ.ﺑﺮاي اﻳﻨﻜﺎر در داﺧﻞ اﻧﺒﺎره آﻧﻘﺪر ﭘﺎﺋﻴﻦ ﻣﻲ رود ﺗﺎ ﺑﻪ
اوﻟﻴﻦ ﻋﻼﻣﺖ < ﺑﺮﺳﺪ ,دﺳﺘﮕﻴﺮه رﺷﺘﻪ ﻣﺎﺑﻴﻦ اﻳﻦ ﻋﻼﻣﺖ و ﺑﺎﻻي اﻧﺒﺎره اﺳﺖ
) ﺑﻌﻼوه ﻏﻴﺮﭘﺎﻳﺎﻧﻪ زﻳﺮ ﻋﻼﻣﺖ < در ﺻﻮرت وﺟﻮد ( .ﭘﺎرﺳﺮ ﺑﺮاي اﻧﺠﺎم ﻋﻤﻞ
ﻛﺎﻫﺶ دﺳﺘﮕﻴﺮه ﭘﻴﺪا ﺷﺪه را از اﻧﺒﺎره ﺣﺬف و ﺑﻪ ﺟﺎي آن ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻧﻮﻋـﻲ
) ﻣﺜﻼ ( Nوارد اﻧﺒﺎره ﻣﻲ ﻛﻨﺪ ).در روش ﺗﻘﺪم – ﻋﻤﻠﮕﺮ ﭘﺲ از ﺗﻬﻴﻪ ﺟﺪول
ﺗﺠﺰﻳﻪ از روي ﮔﺮاﻣﺮ دﻳﮕﺮ ﺑﻴﻦ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎي ﮔﺮاﻣﺮ ﺗﻤﺎﻳﺰي ﻗﺎﺋﻞ ﻧﻤﻲ ﺷﻮﻳﻢ و
ﺑﺠﺎي ﻫﻤﻪ آﻧﻬﺎ ﻣﻲ ﺗﻮان از ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻧﻮﻋﻲ اﺳﺘﻔﺎده ﻧﻤﻮد.ﻫﻤﭽﻨﻴﻦ اﻳﻦ ﻋﺎﻣﻞ
ﺑﺎﻋﺚ ﺿﻌﻒ اﻳﻦ روش در ﻛﺸﻒ ﺑﺮﺧﻲ از ﺧﻄﺎﻫﺎي ﻧﺤﻮي ﮔﺮدﻳﺪه اﺳﺖ(.
-4اﮔﺮ ﭘﺎﻳﺎﻧﻪ ﺑﺎﻻي اﻧﺒﺎره ﺑﺎ ورودي ﺟﺎري راﺑﻄﻪ اي ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ ﻳﻚ ﺧﻄﺎي
ﻧﺤﻮي اﺳﺖ و ﭘﺎرﺳﺮ روﻳﻪ اﺻﻼح ﺧﻄﺎ را ﻓﺮا ﻣﻲ ﺧﻮاﻧﺪ.
ﺣﺎل ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل ﺑﻪ ﺗﺠﺰﻳﻪ رﺷﺘﻪ id + id * idﺑﺎ اﺳﺘﻔﺎده از ﺟﺪول ﺗﺠﺰﻳﻪ ﮔﺮاﻣﺮﻏﻴﺮ
ﮔﻨﮓ ﻋﺒﺎرات ﺟﺒﺮي ﺗﻮﺟﻪ ﻛﻨﻴﺪ.ﺟﺪول ﺗﺠﺰﻳﻪ و ﻣﺮاﺣﻞ ﺗﺠﺰﻳﻪ ﺑﺼﻮرت ﻗﺪم ﺑﻪ ﻗﺪم در
ﺷﻜﻞ ﻫﺎي ﺻﻔﺤﻪ ﺑﻌﺪ آﻣﺪه اﺳﺖ.در ﻗﺪم ﻫﺎﻳﻲ ﻛﻪ ﻋﻤﻞ ﻛﺎﻫﺶ ﺻﻮرت ﮔﺮﻓﺘﻪ زﻳﺮ
دﺳﺘﮕﻴﺮه ﺧﻂ ﻛﺸﻴﺪه ﺷﺪه اﺳﺖ.
E→E+T | T
T→T*F | F
F→ (E) | id
٥٩
+ * ( ) id $
٦٠
ﻛﻪ در آن aﻳﻚ ﭘﺎﻳﺎﻧﻪ B ,ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ و αﻳﻚ رﺷﺘﻪ از ﭘﺎﻳﺎﻧﻪ و ﻏﻴﺮﭘﺎﻳﺎﻧﻪ اﺳﺖ.
ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ ﺗﻌﺎرﻳﻒ ﺗﻮاﺑﻊ ﻓﻮق راﺑﻄﻪ ﻫﺎي ﺗﻘﺪم ﺑﺼﻮرت زﻳﺮ ﺗﻌﺮﻳﻒ ﻣﻲ ﺷﻮﻧﺪ :
a=b iff Э U → . . . ab. . . or U → . . . aWb. . .
a<b iff )Э U → . . . aW. . . and b Є Firstterm(W
a>b iff )Э U → . . . Wb. . . and a Є Lastterm(W
ﻛﻪ Wﻳﻚ ﻏﻴﺮ ﭘﺎﻳﺎﻧﻪ اﺳﺖ.
ﺣﺎل ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ :
E→E+T|T
T→T*F|F
F → ( E ) | id
اﮔﺮ Firsttermو Lasttermرا ﺑﺮ روي ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎي اﻳﻦ ﮔﺮاﻣﺮ اﻋﻤﺎل ﻛﻨﻴﻢ ﺣﺎﺻﻞ
ﺑﺼﻮرت زﻳﺮ ﺧﻮاﻫﺪ ﺷﺪ :
} Firstterm(E) = { + , * , ( , id
} Firstterm(T) = { * , ( , id
} Firstterm(F) = { ( , id
} Lastterm(E) = { + , * , ) , id
} Lastterm(T) = { * , ) , id
} Lastterm(F) = { ) , id
ﺑﺮاي ﺑﺪﺳﺖ آوردن راﺑﻄﻪ ﻋﻼﻣﺖ $و ﺳﺎﻳﺮ ﭘﺎﻳﺎﻧﻪ ﻫﺎ ﻗﺎﻋﺪه اي ﺑﻔﺮم N → $ S $ﻛﻪ
در آن Nﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺟﺪﻳﺪ و Sﻋﻼﻣﺖ ﺷﺮوع ﮔﺮاﻣﺮ اﺳﺖ ﺑﻪ ﮔﺮاﻣﺮ اﺿﺎﻓﻪ ﻣﻲ ﻛﻨﻴﻢ.
ﺣﺎل ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ ﺗﻌﺎرﻳﻒ رواﺑﻂ ﺗﻘﺪم – ﻋﻤﻠﮕﺮ ﻛﻪ در ﺑﺎﻻ ﻋﻨﻮان ﺷﺪ راﺑﻄﻪ ﺗﺴﺎوي ﺑﻪ
ﺳﺎدﮔﻲ ﺑﺎ ﺑﺮرﺳﻲ ﻗﻮاﻋﺪ ﺑﺪﺳﺖ ﻣﻲ آﻳﺪ.در ﮔﺮاﻣﺮ ﻓﻮق در ﻧﻈﺮ ﮔﺮﻓﺘﻦ ﻗﺎﻋﺪه ﺟﺪﻳﺪي ﻛﻪ
ﺑﺨﺎﻃﺮ ﻋﻼﻣﺖ $اﺿﺎﻓﻪ ﻣﻲ ﮔﺮدد دو راﺑﻄﻪ ﺗﺴﺎوي وﺟﻮد دارد $ = $ :و ) = ( .ﺗﻮﺟﻪ
داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ ) = ( ﻫﻴﭻ اﻃﻼﻋﻲ در ﻣﻮرد راﺑﻄﻪ " ) " و " ( " ﺑﻪ ﻣﺎ ﻧﻤﻲ دﻫﺪ.ﻣﺜﻼ از
اﻳﻦ ﻧﻤﻲ ﺗﻮان ﻧﺘﻴﺠﻪ ﮔﺮﻓﺖ ﻛﻪ ( = ) .
ﺑﺮاي ﻳﺎﻓﺘﻦ رواﺑﻂ < ,ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ ﺗﻌﺮﻳﻒ آن ﺑﻪ دﻧﺒﺎل ﻧﻘﺎﻃﻲ در ﮔﺮاﻣﺮ ﻣﻲ ﮔﺮدﻳﻢ ﻛﻪ ﻳﻚ
ﭘﺎﻳﺎﻧﻪ در ﺳﻤﺖ ﭼﭗ ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻗﺮار ﮔﺮﻓﺘﻪ ﺑﺎﺷﺪ.ﻣﺜﻼ در ﻗﺎﻋﺪه ﭘﻨﺠﻢ اﻳﻦ ﮔﺮاﻣﺮ " ( "
٦١
ﺳﻤﺖ ﭼﭗ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ Eﻗﺮار ﮔﺮﻓﺘﻪ اﺳﺖ.ﺣﺎل اﮔﺮ ﭘﺎﻳﺎﻧﻪ اي ﻣﺜﻼ bﻋﻀﻮ ﻣﺠﻤﻮﻋﻪ
) Firstterm(Eﺑﺎﺷﺪ راﺑﻄﻪ ﺗﻘﺪم ( < bﺑﺮﻗﺮار اﺳﺖ.در واﻗﻊ اﻳﻦ راﺑﻄﻪ ﺑﻴﻦ " ( " و
ﻫﻤﻪ اﻋﻀﺎ ) Firstterm(Eﺑﺮﻗﺮار اﺳﺖ.ﻳﻌﻨﻲ در اﻳﻨﺠﺎ ﻣﻲ ﺗﻮان ﻧﺘﻴﺠﻪ ﮔﺮﻓﺖ ﻛﻪ رواﺑﻂ (
( < id , ( < ( , ( < * , < +ﺑﺮﻗﺮار اﺳﺖ.ﺑﻪ ﻫﻤﻴﻦ ﺗﺮﺗﻴﺐ ﻣﻲ ﺗﻮان ﺳﺎﻳﺮ رواﺑﻂ ﺗﻘﺪم
< را ﻧﻴﺰ ﻳﺎﻓﺖ.
ﺑﺮاي ﻳﺎﻓﺘﻦ رواﺑﻂ > ,ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ ﺗﻌﺮﻳﻒ آن ﺑﻪ دﻧﺒﺎل ﻧﻘﺎﻃﻲ در ﮔﺮاﻣﺮ ﻣﻲ ﮔﺮدﻳﻢ ﻛﻪ ﻳﻚ
ﭘﺎﻳﺎﻧﻪ در ﺳﻤﺖ راﺳﺖ ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻗﺮار ﮔﺮﻓﺘﻪ ﺑﺎﺷﺪ.ﻣﺜﻼ در ﻗﺎﻋﺪه ﭘﻨﺠﻢ اﻳﻦ ﮔﺮاﻣﺮ " ) ,
" ﺳﻤﺖ راﺳﺖ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ Eﻗﺮار ﮔﺮﻓﺘﻪ اﺳﺖ.ﺣﺎل اﮔﺮ ﭘﺎﻳﺎﻧﻪ اي ﻣﺜﻼ aﻋﻀﻮ ﻣﺠﻤﻮﻋﻪ
) Lastterm(Eﺑﺎﺷﺪ راﺑﻄﻪ ﺗﻘﺪم ) > aﺑﺮﻗﺮار اﺳﺖ.در واﻗﻊ اﻳﻦ راﺑﻄﻪ ﻧﻴﺰ ﺑﻴﻦ " ) " و
ﻫﻤﻪ اﻋﻀﺎ ) Lastterm(Eﺑﺮﻗﺮار اﺳﺖ.ﻳﻌﻨﻲ در اﻳﻨﺠﺎ ﻣﻲ ﺗﻮان ﻧﺘﺠﻪ ﮔﺮﻓﺖ ﻛﻪ رواﺑﻂ
) > ) > ) , * > ) , +و ) > idﺑﺮﻗﺮار اﺳﺖ.ﺑﻪ ﻫﻤﻴﻦ ﺗﺮﺗﻴﺐ ﻣﻲ ﺗﻮان ﺳﺎﻳﺮ رواﺑﻂ
ﺗﻘﺪم > را ﻧﻴﺰ ﻳﺎﻓﺖ.
در ﻧﻬﺎﻳﺖ ﺟﺪول رواﺑﻂ ﺗﻘﺪم ﮔﺮاﻣﺮ ﻓﻮق ﺑﺼﻮرت زﻳﺮ ﺧﻮاﻫﺪ ﺑﻮد.ﻣﺤﻞ ﻫﺎي ﺧﺎﻟﻲ در
ﺟﺪول ﻧﺸﺎﻧﮕﺮ آن اﺳﺖ ﻛﻪ دو ﭘﺎﻳﺎﻧﻪ ﺑﺎ ﻫﻢ راﺑﻄﻪ ﻧﺪارﻧﺪ.ﻣﺜﻼ در اﻳﻨﺠﺎ idﺑﺎ idراﺑﻄﻪ
ﻧﺪارد.اﻳﻦ ﺑﺪان ﻣﻌﻨﻲ اﺳﺖ ﻛﻪ ﺑﺎ اﺳﺘﻔﺎده از ﮔﺮاﻣﺮ ﻓﻮق ﻧﻤﻲ ﺗﻮان ﻓﺮم ﺟﻤﻠﻪ اي ﺗﻮﻟﻴﺪ ﻧﻤﻮد
ﻛﻪ در آن دو idﻣﺠﺎور ﻫﻢ ﻗﺮار ﺑﮕﻴﺮﻧﺪ ).در اﻳﻨﺠﺎ ﻣﻨﻈﻮر از ﻣﺠﺎور ﺑﻮدن دو ﭘﺎﻳﺎﻧﻪ اﻳﻦ
اﺳﺖ ﻛﻪ ﻳﺎ دﻗﻴﻘﺎ ﻣﺠﺎور ﺑﺎﺷﻨﺪ و ﻳﺎ اﻳﻨﻜﻪ ﺑﻴﻦ آﻧﻬﺎ ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﺎﺷﺪ.ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ
ﺑﻪ دﻟﻴﻞ ﻣﺤﺪودﻳﺖ ﺧﺎﺻﻲ ﻛﻪ روي ﻗﻮاﻋﺪ ﮔﺮاﻣﺮ ﻋﻤﻠﮕﺮ وﺟﻮد دارد اﻣﻜﺎن ﻧﺪارد ﻛﻪ در
ﻳﻚ ﻓﺮم ﺟﻤﻠﻪ اي دو ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻣﺠﺎور ﻫﻢ ﻗﺮار ﺑﮕﻴﺮﻧﺪ.ﺑﻨﺎﺑﺮاﻳﻦ ﺣﺪاﻛﺜﺮ ﻣﻤﻜﻦ اﺳﺖ ﺑﻴﻦ دو
ﭘﺎﻳﺎﻧﻪ ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻗﺮار داﺷﺘﻪ ﺑﺎﺷﺪ ﻛﻪ در اﻳﻨﺼﻮرت ﻧﻴﺰ ﻃﺒﻖ ﺗﻌﺮﻳﻒ آن دو ﭘﺎﻳﺎﻧﻪ ﻣﺠﺎور
ﻣﺤﺴﻮب ﻣﻲ ﺷﻮﻧﺪ(.
+ * ( ) id $
٦٢
3 -15 – 3اﺻﻼح ﺧﻄﺎ در روش ﺗﻘﺪم – ﻋﻤﻠﮕﺮ
در اﻳﻦ روش ﻛﻼ ﺑﻪ دو ﺻﻮرت ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي ﺗﺸﺨﻴﺺ داده ﻣﻲ ﺷﻮد.اول وﻗﺘﻴﻜﻪ ﻫﻴﭻ
راﺑﻄﻪ اي ﺑﻴﻦ ﭘﺎﻳﺎﻧﻪ ﺑﺎﻻي اﻧﺒﺎره و ورودي ﺟﺎري ﻧﺒﺎﺷﺪ و دوم ﻫﻨﮕﺎﻣﻲ ﻛﻪ دﺳﺘﮕﻴﺮه ﺑﺎﻻي
اﻧﺒﺎره ﺑﺎ ﺳﻤﺖ راﺳﺖ ﻫﻴﭻ ﻗﺎﻋﺪه اي ﺗﻄﺒﻴﻖ ﻧﻜﻨﺪ.
ﺑﺮاي اﺻﻼح ﺧﻄﺎﻫﺎي ﻧﻮع اول در ﺧﺎﻧﻪ ﻫﺎي ﺧﺎﻟﻲ ﺟﺪول ﻧﺸﺎﻧﻪ روﻫﺎﻳﻲ ﺑﻪ زﻳﺮ روال ﻫﺎي
اﺻﻼح ﺧﻄﺎ ﻣﻲ ﮔﺬارﻳﻢ ﺑﻄﻮرﻳﻜﻪ اﮔﺮ در ﻋﻤﻞ ﺗﺠﺰﻳﻪ ﺑﻪ ﻳﻚ ﺧﺎﻧﻪ ﺧﺎﻟﻲ ﺟﺪول رﺟﻮع
ﺷﺪه زﻳﺮروال ﻣﺮﺑﻮﻃﻪ ﻓﺮاﺧﻮاﻧﻲ ﺷﺪه و ﺧﻄﺎ ﺑﻪ ﻧﺤﻮ ﻣﻘﺘﻀﻲ اﺻﻼح ﮔﺮدد.
E→E+T|T ﺑﻪ ﻋﻨﻮان ﻧﻤﻮﻧﻪ ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ :
T → (E) | id
ﺟﺪول ﺗﺠﺰﻳﻪ اﻳﻦ ﮔﺮاﻣﺮ ﺑﺼﻮرت زﻳﺮ اﺳﺖ :
id ( ) $ +
٦٣
دو ﻋﻼﻣﺖ ﺗﻔﺎوت داﺷﺘﻪ ﺑﺎﺷﻨﺪ ( ﺟﺴﺘﺠﻮ ﻣﻲ ﻛﻨﺪ و ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﺧﺘﻼف دﺳﺘﮕﻴﺮه و ﺳﻤﺖ
راﺳﺖ ﻗﺎﻋﺪه ﭘﻴﺪا ﺷﺪه ﭘﻴﻐﺎم ﻣﻨﺎﺳﺒﻲ ﭼﺎپ ﻣﻲ ﻛﻨﺪ و ﻋﻤﻞ ﻛﺎﻫﺶ را اﻧﺠﺎم ﻣﻲ دﻫﺪ.
ﻣﺜﻼ ﻓﺮض ﻛﻨﻴﺪ دﺳﺘﮕﻴﺮه aNbcﺑﺎﺷﺪ و ﻗﺎﻋﺪه اي ﺑﺼﻮرت ) ( … → aEcﭘﻴﺪا ﺷﻮد.از
آﻧﺠﺎﻳﻲ ﻛﻪ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎ در اﻳﻦ روش ﺗﺠﺰﻳﻪ اﻫﻤﻴﺘﻲ ﻧﺪارﻧﺪ و ﺗﻨﻬﺎ ﻣﺤﻞ آﻧﻬﺎ در اﻧﺒﺎره
اﻫﻤﻴﺖ دارد ﻟﺬا در ﻣﻘﺎﻳﺴﻪ دﺳﺘﮕﻴﺮه ﺑﺎ ﺳﻤﺖ راﺳﺖ ﻗﻮاﻋﺪ ﺗﻨﻬﺎ ﺑﻪ ﻣﻮﻗﻌﻴﺖ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎ
اﻫﻤﻴﺖ داده ﻣﻲ ﺷﻮد.در اﻳﻦ ﻣﺜﺎل ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﻳﻨﻜﻪ اﺧﺘﻼف دﺳﺘﮕﻴﺮه و ﺳﻤﺖ راﺳﺖ ﻗﺎﻋﺪه
در ﭘﺎﻳﺎﻧﻪ " " bاﺳﺖ ﭘﻴﻐﺎم زﻳﺮ ﺻﺎدر ﻣﻲ ﺷﻮد.ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ ﭘﺎﻳﺎﻧﻪ ﻫﺎي اﺿﺎﻓﻲ در
دﺳﺘﮕﻴﺮه ﻧﺸﺎﻧﻪ ﻋﻼﺋﻢ اﺿﺎﻓﻲ در ﺑﺮﻧﺎﻣﻪ ورودي اﺳﺖ.
Illegal " b " on line ….
ﺣﺎل اﮔﺮ دﺳﺘﮕﻴﺮه ﺑﺼﻮرت abEcﺑﺎﺷﺪ و ﺳﻤﺖ راﺳﺖ ﻗﺎﻋﺪه ﭘﻴﺪا ﺷﺪه ﺑﺼﻮرت abEdc
ﺑﺎﺷﺪ ﭘﻴﻐﺎم زﻳﺮ ﺻﺎدر ﺧﻮاﻫﺪ ﺷﺪ :
Missing " d " on line ….
ﻣﻤﻜﻦ اﺳﺖ اﺧﺘﻼف در ﻣﻮرد ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﺎﺷﺪ.ﺑﻌﻨﻮان ﻣﺜﺎل ﻓﺮض ﻛﻨﻴﺪ abcدﺳﺘﮕﻴﺮه و
aEbcﺳﻤﺖ راﺳﺖ ﻗﺎﻋﺪه اي از ﮔﺮاﻣﺮ ﺑﺎﺷﺪ.دراﻳﻨﺼﻮرت ﺻﺪور ﭘﻴﻐﺎﻣﻲ ﺑﺼﻮرت "
… " Missing " E " on lineﻣﺠﺎز ﻧﻴﺴﺖ.زﻳﺮا ﻛﺎرﺑﺮ ﻳﻚ ﻛﺎﻣﭙﺎﻳﻠﺮ اﻃﻼﻋﻲ در
ﻣﻮرد ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎي ﮔﺮاﻣﺮ ﻧﺪارد و ﻟﺬا در ﭼﺎپ ﭘﻴﻐﺎﻣﻬﺎ ﻧﺒﺎﻳﺴﺘﻲ از ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎ اﺳﺘﻔﺎده
ﻧﻤﻮد.در اﻳﻦ ﺣﺎﻟﺖ ﺑﺎﻳﺴﺘﻲ ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ ﺳﺎﺧﺘﺎر ﻧﺤﻮي ﻛﻪ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻣﻮردﻧﻈﺮ ﺗﻮﺻﻴﻒ ﻣﻲ ﻛﻨﺪ
درﺑﺎره ﺧﻄﺎي ﻛﺸﻒ ﺷﺪه ﮔﺰارش داد.ﻣﺜﻼ اﮔﺮ Eﻣﻌﺮف ﻳﻚ ﻋﺒﺎرت ﺟﺒﺮي ) در ﺳﺎده
ﺗﺮﻳﻦ ﺷﻜﻞ ﻳﻚ ﻋﻤﻠﻮﻧﺪ ( اﺳﺖ ﻣﻲ ﺗﻮان ﭘﻴﻐﺎم زﻳﺮ را ﺻﺎدر ﻧﻤﻮد :
Missing Operand on line ….
٦٤
ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ
ﻛﺎﻣﭙﺎﻳﻠﺮ ﻫﺎﻳﻲ ﻛﻪ از ﺗﺠﺰﻳﻪ ﻛﻨﻨﺪه ﻫﺎي ﻋﻤﻠﮕﺮ -اوﻟﻮﻳﺖ اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﻨﺪ ،ﻧﻴﺎزي ﺑﻪ ذﺧﻴﺮه
ي ﺟﺪول رواﺑﻂ اوﻟﻮﻳﺖ ﻧﺪارﻧﺪ .در اﻛﺜﺮ ﻣﻮارد ،اﻳﻦ ﺟﺪول ﻣﻲ ﺗﻮاﻧﺪ ﺗﻮﺳﻂ دو ﺗﺎﺑﻊ
اوﻟﻮﻳﺖ fو gﻛﻪ ﻧﻤﺎد ﻫﺎي ﭘﺎﻳﺎﻧﻪ را ﺑﻪ اﻋﺪاد ﺻﺤﻴﺢ ﺗﺒﺪﻳﻞ ﻣﻲ ﻧﻤﺎﻳﻨﺪ ،ﻛﺪ ﮔﺬاري ﺷﻮد.
ﺳﻌﻲ ﺑﺮ اﻳﻦ اﺳﺖ ﻛﻪ ﺗﻮاﺑﻊ fو gﺑﻪ ﮔﻮﻧﻪ اي اﻧﺘﺨﺎب ﺷﻮﻧﺪ ﻛﻪ ﺑﺮاي ﻫﺮ ﻧﻤﺎد aو : b
ﺑﻨﺎ ﺑﺮ اﻳﻦ راﺑﻄﻪ ي اوﻟﻮﻳﺖ ﺑﻴﻦ aو bﻣﻲ ﺗﻮاﻧﺪ ﺗﻮﺳﻂ ﻣﻘﺎﻳﺴﻪ ي ﻋﺪدي ﺑﻴﻦ ) f(aو )g(b
اﻧﺠﺎم ﮔﻴﺮد .ﺑﻪ ﻫﺮ ﺣﺎل ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ ورودي ﻫﺎي ﺧﻄﺎ در ﻣﺎﺗﺮﻳﺲ اوﻟﻮﻳﺖ ،ﻣﻬﻢ
ﻣﻲ ﺑﺎﺷﻨﺪ .زﻳﺮا ﻳﻜﻲ از ﺣﺎﻟﺖ ﻫﺎي ) (2) ، (1و ﻳﺎ ) (3ﺑﺪون ﺗﻮﺟﻪ ﺑﻪ اﻳﻦ ﻛﻪ ) f(aو )g(b
داراي ﭼﻪ ﻣﻘﺎدﻳﺮي ﻫﺴﺘﻨﺪ ،ﺑﺮﻗﺮار ﻣﻲ ﺑﺎﺷﺪ .ﻓﻘﺪان ﺗﻮاﻧﺎﻳﻲ آﺷﻜﺎر ﺳﺎزي ﺧﻄﺎ ،ﻋﻤﻮﻣﺎ ﺑﻪ
اﻧﺪازه اي ﻣﻬﻢ ﻧﻴﺴﺖ ﻛﻪ ﻣﺎﻧﻊ اﺳﺘﻔﺎده از ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ در ﻣﻮﻗﻊ ﻟﺰوم ﺷﻮد .ﻫﻨﻮز ﻫﻢ اﻣﻜﺎن
ﮔﺮﻓﺘﻦ ﺧﻄﺎ در زﻣﺎﻧﻲ ﻛﻪ ﻛﺎﻫﺶ درﺧﻮاﺳﺖ ﻣﻲ ﺷﻮد وﻟﻲ دﺳﺘﮕﻴﺮه ﻳﺎﻓﺖ ﻧﻤﻲ ﮔﺮدد ،
وﺟﻮد دارد.
اﻳﻨﭽﻨﻴﻦ ﻧﻴﺴﺖ ﻛﻪ ﻫﺮ ﺟﺪول رواﺑﻂ اوﻟﻮﻳﺖ ،داراي ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ ﺑﻪ ﻣﻨﻈﻮر ﻛﺪ ﮔﺬاري آن
ﺑﺎﺷﺪ ،اﻣﺎ در ﻋﻤﻞ ،اﻳﻦ ﺗﻮاﺑﻊ ﻣﻌﻤﻮﻻ وﺟﻮد دارﻧﺪ.
٦٥
ﻣﺜﺎل (
ﺟﺪول اوﻟﻮﻳﺖ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ :
+ > < > < < < < > >
- > < > < < < < > >
* > > > > < < < > >
/ > > > > < < < > >
> > > > < < < > >
id > > > > > > >
( < < < < < < = <
) > > > > > > >
$ < < < < < < <
f 2 2 4 4 4 0 6 6 0
g 1 1 3 3 5 5 0 5 0
ﺑﺮاي ﻣﺜﺎل * < id ،و ) . f(*) < g(idﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ ) f(id) > g(idﺑﻴﺎن ﻣﻲ
دارد ﻛﻪ ، id > idاﻣﺎ در ﺣﻘﻴﻘﺖ ﻫﻴﭻ راﺑﻄﻪ ي اوﻟﻮﻳﺖ ﺑﻴﻦ idو idوﺟﻮد ﻧﺪارد .ورودي
ﻫﺎي دﻳﮕﺮ ﺧﻄﺎ در ﺟﺪول اوﻟﻮﻳﺖ ﺑﺎﻻ ﺑﻪ ﻃﻮر ﻣﺸﺎﺑﻪ ﺑﺎ ﻳﻜﻲ از رواﺑﻂ اوﻟﻮﻳﺖ ﺟﺎﻳﮕﺰﻳﻦ ﻣﻲ
٦٦
ﮔﺮدﻧﺪ .روﺷﻲ ﺳﺎده ﺑﻪ ﻣﻨﻈﻮر ﻳﺎﻓﺘﻦ ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ ﺑﺮاي ﻳﻚ ﺟﺪول ،اﮔﺮ ﭼﻨﻴﻦ ﺗﻮاﺑﻌﻲ
وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﻨﺪ ،ﺑﻪ ﺗﺮﺗﻴﺐ زﻳﺮ اﺳﺖ.
٦٧
id + * $
id >. >. >.
+ <. >. <. >.
* <. >. >. >.
$ <. <. <.
در اﻳﻦ ﻣﺎﺗﺮﻳﺲ ﻫﻴﭻ راﺑﻄﻪ = وﺟﻮد ﻧﺪارد ،ﺑﻨﺎﺑﺮاﻳﻦ ﻫﺮ ﻧﻤﺎد،ﺑﺎ ﺧﻮدش در ﻳﻚ ﮔﺮوه اﺳﺖ.
ﺷﻜﻞ زﻳﺮ ﮔﺮاف ﺑﺪﺳﺖ آﻣﺪه ﺑﺎ اﻟﮕﻮرﻳﺘﻢ ﺑﺎﻻ را ﻧﺸﺎن ﻣﻲ دﻫﺪ.
در اﻳﻦ ﮔﺮاف دوره وﺟﻮد ﻧﺪارد،ﺑﻨﺎﺑﺮاﻳﻦ ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ وﺟﻮد دارﻧﺪ .از آﻧﺠﺎﻳﻲ ﻛﻪ f$و
g$ﻟﺒﻪ ي ﺧﺮوﺟﻲ ﻧﺪارﻧﺪ .f($)=g($)=0،ﻃﻮﻻﻧﻲ ﺗﺮﻳﻦ ﻣﺴﻴﺮ از g+داراي ﻃﻮل 1
اﺳﺖ،ﺑﻨﺎﺑﺮاﻳﻦ . g(+)=1ﻳﻚ ﻣﺴﻴﺮ از gidﺑﻪ *fو * gو f+و g+و f$وﺟﻮد دارﻧﺪ،
ﺑﻨﺎﺑﺮاﻳﻦ .g(id)=5ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ ﻧﺘﻴﺠﻪ ﻋﺒﺎرت اﻧﺪ از:
+ * id $
f 2 4 4 0
g 1 3 5 0
gid fid
*g
*f
g+ f+
f$ g$
٦٨
16 – 3روش ﺗﺠﺰﻳﻪ ﺗﻘﺪم ﺳﺎده
اﻳﻦ روش ﺗﺠﺰﻳﻪ ﺑﺴﻴﺎر ﺷﺒﻴﻪ روش ﺗﺠﺰﻳﻪ ﺗﻘﺪم – ﻋﻤﻠﮕﺮ اﺳﺖ و در واﻗﻊ ﺑﻬﺒﻮد ﻳﺎﻓﺘﻪ ﺗﻘﺪم –
ﻋﻤﻠﮕﺮ اﺳﺖ.در اﻳﻦ روش رواﺑﻂ ﺗﻘﺪم ﺑﻴﻦ ﻫﻤﻪ ﻋﻨﺎﺻﺮ ﮔﺮاﻣﺮ ﺗﻌﺮﻳﻒ ﺷﺪه در ﺣﺎﻟﻴﻜﻪ در
ﺗﻘﺪم – ﻋﻤﻠﮕﺮ اﻳﻦ رواﺑﻂ ﻓﻘﻂ ﺑﻴﻦ ﭘﺎﻳﺎﻧﻪ ﻫﺎ ﺗﻌﺮﻳﻒ ﻣﻲ ﺷﻮد.ﺑﺮاي اﺳﺘﻔﺎده از اﻳﻦ روش
ﻣﺤﺪودﻳﺖ ﻫﺎي ﻛﻤﺘﺮي ﻧﺴﺒﺖ ﺑﻪ ﻣﻮرد ﺗﻘﺪم – ﻋﻤﻠﮕﺮ وﺟﻮد دارد ﻛﻪ ﺑﺎﻋﺚ ﻣﻲ ﺷﻮد ﻛﻪ
روش ﺗﻘﺪم ﺳﺎده ﻃﻴﻒ ﺑﻴﺸﺘﺮي از ﮔﺮاﻣﺮﻫﺎ را در ﺑﺮﺑﮕﻴﺮد.ﺑﻪ ﻋﻨﻮان ﻧﻤﻮﻧﻪ در اﻳﻨﺠﺎ وﺟﻮد
ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎي ﻣﺠﺎور در ﺳﻤﺖ راﺳﺖ ﻗﻮاﻋﺪ ﻣﺠﺎز اﺳﺖ ﻟﻴﻜﻦ ﻣﺎﻧﻨﺪ ﺣﺎﻟﺖ ﻗﺒﻞ وﺟﻮد ﻗﻮاﻋﺪ
اﭘﺴﻴﻠﻮن ﻣﺠﺎز ﻧﻴﺴﺖ.از آﻧﺠﺎ ﻛﻪ در روش ﺗﻘﺪم ﺳﺎده ﺑﺮ ﺧﻼف روش ﺗﻘﺪم – ﻋﻤﻠﮕﺮ ﺑﻴﻦ
ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎ ﺗﻤﺎﻳﺰ ﻗﺎﺋﻞ ﻣﻲ ﺷﻮﻳﻢ .در اﻳﻨﺠﺎ ﻳﻚ ﻣﺤﺪودﻳﺖ ﺟﺪﻳﺪ دارﻳﻢ ﻛﻪ ﺳﻤﺖ راﺳﺖ
ﻫﻴﭻ دو ﻗﺎﻋﺪه اي ﻧﺒﺎﻳﺪ ﻳﻜﺴﺎن ﺑﺎﺷﺪ زﻳﺮا در ﻏﻴﺮاﻳﻨﺼﻮرت در ﺑﻌﻀﻲ از ﻗﺪم ﻫﺎ ﺗﺪاﺧﻞ
ﻛﺎﻫﺶ – ﻛﺎﻫﺶ ﭘﻴﺶ ﺧﻮاﻫﺪ آﻣﺪ.اﻟﺒﺘﻪ اﻳﻦ ﻣﺤﺪودﻳﺖ ﭼﻨﺪان ﻣﻬﻤﻲ ﻧﻴﺴﺖ.در ﻫﺮ دو ﻣﻮرد
اﻳﻦ روﺷﻬﺎ ﻳﻚ ﻣﺤﺪودﻳﺖ ﻣﺸﺘﺮك وﺟﻮد دارد ﻛﻪ در ﺧﺎﻧﻪ ﻫﺎي ﺟﺪول ﺗﺠﺰﻳﻪ ﺑﺎﻳﺴﺘﻲ
ﺣﺪاﻛﺜﺮ ﻳﻚ راﺑﻄﻪ ﺗﻘﺪم وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﺪ.
در روش ﺗﻘﺪم ﺳﺎده ﻫﻢ ﺑﺮاي ﻫﺪاﻳﺖ ﻋﻤﻠﻴﺎت از رواﺑﻂ ﺳﻪ ﮔﺎﻧﻪ ﺗﻘﺪم اﺳﺘﻔﺎده ﻣﻲ ﺷﻮد.اﻟﺒﺘﻪ
در روش ﺗﻘﺪم ﺳﺎده اﻳﻦ رواﺑﻂ ﺑﻴﻦ ﻛﻠﻴﻪ ﻋﻼﺋﻢ ﮔﺮاﻣﺮ ) ﭘﺎﻳﺎﻧﻪ و ﻏﻴﺮﭘﺎﻳﺎﻧﻪ و ( $ﺗﻌﺮﻳﻒ ﻣﻲ
ﺷﻮد.ﺟﺪول ﺗﺠﺰﻳﻪ ﺗﻘﺪم ﺳﺎده ﻳﻚ ﺟﺪول ﻣﺮﺑﻊ اﺳﺖ ﻛﻪ ﺑﻪ ﺗﻌﺪاد ﺣﺎﺻﻞ ﺟﻤﻊ ﺗﻌﺪاد ﭘﺎﻳﺎﻧﻪ
ﻫﺎ و ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎي ﮔﺮاﻣﺮ ﺑﻌﻼوه ﻳﻚ ) ﺑﺨﺎﻃﺮ ﻋﻼﻣﺖ ( $ﺳﻄﺮ وﺳﺘﻮن دارد.
ﺑﺮاي ﺗﻌﻴﻴﻦ رواﺑﻂ ﺗﻘﺪم ﺳﺎده از ﺗﻮاﺑﻊ ﺑﺎ ﻧﺎﻣﻬﺎي Headو Tailاﺳﺘﻔﺎده ﻣﻲ ﺷﻮد ﻛﻪ
ﺗﻌﺮﻳﻒ رﺳﻤﻲ آﻧﻬﺎ ﺑﺼﻮرت زﻳﺮ اﺳﺖ ) ﻫﺮ دوي اﻳﻦ ﺗﻮاﺑﻊ ﺑﺮ روي ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻋﻤﻞ
ﻛﺮده و ﺣﺎﺻﻞ آﻧﻬﺎ ﻣﺠﻤﻮﻋﻪ اي از ﻋﻼﺋﻢ ﮔﺮاﻣﺮ اﺳﺖ ( :
+
} Head(U) = { X | U ═› Xα
+
} Tail(U) = { X | U ═› αX
ﺑﺎ اﺳﺘﻔﺎده از دو ﺗﺎﺑﻊ ﻓﻮق رواﺑﻂ ﺗﻘﺪم ﺳﺎده ﺑﺼﻮرت زﻳﺮ ﺗﻌﺮﻳﻒ ﻣﻲ ﺷﻮﻧﺪ :
X=Y iff … Э U → … XY
X<Y iff )Э U → … XA … and Y Є Head(A
X>Y iff )Э U → … AB … and X Є Tail(A
and Y Є Head(B) or Y=B
٦٩
ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ :
)S→(SS
S→c
ﻣﺎﻧﻨﺪ روش ﺗﻘﺪم – ﻋﻤﻠﮕﺮ اﺑﺘﺪا ﻗﺎﻋﺪه اي ﺑﻔﺮم N → $ S $ﺑﻪ ﻗﻮاﻋﺪ ﮔﺮاﻣﺮ اﺿﺎﻓﻪ
ﻛﺮده ﺳﭙﺲ ﻣﻄﺎﺑﻖ رواﻟﻲ ﻛﻪ در آﻧﺠﺎ ذﻛﺮ ﮔﺮدﻳﺪ ﺑﻪ دﻧﺒﺎل ﻗﻮاﻋﺪي ﻣﻲ ﮔﺮدﻳﻢ ﻛﻪ ﺷﺮاﻳﻂ
ﺗﻌﺎرﻳﻒ ﻓﻮق در ﻣﻮرد آﻧﻬﺎ ﺻﺪق ﻧﻤﺎﻳﺪ.ﺣﺎﺻﻞ اﻳﻦ ﻛﺎر در ﻣﻮرد ﻣﺜﺎل ﻓﻮق ﺑﺼﻮرت ﺟﺪول
ﺗﺠﺰﻳﻪ زﻳﺮ ﺧﻮاﻫﺪ ﺑﻮد.ﺑﺮاي ﺗﻔﻜﻴﻚ رواﺑﻂ ﺗﻘﺪم ﺳﺎده از رواﺑﻂ ﺗﻘﺪم – ﻋﻤﻠﮕﺮ ,رواﺑﻂ
ﺗﻘﺪم ﺳﺎده ﺑﺎ اﺳﺘﻔﺎده از ﻋﻼﺋﻢ ﻣﺘﻔﺎوﺗﻲ ﻧﻤﺎﻳﺶ داده ﺧﻮاﻫﺪ ﺷﺪ
.
S $ ( ) c
٧٠
ﺣﺬف دﺳﺘﮕﻴﺮه ( را Topﺑﻨﺎﻣﻴﻢ و ﺳﻤﺖ ﭼﭗ ﻗﺎﻋﺪه اي را ﻛﻪ ﭘﺎرﺳﺮ از آن
ﺟﻬﺖ ﻛﺎﻫﺶ اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ Lhsﺑﻨﺎﻣﻴﻢ ﭘﺎرﺳﺮ راﺑﻄﻪ ﺑﻴﻦ Lhsو Topرا
از ﺟﺪول اﺳﺘﺨﺮاج ﻧﻤﻮده و ﻳﻜﻲ از اﻋﻤﺎل زﻳﺮ را اﻧﺠﺎم ﻣﻲ دﻫﺪ :
• اﮔﺮ راﺑﻄﻪ Topو Lhsﺑﺼﻮرت Top < Lhsﺑﺎﺷﺪ ﭘﺎرﺳﺮ اﺑﺘﺪا
ﻋﻼﻣﺖ < وﺳﭙﺲ Lhsرا وارد اﻧﺒﺎره ﻣﻲ ﻛﻨﺪ.
• اﮔﺮ راﺑﻄﻪ Topو Lhsﺑﺼﻮرت Top = Lhsﺑﺎﺷﺪ ﭘﺎرﺳﺮ ﻓﻘﻂ
Lhsرا وارد اﻧﺒﺎره ﻣﻲ ﻛﻨﺪ.
• اﮔﺮ Topو Lhsراﺑﻄﻪ اي ﻧﺪاﺷﺘﻪ ﺑﺎﺷﻨﺪ ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي رخ داده
اﺳﺖ و ﺑﺎﻳﺴﺘﻲ روﻳﻪ اﺻﻼح ﺧﻄﺎ ﻓﺮاﺧﻮاﻧﺪه ﺷﻮد.
.4در ﺻﻮرﺗﻲ ﻛﻪ ﻋﻨﺼﺮ ﺑﺎﻻي اﻧﺒﺎره Xو ورودي ﺟﺎري bراﺑﻄﻪ اي ﻧﺪاﺷﺘﻪ
ﺑﺎﺷﻨﺪ ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي رخ داده اﺳﺖ و ﺑﺎﻳﺴﺘﻲ روﻳﻪ اﺻﻼح ﺧﻄﺎ ﻓﺮاﺧﻮاﻧﺪه
ﺷﻮد.
.5در ﺻﻮرﺗﻲ ﻛﻪ ﺗﻮﻛﻦ ﺟﺎري $و در داﺧﻞ اﻧﺒﺎره S ) $Sﻋﻼﻣﺖ ﺷﺮوع
ﮔﺮاﻣﺮ اﺳﺖ ( ﺑﺎﻗﻲ ﻣﺎﻧﺪه ﺑﺎﺷﺪ ﭘﺎرﺳﺮ ﭘﺎﻳﺎن ﻣﻮﻓﻘﻴﺖ آﻣﻴﺰ ﺗﺠﺰﻳﻪ را اﻋﻼم ﻣﻲ
ﻛﻨﺪ.
ﺑﺮاي ﺣﻞ اﻳﻦ ﻣﺸﻜﻞ ﻗﻮاﻋﺪ ﻓﻮق را ﺑﺼﻮرت زﻳﺮ ﺗﺒﺪﻳﻞ ﻣﻲ ﻛﻨﻴﻢ ﻛﻪ در آن Wﻳﻚ
ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺟﺪﻳﺪ اﺳﺖ :
…U→U
… V → … XW
W→U
٧١
ﺣﺎل رواﺑﻂ ﺗﻘﺪم ﺑﻴﻦ اﻳﻦ ﻋﻼﺋﻢ ﺑﺼﻮرت زﻳﺮ اﺳﺖ :
X = W , X < U
ﻫﻤﭽﻨﻴﻦ ﻫﺮﮔﺎه در ﻗﻮاﻋﺪ ﮔﺮاﻣﺮ وﺿﻌﻴﺘﻲ ﺑﺼﻮرت زﻳﺮ ﺑﺎﺷﺪ ﻛﻪ در آن ﻗﺎﻋﺪه اول ﻳﻚ
ﻗﺎﻋﺪه راﺳﺖ ﮔﺮد اﺳﺖ ﺑﻴﻦ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ Uو ﻋﻼﻣﺖ Xدو راﺑﻄﻪ ﺗﻘﺪم وﺟﻮد دارد :
U→…U
… V → … UX
ﺑﺮاي رﻓﻊ اﻳﻦ ﻣﺸﻜﻞ ﻗﻮاﻋﺪ را ﺑﺼﻮرت زﻳﺮ ﺗﻐﻴﻴﺮ ﻣﻲ دﻫﻴﻢ :
U→…U
… V → … WX
W→U
در اداﻣﻪ ﺑﻪ ﻋﻨﻮان ﻳﻚ ﻧﻤﻮﻧﻪ از ﺗﺠﺰﻳﻪ ﺑﻪ روش ﺗﻘﺪم ﺳﺎده ﺑﻪ ﺗﺠﺰﻳﻪ ﺟﻤﻠﻪ ))(c(cc
ﺗﻮﺟﻪ ﻛﻨﻴﺪ :
٧٢
روﺷﻬﺎي ﺗﺠﺰﻳﻪ : LR
ﻳﻜﻲ از اﻣﻦ ﺗﺮﻳﻦ روﺷﻬﺎي ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﻛﻪ ﻣﻲ ﺗﻮاﻧﺪ در ﻣﻮرد اﻛﺜﺮ ﮔﺮاﻣﺮﻫﺎي
ﻣﺴﺘﻘﻞ از ﻣﺘﻦ اﻋﻤﺎل ﺷﻮد روش LRاﺳﺖ.ﻣﺰاﻳﺎي روﺷﻬﺎي ﺗﺠﺰﻳﻪ LRﻋﺒﺎرﺗﻨﺪ از :
-1ﺗﻘﺮﻳﺒﺎ ﺗﻤﺎﻣﻲ ﺳﺎﺧﺘﺎرﻫﺎي زﺑﺎن ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ را ﻣﻲ ﺗﻮان ﺗﻮﺳﻂ ﭘﺎرﺳﺮﻫﺎي LR
ﺗﺸﺨﻴﺺ داد.
-2روش ﺗﺠﺰﻳﻪ LRﻛﻠﻲ ﺗﺮﻳﻦ روش ﺗﺠﺰﻳﻪ ﻏﻴﺮ ﺑﺎزﮔﺸﺘﻲ ﺑﻪ ﻃﺮﻳﻘﻪ اﻧﺘﻘﺎل – ﻛﺎﻫﺶ
اﺳﺖ ﻛﻪ ﺗﺎ ﻛﻨﻮن ﺷﻨﺎﺧﺘﻪ ﺷﺪه و ﻣﻲ ﺗﻮان آن را ﺑﻪ ﻛﺎراﻳﻲ ﻫﺮ روش دﻳﮕﺮي ﭘﻴﺎده
ﺳﺎزي ﻛﺮد.
-3ﻣﺠﻤﻮﻋﻪ زﺑﺎﻧﻬﺎﻳﻲ ﻛﻪ ﺗﻮﺳﻂ روش LRﺗﺠﺰﻳﻪ ﻣﻲ ﺷﻮﻧﺪ ﺷﺎﻣﻞ ﻣﺠﻤﻮﻋﻪ زﺑﺎﻧﻬﺎﻳﻲ
اﺳﺖ ﻛﻪ ﺗﻮﺳﻂ ﭘﺎرﺳﺮﻫﺎي ﭘﻴﺸﮕﻮ ﺗﺠﺰﻳﻪ ﻣﻲ ﺷﻮﻧﺪ.
-4ﻳﻚ ﭘﺎرﺳﺮ LRﺧﻄﺎي ﻧﺤﻮي را در ﻛﻤﺘﺮﻳﻦ زﻣﺎن ﺗﻮﺳﻂ ﺑﺮرﺳﻲ ﭼﭗ ﺑﻪ راﺳﺖ
ورودي ﭘﻴﺪا ﻣﻲ ﻛﻨﺪ.
ﻳﻜﻲ از ﻣﻌﺎﻳﺐ روش LRآن اﺳﺖ ﻛﻪ ﺣﺠﻢ ﻛﺎر ﺑﺴﻴﺎر زﻳﺎدي دارد ﻛﻪ در ﻣﻮرد ﭘﻴﺎده
ﺳﺎزي دﺳﺘﻲ آن را ﻣﺸﻜﻞ ﻣﻲ ﻧﻤﺎﻳﺪ وﻟﻲ ﺑﻪ ﻫﺮ ﺣﺎل ﻧﺮم اﻓﺰارﻫﺎي ﺧﻮدﻛﺎري وﺟﻮد
دارﻧﺪ ﻛﻪ ﻣﻲ ﺗﻮاﻧﻨﺪ ﺑﻪ ﭘﺎرﺳﺮﻫﺎي LRﻛﻤﻚ ﻛﻨﻨﺪ.ﻣﺎﻧﻨﺪ ﻧﺮم اﻓﺰار Yacc
٧٣
ﺳﺎﺧﺘﺎر ﭘﺎرﺳﺮﻫﺎي LRﺑﻪ ﻓﺮم زﻳﺮ اﺳﺖ :
Input
a1 ... ai ... an $
Sm
Sm-1
S0
Stack
٧٤
ﺑﻪ ﭘﻴﻜﺮﺑﻨﺪي ) ( S0X1S1X2S2 … Xm-rSm-r AS , aiai+1 … an $دﺳﺖ
ﺧﻮاﻫﻴﻢ ﻳﺎﻓﺖ ﻛﻪ در آن :
] S = goto [ Sm-r , A ﻃﻮل , r = |α| → α
ﺑﻨﺎﺑﺮاﻳﻦ ﺗﻌﺪاد ﺣﺮوف ﺣﺬف ﺷﺪه از Stackﺑﻪ اﻧﺪازه 2rﺧﻮاﻫﺪ ﺑﻮد ﻛﻪ rﺑﻪ ﺗﻌﺪاد
وﺿﻌﻴﺘﻬﺎ ﻳﺎ ﺗﻌﺪاد ﻋﻼﺋﻢ ﮔﺮاﻣﺮ Aاﺳﺖ.
-3اﮔﺮ action [ Sm,ai ] = acceptﺑﺎﺷﺪ در اﻳﻨﺼﻮرت ﻋﻤﻞ ﭘﺎرس ﺑﻪ ﺷﻜﻞ ﻣﻮﻓﻘﻴﺖ
آﻣﻴﺰي اﻧﺠﺎم ﭘﺬﻳﺮﻓﺘﻪ اﺳﺖ.
اﮔﺮ ] action [ Sm,aiﺧﺎﻟﻲ ﺑﺎﺷﺪ ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي رخ داده اﺳﺖ ﻛﻪ در اﻳﻦ ﺣﺎل
روﻳﻪ ﺧﻄﺎﭘﺮداز ﻓﺮاﺧﻮاﻧﻲ ﻣﻲ ﺷﻮد.
ﻗﺮارداد :ﻣﻨﻈﻮر از rnدر ﺟﺪول ﭘﺎرس ,ﺑﻪ ﻣﻨﻈﻮر ﻋﻤﻞ ﻛﺎﻫﺶ ) ( reduceﺗﻮﺳﻂ ﻗﺎﻧﻮن
ﺷﻤﺎره nاﺳﺖ و ﻣﻨﻈﻮر از Snﻳﻌﻨﻲ اﻧﺘﻘﺎل ﻳﺎ Shiftاﺳﺖ ﻛﻪ nﺷﻤﺎره ﻳﻚ وﺿﻌﻴﺖ ﻣﻲ
ﺑﺎﺷﺪ.
1) E→E+T ﻣﺜﺎل – ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ :
)2 E→T
)3 T→T*F
)4 T→F
)5 )F→(E
)6 F → id
State id + * ( ) $ E T F
0 S5 S4 1 2 3
1 S6 acc
2 r2 S7 r2 r2
3 r4 r4 r4 r4
4 S5 S4 8 2 3
5 r6 r6 r6 r6
6 S5 S4 9 3
7 S5 S4 10
8 S6 S11
9 r1 S7 r1 r1
10 r3 r3 r3 r3
11 r5 r5 r5 r5
٧٥
State Input Action
0 id*id+id $ Shift
0id5 *id+id $ Reduce by F → id
0F3 *id+id $ Reduce by T → F
0T2 *id+id $ Shift
0T2*7 id+id $ Shift
0T2*7id5 +id $ Reduce by F → id
0T2*7F10 +id $ Reduce by T → T*F
0T2 +id $ Reduce by E → T
0E1 +id $ Shift
0E1+6 id $ Shift
0E1+6id5 $ Reduce by F → id
0E1+6F3 $ Reduce by T → F
0E1+6T9 $ E → E+T
0E1 $ Accept
٧٦
ﺗﻌﺮﻳﻒ ) : Closure( Iاﮔﺮ Iﻳﻚ ﻣﺠﻤﻮﻋﻪ از آﻳﺘﻢ ﻫﺎي ﻳﻚ ﮔﺮاﻣﺮ ﺑﺎﺷﺪ در
آﻧﺼﻮرت ) Closure( Iرا ﺑﺴﺘﺎر ) ( Iﻣﻲ ﻧﺎﻣﻴﻢ ﻛﻪ آن ﻧﻴﺰ ﻳﻚ ﻣﺠﻤﻮﻋﻪ از آﻳﺘﻢ
ﻫﺎﺳﺖ و ﺑﺮاي ﻣﺤﺎﺳﺒﻪ آن ﺑﺎﻳﺴﺘﻲ دو ﻗﺪم زﻳﺮ را ﭘﻴﻤﺎﻳﺶ ﻛﺮد :
-1ﻫﺮ آﻳﺘﻤﻲ ﻛﻪ در Iوﺟﻮد دارد را ﺑﻪ ) Closure ( Iﻣﻲ اﻓﺰاﺋﻴﻢ .
-2اﮔﺮ ﻗﺎﻋﺪه اي ﺑﻪ ﺷﻜﻞ A → α.Bβدر ﺑﺴﺘﺎر Iداﺷﺘﻪ ﺑﺎﺷﻴﻢ و B → δﻳﻚ
ﻗﺎﻋﺪه از ﮔﺮاﻣﺮ ﺑﺎﺷﺪ آﻧﮕﺎه ﻗﺎﻋﺪه B → .δرا ﺑﻪ ﺑﺴﺘﺎر Iﻣﻲ اﻓﺰاﺋﻴﻢ.
ﺑﺎ ﺑﻮﺟﻮد آﻣﺪن وﺿﻌﻴﺖ ﺟﺪﻳﺪ Iiﻣﻴﺘﻮان Iiرا ﺗﻮﺳﻂ ﻟﺒﻪ ﻫﺎﻳﻲ ﺑﺎ ﺑﺮﭼﺴﺐ Xﺑﻪ Ijﻣﺘﺼﻞ
ﻧﻤﺎﺋﻴﻢ و ﭘﺲ از اﻳﻨﻜﻪ در ﻫﻤﻪ ﻋﺒﺎرات ﻋﻼﻣﺖ ﻧﻘﻄﻪ را ﺑﻪ ﺑﻌﺪ از ﻋﻼﻣﺖ Xﻣﻨﺘﻘﻞ ﻛﺮدﻳﻢ
در وﺿﻌﻴﺖ ﺟﺪﻳﺪي ﻗﺮار ﺧﻮاﻫﻴﻢ ﮔﺮﻓﺖ.ﺑﺪﻳﻦ ﺗﺮﺗﻴﺐ آﻧﻘﺪر اﻳﻦ ﻋﻤﻞ را اداﻣﻪ ﻣﻲ دﻫﻴﻢ ﺗﺎ
دﻳﮕﺮ ﺣﺎﻟﺖ ﺟﺪﻳﺪي ﺑﻪ دﻳﺎﮔﺮام اﺿﺎﻓﻪ ﻧﺸﻮد.
٧٧
1–2 E→E+T|T
3–4 T→T*F|F
5–6 F → ( E ) | id
I6 I9
T
E → E+. T E → E+T.
T → .T*F T → T.*F *
T → .F I7
I1 F → .(E) F
I0 E' → E. + F → .id
E E → E.+T (
I3
E' → .E I4
I7 id I5
E → .E+ T
E → .T I2
T → .T*F T * T → T*.F F I10
T → .F E → T. F → .(E)
F → .(E) T → T.*F F → .id ( T → T*F.
F →.id F
I3 I4
id
id ( I5
T → F.
I5
I8 I11
F → id. )
I4 F → (E.) F → (E).
E
E → E.+T
F → (.E)
E → .E+ T +
E → .T
T → .T*F T I2 I6
T → .F
F → .(E) F
F → .id I3
id I5
٧٨
ﺗﻬﻴﻪ ﺟﺪول ﺗﺠﺰﻳﻪ از روي دﻳﺎﮔﺮام ): SLR(1
ﭘﺲ از رﺳﻢ دﻳﺎﮔﺮام ) SLR(1ﻳﻚ ﮔﺮاﻣﺮ ﺟﺪول ﺗﺠﺰﻳﻪ آﻧﺮا ﺑﺼﻮرت زﻳﺮ ﺗﻜﻤﻴﻞ ﻣﻲ
ﻧﻤﺎﺋﻴﻢ :
.1ﻧﺤﻮه ﭘﺮ ﺷﺪن ﺑﺨﺶ : action
.1-1اﮔﺮ ﺑﺎ ورودي aاز وﺿﻌﻴﺖ Iﺑﻪ وﺿﻌﻴﺖ Jﺑﺮوﻳﻢ در ﺧﺎﻧﻪ ] action[I,aدﺳﺘﻮر
Shift Jرا ﻗﺮار ﻣﻲ دﻫﻴﻢ.
.2-1اﮔﺮ در ﺣﺎﻟﺖ Iآﻳﺘﻤﻲ ﺑﻔﺮم A → α.داﺷﺘﻪ ﺑﺎﺷﻴﻢ در اﻳﻨﺼﻮرت ﺑﻪ ازاي ﻫﺮ a Є
) Follow(Aﺧﺎﻧﻪ ﻳﺎ ﺷﻤﺎره ﻗﺎﻋﺪه ﻣﺬﻛﻮر را در action[I,a] = reduce A → α
در ﺟﺪول ﻗﺮار ﻣﻲ دﻫﻴﻢ.
.3-1اﮔﺮ در ﺣﺎﻟﺖ Iآﻳﺘﻤﻲ ﺑﻔﺮم S' → S.داﺷﺘﻪ ﺑﺎﺷﻴﻢ در آﻧﺼﻮرت در ﺧﺎﻧﻪ
] action[I,$ﻋﻼﻣﺖ Acceptﻗﺮار ﻣﻲ دﻫﻴﻢ.
.4-1در ﺧﺎﻧﻪ ﻫﺎي ﺧﺎﻟﻲ ﺑﺨﺶ actionﻋﻼﻣﺘﻲ را ﺑﻌﻨﻮان ﺧﻄﺎ ﻣﻴﺘﻮان ﻗﺮار داد.
.2ﻧﺤﻮه ﭘﺮ ﺷﺪن ﺑﺨﺶ : goto
ﺑﺮاي ﭘﺮ ﻛﺮدن ﺑﺨﺶ gotoاز ﺟﺪول ﺗﺠﺰﻳﻪ ﺑﺪﻳﻦ ﺗﺮﺗﻴﺐ ﻋﻤﻞ ﻣﻲ ﻛﻨﻴﻢ ﻛﻪ اﮔﺮ ﺑﺎ
ﻏﻴﺮﭘﺎﻳﺎﻧﻪ Aاز ﺣﺎﻟﺖ Iﺑﻪ ﺣﺎﻟﺖ Jﺑﺮوﻳﻢ در ﺧﺎﻧﻪ ] goto[I,Aﻣﻘﺪار Jرا ﻗﺮار ﻣﻲ دﻫﻴﻢ
ﻳﻌﻨﻲ . goto[I,A] = J
ﻣﺜﺎل :
E→E+T|T
T→TF
F→F*|a|b
٧٩
I8
I1 I3 T F 14
I0 + E→
E E' → E. E → E+.T
E+T. b
E → E.+T T → .TF
E' → .E T → T.F 15
E → .E+T F → .F*
E → .T T F → .b
I2 F I4 a
T → .TF 16
E → T. T → TF.
T → T.F F → F.*
F → .F* *
F → .b
F → .a b I5 I7
F → F*.
F → b.
a
I6
F → a.
* + $ a b E T F
0 1 2
1 S3 acc
2 r2 r2 S6 S5 4
3 8
4 S7 r3 r3 r3 r3
5 r6 r6 r6 r6 r6
6 r5 r5 r5 r5 r5
7 r4 r4 r4 r4 r4
8 r1 r1 S6 S5 4
Follow(T) = { $ , + , a , b }
Follow(F) = { * , $ , + , a , b }
Follow(E) = { $ , + }
. اﺳﺖSLR(1) اﻳﻦ ﮔﺮاﻣﺮ
٨٠
:ﺗﻤﺮﻳﻦ
0 S' → .S
1 S→L=R
2 S→R
3 L → *R
4 L → id
5 R→L
* I8
I4
I0 R → L.
L → *.R L
S' → .S R → .L
S → .L=R L → .*R I7
* L → .id R
S → .R
L → .*R L → *R.
L → .id
R → .L id
id
I5
S
L → id. *
L I1
S' → S. id
I6
R I9
S → L=.R
I2 R → .L R S → L=R.
= L → .*R
S → L.=R L → .id
R → L. L
I8
I3
S → R.
٨١
= State * id $ L R S
0 S4 S5 2 3 1
1 acc
2 S6 /r5 r5
3 r2
4 S4 S5 8 7
5 r4 r4
6 S4 S5 8 9
7 r3 r3
8 r5 r5
9 r1
اﻟﮕﻮرﻳﺘﻢ ﻣﺤﺎﺳﺒﻪ ﺗﺎﺑﻊ ﺑﺴﺘﺎر در ﻣﻮرد اﻟﮕﻮرﻳﺘﻢ ﺗﺎﺑﻌﻬﺎي ) LR(1دﻗﻴﻘﺎ ﻣﺸﺎﺑﻪ اﻟﮕﻮرﻳﺘﻢ
ﻣﺤﺎﺳـﺒﻪ ﺑﺴـﺘﺎر آﻳﺘﻢ ) LR(0اﺳﺖ .در ﻣﻮرد آﻳﺘﻢ ] } [ A → α . B β , {aﭼﻨﺎﻧﭽـﻪ
B → Sﻳﻚ ﻣﺠﻤﻮﻋﻪ از ﻗﺎﻋﺪه ﮔﺮاﻣﺮ ﺑﺎﺷﺪ در اﻳﻨﺼﻮرت ﻣﺠﻤﻮﻋﻪ ﭘﻴﺶ ﺑﻴﻨﻲ زﻳﺮ را ﻣﻲ
اﻓﺰاﺋﻴﻢ :
] }[ B → .S , {b )b Є First(βa
٨٢
: CLR ﻃﺮﻳﻘﻪ رﺳﻢ دﻳﺎﮔﺮام
اﺳﺖ ﺑﺎ اﻳﻦ ﺗﻔﺎوت ﻛﻪ ﺑﺮاي ﺷﺮوع ﻗﺎﻋﺪهSLR دﻗﻴﻘﺎ ﻣﺸﺎﺑﻪ دﻳﺎﮔﺮامCLR رﺳﻢ دﻳﺎﮔﺮام
. ﺑﻪ ﻣﺠﻤﻮﻋﻪ ﻗﻮاﻋﺪ ﮔﺮاﻣﺮ اﺿﺎﻓﻪ ﻣﻲ ﻛﻨﻴﻢLR(1) [ را ﺑﻌﻨﻮان آﻳﺘﻢS' → .S,$ ]
: ﻣﺜﺎل
1 S → CC
2,3 C → cC | d
a=$ First(βa) = First($)
B=S {$}
α=ε S → CC
S'=A [ S → .CC,$ ] [C→.cC,c|d]
β=ε A → α.Bβ,a
I1 I5
I0
S
S' → S.,$ S → CC.,$
S' → .S,$
S → .CC,$ C c
C → .cC,c|d
C → .d,c|d I2 I6
C
S →C.C,$ c C → c.C, $
d C → .cC,$ C → .cC, $
I4 C → .d,$ C → .d, $
C → d.,c|d c d
d I7
I8 C → d.,$ C
I3
d C
C → c.C,c|d C → cC.,c|d
C → .cC,c|d
C → .d,c|d I9
C → cC.,$
٨٣
ﻧﺤﻮه ﺗﺸﻜﻴﻞ ﺟﺪول ): CLR(1
روش ﺗﻬﻴﻪ ﺟﺪول ) CLR(1دﻗﻴﻘﺎ ﻣﻄﺎﺑﻖ ﺟﺪول ) SLR(1اﺳﺖ ﺑﺎ اﻳﻦ ﺗﻔﺎوت ﻛﻪ در
اﻳﻨﺠﺎ در وﺿﻌﻴﺖ Iاﮔﺮ آﻳﺘﻤﻲ ﺑﻪ ﻓﺮم ] } [ A → α . , {bداﺷﺘﻪ ﺑﺎﺷﻴﻢ آﻧﻮﻗﺖ در ﺧﺎﻧﻪ
ﻫﺎي ] action[I,bﺷﻤﺎره دﺳﺘﻮر Reduce A → αرا ﻗﺮار ﻣﻲ دﻫﻴﻢ ﻳﻌﻨﻲ در اﻳﻨﺠﺎ
از Followﻫﺎ ﻛﻤﻚ ﻧﻤﻲ ﮔﻴﺮﻳﻢ.
٨٤
رﺳﻢ دﻳﺎﮔﺮام و ﺟﺪول ﺗﺠﺰﻳﻪ : LALR
ﺑﺮاي رﺳﻢ دﻳﺎﮔﺮام LALRاﺑﺘﺪا دﻳﺎﮔﺮام CLRرا ﺗﺮﺳﻴﻢ ﻛﺮده و ﺳﭙﺲ وﺿﻌﻴﺘﻬﺎي اﻳﻦ
دﻳﺎﮔﺮام را ﺑﻪ ﺷﻜﻞ زﻳﺮ ادﻏﺎم ﻣﻲ ﻛﻨﻴﻢ.ﺑﺪﻳﻦ ﺗﺮﺗﻴﺐ ﻛﻪ در وﺿﻌﻴﺘﻬﺎي ﻣﺘﻔﺎوﺗﻲ ﻛﻪ آﻳﺘﻢ
ﻫﺎي ) LR(0آﻧﻬﺎ ﻳﻜﺴﺎن اﺳﺖ و ﻋﻼﻣﺖ ﭘﻴﺶ ﺑﻴﻨﻲ آﻧﻬﺎ ﻣﺘﻔﺎوت اﺳﺖ آﻧﻬﺎ را ﻳﻜﻲ ﻓﺮض
ﻣﻲ ﻧﻤﺎﺋﻴﻢ و ﺑﺨﺶ ﭘﻴﺶ ﺑﻴﻨﻲ آﻧﻬﺎ را ﺑﻪ ﺷﻜﻞ اﺟﺘﻤﺎع ﭘﻴﺶ ﺑﻴﻨﻲ ﻫﺎي دو وﺿﻌﻴﺖ در ﻧﻈﺮ ﻣﻲ
ﮔﻴﺮﻳﻢ.
I1 I5
I0
S
S' → S.,$ S → CC.,$
S' → .S,$
S → .CC,$ C
C → .cC,c|d c
C → .d,c|d I2 I6
C
S →C.C,$ c C → c.C, $
d C → .cC,$ C → .cC, $
C → .d,$ C → .d, $
I47
C → cC.,$
c
٨٥
ﺑﺮاي ﻣﺜﺎل ﻗﺒﻞ ﺗﻐﻴﻴﺮات را اﻋﻤﺎل ﻣﻲ ﻛﻨﻴﻢ.
٨٦
ﻓﺸﺮده ﺳﺎزي ﺟﺪاول ﺗﺠﺰﻳﻪ : LR
ﻳﻚ روش ﻣﻔﻴﺪ ﺑﻪ ﻣﻨﻈﻮر ﻓﺸﺮده ﺳﺎزي ﻓﻴﻠﺪ actionﺗﺸﺨﻴﺺ اﻳﻦ ﻧﻜﺘﻪ اﺳﺖ ﻛﻪ اﻏﻠﺐ
ﺗﻌﺪاد زﻳﺎدي از ﺳﻄﺮ ﻫﺎي ﺟﺪول actionﻣﺸﺎﺑﻪ ﻫﺴﺘﻨﺪ .ﺑﻨﺎ ﺑﺮ اﻳﻦ در زﻣﺎن ﻛﻮﺗﺎه اﻣﻜﺎن
ﺻﺮﻓﻪ ﺟﻮﻳﻲ ﻗﺎﺑﻞ ﺗﻮﺟﻪ در ﺣﺎﻓﻈﻪ وﺟﻮد ﺧﻮاﻫﺪ داﺷﺖ .اﺷﺎره ﮔﺮ ﻫﺎ ﺑﺮاي ﺣﺎﻟﺖ ﻫﺎﻳﻲ ﻛﻪ
actionﻣﺸﺎﺑﻪ دارﻧﺪ ،ﺑﻪ ﻣﻜﺎن ﻣﺸﺎﺑﻪ اﺷﺎره ﻣﻲ ﻛﻨﻨﺪ .ﺑﻪ ﻣﻨﻈﻮر دﺳﺘﺮﺳﻲ ﺑﻪ اﻃﻼﻋﺎت اﻳﻦ
آراﻳﻪ ،ﺑﻪ ﻫﺮ ﭘﺎﻳﺎﻧﻪ ﻳﻚ ﻋﺪد ﺑﻴﻦ ﺻﻔﺮ ﺗﺎ ﻳﻚ واﺣﺪ ﻛﻤﺘﺮ از ﺗﻌﺪاد ﭘﺎﻳﺎﻧﻪ ﻫﺎ ﻧﺴﺒﺖ ﻣﻲ دﻫﻴﻢ،
و اﻳﻦ ﻋﺪد ﺻﺤﻴﺢ را ﺑﻪ ﻋﻨﻮان ﺗﻔﺎوت ﻣﻜﺎن از ﻣﺤﻠﻲ ﻛﻪ ﻣﻘﺪار اﺷﺎره ﮔﺮ ﺑﺮاي ﻫﺮ ﺣﺎﻟﺖ
ﻣﺸﺨﺺ ﻣﻲ ﻛﻨﺪ ،ﻣﻮرد اﺳﺘﻔﺎده ﻗﺮار ﻣﻲ دﻫﻴﻢ .ﺑﺮاي ﻳﻚ ﺣﺎﻟﺖ داده ﺷﺪه ﻋﻤﻞ action
در ﺗﺠﺰﻳﻪ ﺑﺮاي ﭘﺎﻳﺎﻧﻪ iام ،در iﻣﺤﻞ ﺑﻌﺪ از ﻣﺤﻞ اﺷﺎره ﮔﺮ ﺑﺮاي آن ﺣﺎﻟﺖ ،ﺑﻪ دﺳﺖ ﻣﻲ
آﻳﺪ.
ﻋﻼوه ﺑﺮ آن ،ﻛﺎراﻳﻲ ﺑﻴﺸﺘﺮ ﺣﺎﻓﻈﻪ ﻣﻲ ﺗﻮاﻧﺪ در ﻣﻘﺎﺑﻞ ﺗﺠﺰﻳﻪ ﻛﻨﺪ ﺗﺮ ﺑﻪ دﺳﺖ آﻳﺪ .اﻳﻦ ﻋﻤﻞ
ﺑﺎ اﻳﺠﺎد ﻳﻚ ﻟﻴﺴﺖ ﺑﺮاي actionﻫﺎي ﻫﺮ ﺣﺎﻟﺖ ﺑﺪﺳﺖ ﻣﻲ آﻳﺪ .اي ﻟﻴﺴﺖ ﺷﺎﻣﻞ زوج
)ﻧﻤﺎد ﭘﺎﻳﺎﻧﻪ (action ،ﻣﻲ ﺑﺎﺷﺪ .ﻣﺘﺪاول ﺗﺮﻳﻦ actionﺑﺮاي ﻳﻚ ﺣﺎﻟﺖ ﻣﻲ ﺗﻮاﻧﺪ در
اﻧﺘﻬﺎي ﻟﻴﺴﺖ ﻗﺮار ﮔﻴﺮد و ﺑﻪ ﺟﺎي ﻳﻚ ﭘﺎﻳﺎﻧﻪ ﻣﻤﻜﻦ اﺳﺖ ﻛﻠﻤﻪ ي anyرا ﻗﺮار دﻫﻴﻢ .ﺑﻪ
اﻳﻦ ﻣﻌﻨﻲ ﻛﻪ اﮔﺮ ﻧﻤﺎد ورودي ﺟﺎري ﺗﺎ ﻛﻨﻮن در ﻟﻴﺴﺖ ﻳﺎﻓﺖ ﻧﺸﺪه اﺳﺖ ،اﻳﻦ action
ﺑﺎﻳﺪ ﺑﺪون ﺗﻮﺟﻪ ﺑﻪ ورودي اﻧﺠﺎم ﺷﻮد.ﻋﻼوه ﺑﺮ اﻳﻦ وارده ﻫﺎي ﺧﻄﺎ ﻣﻲ ﺗﻮاﻧﻨﺪ ﺑﻪ ﻣﻨﻈﻮر
ﻳﻜﻨﻮاﺧﺘﻲ ﺑﻴﺸﺘﺮ در ﻳﻚ ﺳﻄﺮ ﺑﺎ reduceﺟﺎﻳﮕﺰﻳﻦ ﺷﻮﻧﺪ .اﻳﻦ ﺧﻄﺎ ﻫﺎ ﺑﻌﺪا ﻗﺒﻞ از ﻳﻚ
ﺣﺮﻛﺖ اﻧﺘﻘﺎل آﺷﻜﺎر ﺧﻮاﻫﻨﺪ ﺷﺪ.
ﻣﺜﺎل -ﺟﺪول ﺗﺠﺰﻳﻪ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ:
ﻧﺨﺴﺖ ﺑﻪ اﻳﻦ ﻧﻜﺘﻪ ﺗﻮﺟﻪ ﻧﻤﺎﻳﻴﺪ ﻛﻪ ﺑﺨﺶ actionاز ﺣﺎﻟﺖ ﻫﺎي 0,4,6,7ﺑﺎ ﻳﻜﺪﻳﮕﺮ
ﻣﺸﺎﺑﻪ ﻣﻲ ﺑﺎﺷﻨﺪ .ﻫﻤﻪ ي آن ﻫﺎ را ﻣﻲ ﺗﻮان ﺑﺎ ﻟﻴﺴﺘﻲ ﺑﻪ ﺻﻮرت زﻳﺮ ﻧﻤﺎﻳﺶ داد:
ﻧﻤﺎد ﻋﻤﻞ
id S5
( S4
any Error
٨٧
ﺣﺎﻟﺖ 1ﻧﻴﺰ ﻟﻴﺴﺘﻲ ﻣﺸﺎﺑﻪ دارد:
+ S6
$ Acc
any Error
ﺟﺪول gotoرا ﻧﻴﺰ ﻣﻲ ﺗﻮان ﺑﺎ اﺳﺘﻔﺎده از ﻳﻚ ﻟﻴﺴﺖ ﻛﺪ ﮔﺬاري ﻧﻤﻮد ،اﻣﺎ ﺑﻪ ﻧﻈﺮ ﻣﻲ رﺳﺪ
ﺳﺎﺧﺖ ﻳﻚ ﻟﻴﺴﺖ از زوج ﻫﺎ ﺑﺮاي ﻫﺮ ﻏﻴﺮ ﭘﺎﻳﺎﻧﻪ ﻣﺎﻧﻨﺪ Aﻛﺎراﻳﻲ ﺑﻴﺸﺘﺮي داﺷﺘﻪ ﺑﺎﺷﺪ .ﻫﺮ
زوج در اﻳﻦ ﻟﻴﺴﺖ ﺑﺮاي ﻏﻴﺮ ﭘﺎﻳﺎﻧﻪ Aﺑﻪ ﺷﻜﻞ )ﺣﺎﻟﺖ ﺑﻌﺪي ،ﺣﺎﻟﺖ ﺟﺎري( ﻣﻲ ﺑﺎﺷﺪ ﻛﻪ
ﺣﺎﻟﺖ ﺑﻌﺪي=] ، Aﺣﺎﻟﺖ ﺟﺎري[goto ﻧﺸﺎن دﻫﻨﺪه:
ﻣﻲ ﺑﺎﺷﺪ.
ﺑﻪ ﻣﻨﻈﻮر ﻛﺎﻫﺶ ﺑﻴﺸﺘﺮ ﻓﻀﺎ ﺑﻪ اﻳﻦ ﻧﻜﺘﻪ ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ ﺑﻪ وارده ﻫﺎي ﺧﻄﺎ در ﺑﺨﺶ
gotoاز ﺟﺪول ﻫﺮﮔﺰ ﻣﺮاﺟﻌﻪ ﻧﻤﻲ ﺷﻮد .ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﻫﺮ وارده ﺧﻄﺎ را ﻣﻲ ﺗﻮان ﺑﺎ
ﻣﺘﺪاول ﺗﺮﻳﻦ ﺣﺎﻟﺖ ﻏﻴﺮ ﺧﻄﺎ در ﻫﻤﺎن ﺳﺘﻮن ﺟﺎﻳﮕﺰﻳﻦ ﻧﻤﻮد.
ﻣﺜﺎل -ﺟﺪول ﺗﺠﺰﻳﻪ ﻣﺜﺎل ﻗﺒﻞ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ .ﺳﺘﻮن ﻣﺮ ﺑﻮط ﺑﻪ Fﺑﺮاي ﺣﺎﻟﺖ 7ﻣﻘﺪار
10را دارد ،و ﺗﻤﺎم وارده ﻫﺎي دﻳﮕﺮ ﻣﻘﺪار 3ﻳﺎ ﺧﻄﺎ دارﻧﺪ ،ﻣﻲ ﺗﻮان ﺧﻄﺎ را ﺑﺎ ﻣﻘﺪار 3
ﺟﺎﻳﮕﺰﻳﻦ ﻛﺮد و ﺑﺮاي ﺳﺘﻮن Fﻟﻴﺴﺘﻲ ﺑﻪ ﺻﻮرت زﻳﺮ اﻳﺠﺎد ﻛﺮد:
٨٨
ﺣﺎﻟﺖ ﺑﻌﺪي ﺣﺎﻟﺖ ﺟﺎري
7 10
any 3
ﺑﻪ ﻃﻮر ﻣﺸﺎﺑﻪ ﺑﺮاي ﺳﺘﻮن Tدارﻳﻢ:
6 9
any 2
و ﺑﺮاي ﺳﺘﻮن : E
4 8
any 1
٨٩
E **** E+E | E*E |(E) | id
ﻛﻪ E` Eﺑﻪ آن اﺿﺎﻓﻪ ﺷﺪه ﻧﺸﺎن ﻣﻲ دﻫﺪ.
I6: I1:
)E (E. E` E.
E E.+E E E.+E
E E.*E E E.*E
I7: I2:
E E+E. )E (.E
E E.+E E .E+E
E E.*E E .E*E
)E .(E
I8: E .id
E E*E.
E E.+E I3:
E E.*E E id.
I6: I4:
E (E). E E+.E
E .E+E
E .E*E
)E .(E
E .id
از آن ﺟﺎﻳﻲ ﻛﻪ اﻳﻦ ﮔﺮاﻣﺮ ﻣﺒﻬﻢ اﺳﺖ ،ﻫﻨﮕﺎم ﺗﻮﻟﻴﺪ ﺟﺪول ﺗﺠﺰﻳﻪ LRﺗﻨﺎﻗﺾ ﻫﺎﻳﻲ در
ﺑﺨﺶ actionاﻳﺠﺎد ﺧﻮاﻫﺪ ﺷﺪ .ﺣﺎﻟﺖ ﻫﺎﻳﻲ ﻣﺸﺎﺑﻪ I7و I8اﻳﻦ ﺗﻨﺎﻗﺾ ﻫﺎ را ﺗﻮﻟﻴﺪ ﻣﻲ
٩٠
ﻛﻨﻨﺪ .اﮔﺮ از روش SLRﺑﺮاي اﻳﺠﺎد ﺟﺪول ﺗﺠﺰﻳﻪ اﺳﺘﻔﺎده ﻛﻨﻴﻢ ،ﺗﻨﺎﻗﺾ اﻳﺠﺎد ﺷﺪه
ﺗﻮﺳﻂ I7ﺑﻴﻦ ﻛﺎﻫﺶ ﺑﺎ E E+Eو اﻧﺘﻘﺎل ﺑﺎ +و* ﻗﺎﺑﻞ ﺣﻞ ﻧﻴﺴﺖ ،زﻳﺮا +و* ﻫﺮ دو در
) follow(Eﻗﺮار دارﻧﺪ .ﺑﻨﺎﺑﺮ اﻳﻦ ﻫﺮ دوي اﻳﻦ actionﻫﺎ در ﺻﻮرت در ﻳﺎﻓﺖ ورودي
ﻫﺎي +و* ﻓﺮا ﺧﻮاﻧﻲ ﺧﻮاﻫﻨﺪ ﺷﺪ.
ﺑﺮاي ﻣﺜﺎل ورودي id+id*idرا در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ.در ﻧﻬﺎﻳﺖ ﺑﻪ ﺣﺎﻟﺘﻲ ﻣﻲ رﺳﻴﻢ ﻛﻪ در ﭘﺸﺘﻪ
0E1+4E7و در ورودي *id$دارﻳﻢ .ﻓﺮض ﻛﻨﻴﺪ * ﻧﺴﺒﺖ ﺑﻪ ﺟﻤﻊ اوﻟﻮﻳﺖ داﺷﺘﻪ
ﺑﺎﺷﺪ ،ﺗﺠﺰﻳﻪ ﻛﻨﻨﺪه ﺑﺎﻳﺪ * را ﺑﻪ ﭘﺸﺘﻪ ﻣﻨﺘﻘﻞ ﻛﻨﺪ و ﺑﺮاي ﻛﺎﻫﺶ ﺑﺎ * و idﻫﺎي ﻃﺮﻓﻴﻦ آن
آﻣﺎده ﺷﻮد .اﻳﻦ ﻫﻤﺎن ﭼﻴﺰي اﺳﺖ ﻛﻪ ﺗﺠﺰﻳﻪ ﻛﻨﻨﺪه SLRاﻧﺠﺎم ﻣﻲ دﻫﺪ ،و ﻫﻤﺎن ﭼﻴﺰي
اﺳﺖ ﻛﻪ ﻳﻚ ﺗﺠﺰﻳﻪ ﻛﻨﻨﺪه ﻋﻤﻠﮕﺮ -اوﻟﻮﻳﺖ اﻧﺠﺎم ﻣﻲ دﻫﺪ .اﮔﺮ +اوﻟﻮﻳﺘﻲ ﺑﺎﻻ ﺗﺮ از *
داﺷﺘﻪ ﺑﺎﺷﺪ ،ﺗﺠﺰﻳﻪ ﻛﻨﻨﺪه E+Eرا ﺑﻪ Eﻛﺎﻫﺶ ﺧﻮاﻫﺪ داد .ﺑﻨﺎ ﺑﺮ اﻳﻦ اوﻟﻮﻳﺖ ﻧﺴﺒﻲ ﺟﻤﻊ
ﻛﻪ ﺑﻌﺪ از ﺿﺮب ﻣﻲ ﺑﺎﺷﺪ ،ﺑﻪ ﻃﻮر ﻣﻨﺤﺼﺮ ﺑﻪ ﻓﺮد ﻣﺸﺨﺺ ﻣﻲ ﺳﺎزد ﻛﻪ ﭼﮕﻮﻧﻪ ﺗﻨﺎﻗﺾ
actionدر ﺗﺠﺰﻳﻪ ﺑﻴﻦ ﻛﺎﻫﺶ ﺑﺎ E E+Eو اﻧﺘﻘﺎل ﺑﺎ * در ﺣﺎﻟﺖ 7ﺑﺮﻃﺮف ﺧﻮاﻫﺪ ﺷﺪ.
اﮔﺮ ﺑﻪ ﺟﺎي ورودي ﻗﺒﻠﻲ ﻣﻘﺪار id+id+idﺑﻪ ﻋﻨﻮان ورودي ﺑﺎﺷﺪ ،ﭘﺲ از ﭘﺮدازش ﺑﺨﺶ
id+idﺗﺠﺰﻳﻪ ﻛﻨﻨﺪه ﺑﻪ وﺿﻌﻴﺘﻲ ﺧﻮاﻫﺪ رﺳﻴﺪ ﻛﻪ در آن ﻣﺤﺘﻮاي ﭘﺸﺘﻪ 0E1+4E7
ﺧﻮاﻫﺪ ﺑﻮد .ﺑﺎ ورودي +ﻣﺠﺪدا در ﺣﺎﻟﺖ I7ﺗﻨﺎﻗﺾ اﻧﺘﻘﺎل – ﻛﺎﻫﺶ وﺟﻮد ﺧﻮاﻫﺪ داﺷﺖ.
ﺑﻪ ﻫﺮ ﺣﺎل ﺷﺮﻛﺖ ﭘﺬﻳﺮي ﻋﻤﻠﮕﺮ +ﻣﺸﺨﺺ ﺧﻮاﻫﺪ ﻛﺮد ﻛﻪ اﻳﻦ ﺗﻨﺎﻗﺾ ﭼﮕﻮﻧﻪ ﺑﺎﻳﺪ ﺣﻞ
ﺷﻮد .اﮔﺮ +ﺷﺮﻛﺖ ﭘﺬﻳﺮي ﭼﭗ داﺷﺘﻪ ﺑﺎﺷﺪ actionﺻﺤﻴﺢ ،ﻛﺎﻫﺶ E+Eﺑﻪ Eﺧﻮاﻫﺪ
ﺑﻮد.
ﻓﺮض ﻛﻨﻴﺪ +ﺷﺮﻛﺖ ﭘﺬﻳﺮي ﭼﭗ داﺷﺘﻪ ﺑﺎﺷﺪ ،در ﺣﺎﻟﺖ action ،7ﺑﺎ ورودي +ﺑﺎﻳﺪ
ﻛﺎﻫﺶ ﺑﺎ E E+Eﺑﺎﺷﺪ ،و ﻓﺮض ﻛﻨﻴﺪ ﻛﻪ * اوﻟﻮﻳﺖ ﺑﻴﺸﺘﺮي ﻧﺴﺒﺖ ﺑﻪ ﺟﻤﻊ داﺷﺘﻪ ﺑﺎﺷﺪ،
actionدر ﺣﺎﻟﺖ 7ﺑﺎ ورودي * اﻧﺘﻘﺎل ﺧﻮاﻫﺪ ﺑﻮد.
ﺑﻪ ﻃﻮر ﻣﺸﺎﺑﻪ ﻓﺮض ﻛﻨﻴﺪ * ﺷﺮﻛﺖ ﭘﺬﻳﺮي ﭼﭗ دارد و اوﻟﻮﻳﺖ آن ﺑﺎﻻ ﺗﺮ از +ﻣﻲ ﺑﺎﺷﺪ،
اﻳﻦ ﻧﻜﺘﻪ ﻗﺎﺑﻞ ﺑﺤﺚ اﺳﺖ ﻛﻪ ﺣﺎﻟﺖ 8در ﺑﺨﺶ ، actionﺑﺎﻳﺪ ﻋﻤﻞ ﻛﺎﻫﺶ ﺑﺎ E E*Eرا
ﺑﻪ ازاي ﻫﺮ دو ورودي +و* اﻧﺠﺎم دﻫﺪ و اﻳﻦ ﻛﻪ اﻳﻦ ﺣﺎﻟﺖ ﻓﻘﻂ زﻣﺎﻧﻲ در ﺑﺎﻻي ﭘﺸﺘﻪ ﻗﺮار
ﻣﻲ ﮔﻴﺮد ﻛﻪ E*Eﺳﻪ ﻧﻤﺎد ﺑﺎﻻي ﭘﺸﺘﻪ ﺑﺎﺷﻨﺪ .در راﺑﻄﻪ ﺑﺎ ، +دﻟﻴﻞ آن اﻳﻦ اﺳﺖ ﻛﻪ *
اوﻟﻮﻳﺘﻲ ﺑﺎﻻﺗﺮ از +دارد ،در ﺣﺎﻟﻲ ﻛﻪ در ﻣﻮرد* ﻋﻠﺖ ﺷﺮﻛﺖ ﭘﺬﻳﺮي ﭼﭗ * ﻣﻲ ﺑﺎﺷﺪ.
٩١
ﺑﺎ اداﻣﻪ اﻳﻦ روش ﺟﺪول ﺗﺠﺰﻳﻪ LRﺑﻪ ﺷﻜﻞ زﻳﺮ ﺑﻪ دﺳﺖ ﻣﻲ آﻳﺪ:
٩٢
ﺟﺪول ﻫﺎي ﻧﻤﺎد
ﻛﺎﻣﭙﺎﻳﻠﺮ از ﺟﺪول ﻧﻤﺎد ﺑﺮاي ﻧﮕﻬﺪاري اﻃﻼﻋﺎت و ﻣﺤﺪوده ﺗﻌﺮﻳﻒ ﻧﺎم ﻫﺎي ﺑﺮﻧﺎﻣﻪ اﺳﺘﻔﺎده
ﻣﻲ ﻧﻤﺎﻳﺪ .ﻫﺮ زﻣﺎن ﻧﺎﻣﻲ در ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ ﻳﺎﻓﺖ ﻣﻲ ﺷﻮد ،ﺟﺪول ﻧﻤﺎد ﺟﺴﺘﺠﻮ ﻣﻲ ﮔﺮدد .
ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﻧﺎم ﺟﺪﻳﺪي ﻳﺎ اﻃﻼﻋﺎت ﺟﺪﻳﺪي در راﺑﻄﻪ ﺑﺎ ﻧﺎم ﻫﺎي ﻣﻮﺟﻮد در ﺟﺪول ﻳﺎﻓﺖ
ﻣﻜﺎﻧﺰﻳﻢ ﺟﺪول ﻧﻤﺎد ﺑﺎﻳﺪ اﻳﻦ اﻣﻜﺎن را ﻓﺮاﻫﻢ ﺳﺎزد ﻛﻪ اﺿﺎﻓﻪ ﻧﻤﻮدن وارده ﻫﺎي ﺟﺪﻳﺪ و
ﺟﺴﺘﺠﻮي وارده ﻫﺎي ﻣﻮﺟﻮد ﺑﻪ ﺻﻮرت ﻛﺎرآﻣﺪ اﻧﺠﺎم ﮔﻴﺮد .دو ﻣﻜﺎﻧﻴﺰم ﺟﺪول ﻧﻤﺎد ﻛﻪ
در اﻳﻦ ﺑﺨﺶ اراﺋﻪ ﺷﺪه اﺳﺖ ،ﻟﻴﺴﺖ ﻫﺎي ﺧﻄﻲ و ﺟﺪول در ﻫﻢ ﻣﻲ ﺑﺎﺷﻨﺪ .ﻫﺮ ﻳﻚ از اﻳﻦ
ﻃﺮح ﻫﺎ ﺑﺮ ﻣﺒﻨﺎي زﻣﺎن ﻻزم ﺟﻬﺖ اﻓﺰودن nوارده و اﻧﺠﺎم eدر ﺧﻮاﺳﺖ ﺑﺮرﺳﻲ ﻣﻲ
ﮔﺮدﻧﺪ .ﻟﻴﺴﺖ ﺧﻄﻲ آﺳﺎﻧﺘﺮﻳﻦ آن ﻫﺎ ﺑﺮاي ﺳﺎﺧﺖ ﻣﻲ ﺑﺎﺷﺪ .اﻣﺎ زﻣﺎﻧﻲ ﻛﻪ nو eﺑﻪ ﺳﻤﺖ
ﻣﻘﺎدﻳﺮ ﺑﺰرگ ﻣﻴﻞ ﻛﻨﻨﺪ ﻛﺎراﻳﻲ ﻛﺎﻫﺶ ﻣﻲ ﻳﺎﺑﺪ .ﻃﺮح ﻫﺎي درﻫﻢ ﻛﺎراﻳﻲ ﺑﻬﺘﺮي را ﺑﺎ
ﻓﻌﺎﻟﻴﺖ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ و ﺻﺮف ﻓﻀﺎي ﺑﻴﺸﺘﺮ اراﺋﻪ ﻣﻴﺪﻫﻨﺪ .ﻫﺮ دو روش ﻣﻲ ﺗﻮاﻧﻨﺪ ﺑﻪ ﮔﻮﻧﻪ
اي ﺳﺎزﮔﺎر ﺷﻮﻧﺪ ﻛﻪ ﺑﺘﻮاﻧﻨﺪ اﻛﺜﺮ ﻗﻮاﻧﻴﻦ ﻣﺤﺪوده ﺑﺮﻧﺎﻣﻪ را اداره ﻧﻤﺎﻳﻨﺪ .
ﺑﺮاي ﻛﺎﻣﭙﺎﻳﻠﺮﻣﻔﻴﺪ اﺳﺖ ﻛﻪ ﺑﺘﻮاﻧﺪ در ﺻﻮرت ﻟﺰوم در زﻣﺎن ﻛﺎﻣﭙﺎﻳﻞ اﻧﺪازه ﺟﺪول ﻧﻤﺎد را
ﺑﻪ ﺻﻮرت ﭘﻮﻳﺎ اﻓﺰاﻳﺶ دﻫﺪ .اﮔﺮ در زﻣﺎن ﻧﻮﺷﺘﻦ ﻛﺎﻣﭙﺎﻳﻠﺮ ،اﻧﺪازه ﺟﺪول ﻧﻤﺎد ﺛﺎﺑﺖ ﺑﺎﺷﺪ ،
اﻧﺪازه آن ﺑﺎﻳﺪ ﺑﻪ اﻧﺪازه ﻛﺎﻓﻲ ﺑﺰرگ اﻧﺘﺨﺎب ﺷﻮد ﺗﺎ ﺑﺘﻮاﻧﺪ ﻫﺮ ﺑﺮﻧﺎﻣﻪ ﻣﺒﺪا ﻣﻤﻜﻦ را اداره
ﻧﻤﺎﻳﺪ .اﻳﻦ ﭼﻨﻴﻦ اﻧﺪازه ﺛﺎﺑﺘﻲ ﺑﺮاي اﻛﺜﺮ ﺑﺮﻧﺎﻣﻪ ﻫﺎ ﺑﺴﻴﺎر ﺑﺰرگ و ﺑﺮاي ﺑﻌﻀﻲ ﻧﺎﻣﻨﺎﺳﺐ اﺳﺖ.
٩٣
وارده ﻫﺎي ﺟﺪول ﻧﻤﺎد
ﻫﺮ وارده در ﺟﺪول ﻧﻤﺎد ،ﺑﺮاي اﻋﻼن ﻧﺎم اﺳﺖ .ﻗﺎﻟﺐ ﻳﺎ ﺷﻜﻞ وارده ﻫﺎ ﻟﺰوﻣﻲ ﻧﺪارد ﻛﻪ
ﻳﻜﻨﻮاﺧﺖ ﺑﺎﺷﺪ ،زﻳﺮا اﻃﻼﻋﺎت ذﺧﻴﺮه ﺷﺪه ﺑﺮاي ﻧﺎم ،ﺑﻪ اﺳﺘﻔﺎده ازآن ﻧﺎم ﺑﺴﺘﮕﻲ دارد .ﻫﺮ
وارده ﻣﻲ ﺗﻮاﻧﺪ ﺑﻪ ﻋﻨﻮان رﻛﻮردي ﻛﻪ ﺷﺎﻣﻞ دﻧﺒﺎﻟﻪ اي از ﻛﻠﻤﺎت ﻣﺘﻮاﻟﻲ ﺣﺎﻓﻈﻪ اﺳﺖ ،ﭘﻴﺎده
ﺳﺎزي ﺷﻮد .ﺑﻪ ﻣﻨﻈﻮر ﺣﻔﻆ ﻳﻜﻨﻮاﺧﺘﻲ رﻛﻮردﻫﺎي ﺟﺪول ﻧﻤﺎد ،ﺑﻌﻀﻲ از اﻃﻼﻋﺎت ﻣﺮﺑﻮط
ﺑﻪ ﻧﺎم ﻣﻨﺎﺳﺐ ﺗﺮ اﺳﺖ ﻛﻪ درﺧﺎرج ازاﻳﻦ وارده ﺟﺪول ﻧﮕﻬﺪاري ﺷﻮﻧﺪ و ﺗﻨﻬﺎ ﻳﻚ اﺷﺎره ﮔﺮ
ﺑﻪ اﻃﻼﻋﺎت ذﺧﻴﺮه ﺷﺪه در اﻳﻦ رﻛﻮرد ،ﻗﺮار داده ﺷﻮد .
اﻃﻼﻋﺎت در زﻣﺎن ﻫﺎي ﻣﺘﻔﺎوﺗﻲ ﺑﻪ ﺟﺪول ﻧﻤﺎد وارد ﻣﻲ ﺷﻮﻧﺪ .ﻛﻠﻤﺎت ﻛﻠﻴﺪي در اﺑﺘﺪا ﺑﻪ
ﺟﺪول وارد ﻣﻲ ﺷﻮﻧﺪ .ﺗﺤﻠﻴﻞ ﮔﺮ ﻟﻐﻮي ،دﻧﺒﺎﻟﻪ اي از ﺣﺮوف و ارﻗﺎم را درﺟﺪول ﻧﻤﺎد
ﺟﺴﺘﺠﻮ ﻣﻲ ﻧﻤﺎﻳﺪ ﺗﺎ ﻣﺸﺨﺺ ﺷﻮد ﻛﻪ ﻳﻚ ﻛﻠﻤﻪ ﻛﻠﻴﺪي و ﻳﺎ ﻧﺎم ﺗﺸﺨﻴﺺ داده ﺷﺪه اﺳﺖ .
دراﻳﻦ روش ،ﻛﻠﻤﺎت ﻛﻠﻴﺪي ﻗﺒﻞ از اﻳﻨﻜﻪ ﺗﺤﻠﻴﻞ ﮔﺮ ﻟﻐﻮي ﻛﺎر ﺧﻮد را آﻏﺎز ﻧﻤﺎﻳﺪ ،ﺑﺎﻳﺪ
در ﺟﺪول وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﻨﺪ .در ﻣﻘﺎﺑﻞ ،اﮔﺮ ﺗﺤﻠﻴﻞ ﮔﺮ ﻟﻐﻮي در ﺿﻤﻦ ﻛﺎر ﻛﻠﻤﺎت
ﻛﻠﻴﺪي رزرو ﺷﺪه را ﺗﺸﺨﻴﺺ ﻣﻲ دﻫﺪ ،ﻧﻴﺎزي ﺑﻪ ﺣﻀﻮرآﻧﻬﺎ در ﺟﺪول ﻧﻤﺎد ﻧﻤﻲ ﺑﺎﺷﺪ .
اﮔﺮ زﺑﺎن ﻛﻠﻤﺎت ﻛﻠﻴﺪي را رزرو ﻧﻜﻨﺪ ،ﻻزم اﺳﺖ ﻛﻪ ﻛﻠﻤﺎت ﻛﻠﻴﺪي ﺑﻪ ﺟﺪول وارد ﺷﻮﻧﺪ
ﺑﺎ اﻳﻦ ﻫﺸﺪار ﻛﻪ اﻣﻜﺎن اﺳﺘﻔﺎده آﻧﻬﺎ ﺑﻪ ﻋﻨﻮان ﻛﻠﻤﻪ ﻛﻠﻴﺪي وﺟﻮد دارد .
ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﻧﻘﺶ ﻳﻚ ﻧﺎم روﺷﻦ ﺷﻮد ،وارده اي از ﺟﺪول ﻧﻤﺎد ﻣﻲ ﺗﻮاﻧﺪ ﺑﻪ آن اﺧﺘﺼﺎص
داده ﺷﻮد و ﺑﻪ ﻣﺤﺾ اﻳﻨﻜﻪ اﻃﻼﻋﺎت ﻣﺮﺑﻮط ﺑﻪ آن در دﺳﺘﺮس ﻗﺮار ﮔﺮﻓﺖ ﻣﻘﺎدﻳﺮ ﺻﻔﺖ
٩٤
آن وارده در ﺟﺪول ﺗﻜﻤﻴﻞ ﻣﻲ ﮔﺮدد .در ﺑﺮﺧﻲ از ﻣﻮارد ،ﺑﻪ ﻣﺤﺾ اﻳﻨﻜﻪ ﺗﺤﻠﻴﻞ ﮔﺮ ﻟﻐﻮي
ﻧﺎم را در ورودي دﻳﺪ وارده اي ﺑﺮاي آن در ﺟﺪول ﻧﻤﺎد اﺧﺘﺼﺎص ﻣﻲ دﻫﺪ اﻏﻠﺐ اوﻗﺎت ،
ﻳﻚ ﻧﺎم ﻣﻲ ﺗﻮاﻧﺪ ﻣﺸﺨﺺ ﻛﻨﻨﺪه ﭼﻨﺪﻳﻦ ﺷﻲء ﺑﺎﺷﺪ ﺣﺘﻲ در ﻳﻚ ﺑﻠﻮك ﻳﺎروﻳﻪ .ﺑﺮاي
) -1اﻟﻒ(
;Int x
}; Struct x { float y , z
xرا ﻫﻢ ﺑﻪ ﻋﻨﻮان ﻣﺘﻐﻴﺮ وﻫﻢ ﺑﻪ ﻋﻨﻮان ﻳﻚ ﺳﺎﺧﺘﺎر ﺑﺎ دو ﻓﻴﻠﺪ ﻣﻌﺮﻓﻲ ﻣﻲ ﻧﻤﺎﻳﺪ .درﭼﻨﻴﻦ
ﻣﻮاردي ،ﺗﺤﻠﻴﻞ ﮔﺮ ﻟﻐﻮي ﻣﻲ ﺗﻮاﻧﺪ ﺑﺠﺎي اﺷﺎره ﮔﺮي ﺑﻪ وارده ﺟﺪول ﻧﻤﺎد ) ﻳﺎ ﻳﻚ اﺷﺎره
ﮔﺮ ﺑﻪ ﻟﻐﺖ ﺗﺸﻜﻴﻞ دﻫﻨﺪه آن ﻧﺎم ( ﻓﻘﻂ ﻧﺎم را ﺑﻪ ﺗﺠﺰﻳﻪ ﻛﻨﻨﺪه ﺑﺮﮔﺮداﻧﺪ .رﻛﻮرد ﺟﺪول
ﻧﻤﺎد ،ﻫﻨﮕﺎﻣﻲ ﻛﻪ اﻳﺠﺎد ﻣﻲ ﺷﻮد ﻛﻪ ﻧﻘﺶ ﻧﺤﻮي اﻳﻦ ﻧﺎم روﺷﻦ ﮔﺮدد .ﺑﺮاي اﻋﻼن ﻫﺎي
) -1اﻟﻒ ( دو وارده در ﺟﺪول ﻧﻤﺎد ﺑﺮاي xاﻳﺠﺎد ﺧﻮاﻫﺪ ﺷﺪ .ﻳﻜﻲ ﺑﺎ xﺑﻪ ﻋﻨﻮان ﻳﻚ ﻋﺪد
ﺻﻔﺎت در ﭘﺎﺳﺦ ﺑﻪ اﻋﻼن ﻫﺎ ﻛﻪ ﻣﻤﻜﻦ اﺳﺖ ﺿﻤﻨﻲ ﺑﺎﺷﻨﺪ ،ﺑﻪ ﺟﺪول وارد ﻣﻲ ﺷﻮﻧﺪ .
ﺑﺮﭼﺴﺐ ﻫﺎ ،اﻏﻠﺐ ﺷﻨﺎﺳﻪ ﻫﺎﻳﻲ ﻫﺴﺘﻨﺪ ﻛﻪ ﺑﻪ دﻧﺒﺎل آﻧﻬﺎ دو ﻧﻘﻄﻪ ) (:ﻗﺮار ﻣﻲ ﮔﻴﺮد ،ﺑﻨﺎﺑﺮاﻳﻦ
ﭘﺲ از ﺗﺸﺨﻴﺺ ﭼﻨﻴﻦ ﺷﻨﺎﺳﻪ اي ﺑﺎﻳﺪ اﻃﻼع ﻣﺮﺑﻮط ﺑﻪ ﺑﺮ ﭼﺴﺐ ﺑﻮدن آن را در وارده
ﻣﺮﺑﻮط درﺟﺪول ﻧﻤﺎد وارد ﻛﺮد .ﺑﻄﻮر ﻣﺸﺎﺑﻪ ،ﺳﺎﺧﺘﺎر ﻧﺤﻮي اﻋﻼن روﻳﻪ ﻫﺎ ﻣﺸﺨﺺ ﻣﻲ
٩٥
ﻛﺎراﻛﺘﺮﻫﺎي ﻣﻮﺟﻮد در ﻳﻚ ﻧﺎم
ﺑﻴﻦ ﻧﺸﺎﻧﻪ idﺑﺮاي ﻳﻚ ﺷﻨﺎﺳﻪ ﻳﺎ ﻧﺎم ،ﻟﻐﺖ ﺷﺎﻣﻞ رﺷﺘﻪ ﻛﺎراﻛﺘﺮﻫﺎي ﺗﺸﻜﻴﻞ دﻫﻨﺪه ﻧﺎم و
ﺻﻔﺎت اﻳﻦ ﻧﺎم ﺗﻔﺎوت وﺟﻮد دارد .ﺑﻄﻮر ﮔﺴﺘﺮده اي ﻣﻤﻜﻦ اﺳﺖ ﺑﺎ رﺷﺘﻪ ﻫﺎﻳﻲ از
ﻛﺎراﻛﺘﺮﻫﺎ ﻛﺎر ﻧﺸﻮد ،ﺑﻨﺎﺑﺮاﻳﻦ ﻛﺎﻣﭙﺎﻳﻠﺮ ﻧﻤﺎﻳﺸﻲ ﺑﺎ ﻃﻮل ﺛﺎﺑﺖ از آن ﻧﺎم را ﺑﺠﺎي ﻟﻐﺖ آن
اﺳﺘﻔﺎده ﻣﻲ ﻧﻤﺎﻳﺪ .اﻳﻦ ﻟﻐﺖ زﻣﺎﻧﻲ ﻣﻮرد ﻧﻴﺎزاﺳﺖ ﻛﻪ وارده ﺟﺪول ﻧﻤﺎد ﺑﺮاي اوﻟﻴﻦ ﺑﺎر وارد
ﻣﻲ ﺷﻮد .ﻫﻤﭽﻨﻴﻦ ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﺑﺮاي ﻟﻐﺖ ﻳﺎﻓﺖ ﺷﺪه در ورودي ﺑﻪ ﺟﺪول ﻣﺮاﺟﻌﻪ ﻣﻲ ﺷﻮد
ﺗﺎ ﻣﺸﺨﺺ ﺷﻮد آﻳﺎ اﻳﻦ ﻧﺎم ﻗﺒﻼ وﺟﻮد داﺷﺘﻪ اﺳﺖ ﻳﺎ ﺧﻴﺮ .ﻧﻤﺎﻳﺸﻲ ﻣﺮﺳﻮم از ﻧﺎم ،اﺷﺎره
ﮔﺮي اﺳﺖ ﺑﻪ وارد ه ﺟﺪول ﻧﻤﺎد ﻣﺮﺑﻮط ﺑﻪ آن .اﮔﺮ ﺣﺪ ﺑﺎﻻي ﻣﺘﻮﺳﻄﻲ ﺑﺮاي ﻃﻮل ﻧﺎم
وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﺪ ،ﻛﺎراﻛﺘﺮﻫﺎي ﻣﻮﺟﻮد در ﻧﺎم ﻣﻲ ﺗﻮاﻧﻨﺪ در وارده ﺟﺪول ﻧﻤﺎد ﻣﺸﺎﺑﻪ زﻳﺮ
ﺻﻔﺎت
ﻧﺎم
a
i
٩٦
اﮔﺮ ﻣﺤﺪودﻳﺘﻲ ﺑﺮاي ﻃﻮل ﻧﺎم وﺟﻮد ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ ﻳﺎ اﮔﺮ اﻳﻦ ﺣﺪ ﺑﻨﺪرت ﻗﺎﺑﻞ ﺣﺼﻮل ﺑﺎﺷﺪ
ﺑﺠﺎي ﺗﺨﺼﻴﺺ ﺣﺪاﻛﺜﺮ ﻓﻀﺎي ﻣﻤﻜﻦ ﺑﺮاي ﻧﮕﻬﺪاري ﻳﻚ ﻟﻐﺖ در ﻫﺮ وارده ﺟﺪول ﻧﻤﺎد ،
اﮔﺮ ﻓﻘﻂ ﻓﻀﺎ ﺑﺮاي اﺷﺎره ﮔﺮي در وارد ه ﺟﺪول ﻧﻤﺎد اﺧﺘﺼﺎص داده ﺷﻮد ،ﻣﻲ ﺗﻮاﻧﺪ از
ﻓﻀﺎ ﺑﻄﻮر ﻛﺎرآﻣﺪي اﺳﺘﻔﺎده ﻧﻤﺎﻳﺪ .در رﻛﻮد ﻳﻚ ﻧﺎم ،اﺷﺎره ﮔﺮي ﺑﻪ آراﻳﻪ اي از
ﻛﺎراﻛﺘﺮﻫﺎ )ﺟﺪول رﺷﺘﻪ ( ﻗﺮار داده ﻣﻲ ﺷﻮد ﻛﻪ ﻣﻮﻗﻌﻴﺖ اوﻟﻴﻦ ﻛﺎراﻛﺘﺮ آن ﻟﻐﺖ را
ﻣﺸﺨﺺ ﻣﻲ ﻧﻤﺎﻳﺪ .ﻃﺮح ﻏﻴﺮﻣﺴﺘﻘﻴﻢ اﺟﺎزه ﻣﻲ دﻫﺪ اﻧﺪازه ﻓﻴﻠﺪ ﻧﺎم وارده ﺟﺪول ﻧﻤﺎد ﺛﺎﺑﺖ
٩٧
ﺑﺮاي ﻫﺮ ﻧﺎم ،ﻟﻐﺖ ﻛﺎﻣﻞ ﺗﺸﻜﻴﻞ دﻫﻨﺪه آن ﺑﺎﻳﺪ ذﺧﻴﺮه ﺷﻮد ﺗﺎ اﻃﻤﻴﻨﺎن ﺣﺎﺻﻞ ﮔﺮدد ﻛﻪ
ﺗﻤﺎم ﻛﺎرﺑﺮدﻫﺎي آن ﻧﺎم ﺑﻪ ﻳﻚ رﻛﻮرد از ﺟﺪول ﻧﻤﺎد ﻣﺮﺗﺒﻂ ﻣﻲ ﺷﻮﻧﺪ .ﺑﻪ ﻫﺮ ﺣﺎل
رﺧﺪادﻫﺎي ﻟﻐﺖ ﻣﺸﺎﺑﻪ ﻛﻪ در ﻣﺤﺪوده ﻫﺎي اﻋﻼن ﻫﺎي ﻣﺘﻔﺎوت ﻗﺮار دارﻧﺪ ﺑﺎﻳﺪ ﻗﺎﺑﻞ
اﻃﻼﻋﺎت ﻣﺮﺑﻮط ﺑﻪ ﻣﻜﺎن ﻫﺎي ﺣﺎﻓﻈﻪ ﻛﻪ در زﻣﺎن اﺟﺮا ﺑﻪ ﻧﺎم ﻫﺎ اﺧﺘﺼﺎص ﺧﻮاﻫﺪ ﻳﺎﻓﺖ ،
در ﺟﺪول ﻧﻤﺎد ﻧﮕﻬﺪاري ﻣﻲ ﺷﻮد .اﺑﺘﺪا ﻧﺎم ﻫﺎﻳﻲ را ﺑﺎ ﺣﺎﻓﻈﻪ اﻳﺴﺘﺎ در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ .اﮔﺮ ﻛﺪ
ﻫﺪف زﺑﺎن اﺳﻤﺒﻠﻲ ﺑﺎﺷﺪ ،اﺳﻤﺒﻠﺮ اﻳﻦ اﺟﺎزه را ﺧﻮاﻫﺪ داﺷﺖ ﻛﻪ در ﻣﻮرد ﻣﻜﺎن ﻫﺎي
ﺣﺎﻓﻈﻪ ﺑﺮاي ﻧﺎم ﻫﺎي ﮔﻮﻧﺎﮔﻮن ،ﻣﺮ اﻗﺒﺖ ﻻزم را اﻧﺠﺎم دﻫﺪ .ﺗﻤﺎم آن ﭼﻴﺰي ﻛﻪ ﺑﺎﻳﺪ ﭘﺲ
از ﺗﻮﻟﻴﺪ ﻛﺪ اﺳﻤﺒﻠﻲ ﺑﺮاي ﺑﺮﻧﺎﻣﻪ اﻧﺠﺎم ﮔﻴﺮد ،ﭘﻮﻳﺶ ﺟﺪول ﻧﻤﺎد و ﺗﻮﻟﻴﺪ ﺗﻌﺎرﻳﻒ داده ﻫﺎي
اﮔﺮ ﻛﺪ ﻣﺎﺷﻴﻦ را ﻛﺎﻣﭙﺎﻳﻠﺮ ﺑﺎﻳﺪ ﺗﻮﻟﻴﺪ ﻛﻨﺪ ﻣﻮﻗﻌﻴﺖ ﻫﺮ داده ﻣﻘﺼﻮد ﻧﺴﺒﺖ ﺑﻪ ﻳﻚ ﻣﺒﺪا ﺛﺎﺑﺖ
ﻣﺎﻧﻨﺪ اﺑﺘﺪاي ﻳﻚ رﻛﻮرد ﻓﻌﺎﻟﻴﺖ ،ﺑﺎﻳﺪ ﻣﺸﺨﺺ ﺷﻮد .ﺗﻮﺿﻴﺤﺎت ﻣﺸﺎﺑﻬﻲ در ﻣﻮرد ﻳﻚ
ﺑﻠﻮك ازداده ﻫﺎ ﻛﻪ ﺑﻪ ﻋﻨﻮان ﻳﻚ ﭘﻴﻤﺎﻧﻪ ﻣﺠﺰا از ﺑﺮﻧﺎﻣﻪ ﺑﺎر ﻣﻲ ﺷﻮد ،ﻣﻲ ﺗﻮاﻧﺪ ﺑﻜﺎر رود.
ﺑﺮاي ﻣﺜﺎل ﺑﻠﻮك ﻫﺎي COMMONدر ﻓﺮﺗﻦ ﺑﻪ ﺻﻮرت ﻣﺠﺰا ﺑﺎر ﻣﻲ ﺷﻮﻧﺪ و ﻣﻮﻗﻌﻴﺖ ﻧﺎم
ﻫﺎ ﻧﺴﺒﺖ ﺑﻪ اﺑﺘﺪاي ﺑﻠﻮك COMMONدر ﻓﺮﺗﺮن ﺑﻪ ﺻﻮرت ﻣﺠﺰا ﺑﺎر ﻣﻲ ﺷﻮﻧﺪ و ﻣﻮﻗﻌﻴﺖ
ﻧﺎم ﻫﺎ ﻧﺴﺒﺖ ﺑﻪ اﺑﺘﺪاي ﺑﻠﻮك COMMONﻛﻪ درآن ﻗﺮار دارﻧﺪ ،ﺑﺎﻳﺪ ﻣﺸﺨﺺ ﺷﻮد .
٩٨
در راﺑﻄﻪ ﺑﺎ ﻧﺎم ﻫﺎﻳﻲ ﻛﻪ ﺣﺎﻓﻈﻪ آﻧﻬﺎ در ﭘﺸﺘﻪ ﻳﺎ ﻛﭙﻪ اﺧﺘﺼﺎص ﻳﺎﻓﺘﻪ ،ﺑﻪ ﻫﻴﭻ وﺟﻪ ﻛﺎﻣﭙﺎﻳﻠﺮ
ﺳﺎده ﺗﺮﻳﻦ ﺳﺎﺧﺘﻤﺎن داده ﺑﺮاي ﺳﺎﺧﺖ ﺟﺪول ﻧﻤﺎد ،ﻟﻴﺴﺖ ﺧﻄﻲ از رﻛﻮرد ﻫﺎ ﻣﻲ ﺑﺎﺷﺪ ،
info1
id2
info2
…
idn
infon
available
از ﻳﻚ آراﻳﻪ ﻳﺎ ﭼﻨﺪﻳﻦ آراﻳﻪ ﻣﻌﺎدل ،ﺑﺮاي ذﺧﻴﺮه ﻧﺎم ﻫﺎ و اﻃﻼﻋﺎت ﻣﺮﺗﺒﻂ ﺑﺎ آﻧﻬﺎ اﺳﺘﻔﺎده
ﻣﻲ ﺷﻮد .ﻧﺎم ﻫﺎي ﺟﺪﻳﺪ ﺑﻪ ﻫﻤﺎن ﺗﺮ ﺗﻴﻲ ﻛﻪ ﻳﺎﻓﺖ ﻣﻲ ﺷﻮﻧﺪ ،ﺑﻪ ﻟﻴﺴﺖ اﺿﺎﻓﻪ ﻣﻲ ﮔﺮدﻧﺪ
ﻣﻮﻗﻌﻴﺖ اﻧﺘﻬﺎي آراﻳﻪ ﺑﺎ اﺷﺎره ﮔﺮ availableﻋﻼﻣﺖ ﮔﺬاري ﻣﻲ ﺷﻮد ،ﻛﻪ ﺑﻪ ﻣﺤﻠﻲ اﺷﺎره
ﻣﻲ ﻛﻨﺪ ﻛﻪ وارده ﺑﻌﺪي ﺑﺎﻳﺪ در ﺟﺪول ﻧﻤﺎد ﻗﺮار ﮔﻴﺮد .ﺟﺴﺘﺠﻮ ﺑﺮاي ﻧﺎم از اﻧﺘﻬﺎي آراﻳﻪ
ﺗﺎ اﺑﺘﺪا ﺑﻪ ﺻﻮرت ﻣﻌﻜﻮس اﻧﺠﺎم ﻣﻲ ﮔﻴﺮد .ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﻧﺎم ﻣﻮرد ﻧﻈﺮ ﻳﺎﻓﺖ ﺷﻮد ،اﻃﻼﻋﺎت
٩٩
ﻣﺮﺗﺒﻂ ﺑﺎ آن ﻣﻲ ﺗﻮاﻧﺪ در ﻛﻠﻤﺎت ﺑﻌﺪ ازآن ﻳﺎﻓﺖ ﺷﻮد .اﮔﺮ ﺑﺪون ﻳﺎﻓﺘﻦ ﻧﺎم ﺑﻪ اﺑﺘﺎي آراﻳﻪ
ﺑﺮﺳﺪ ،ﺧﻄﺎﻳﻲ رخ ﻣﻲ دﻫﺪ ﻣﺒﻨﻲ ﺑﺮاﻳﻨﻜﻪ ﻧﺎم ﻣﻮرد ﻧﻈﺮ در ﺟﺪول وﺟﻮد ﻧﺪارد .
ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ اﻳﺠﺎد ﻳﻚ وارده ﺑﺮاي ﻧﺎم و ﻳﺎﻓﺘﻦ ﻧﺎم در ﺟﺪول ﻧﻤﺎد ،ﻋﻤﻠﻴﺎت
ﻣﺴﺘﻘﻠﻲ ﻫﺴﺘﻨﺪ ،ﻳﻌﻨﻲ ﻣﻲ ﺗﻮان ﻳﻜﻲ ازآﻧﻬﺎ را ﺑﺪون دﻳﮕﺮي اﻧﺠﺎم داد .در زﺑﺎﻧﻲ ﺑﺎ ﺳﺎﺧﺘﺎر
ﺑﻠﻮﻛﻲ ،رﺧﺪادي از ﻧﺎم در ﻣﺤﺪوده ﻧﺰدﻳﻜﺘﺮﻳﻦ اﻋﻼن ﻣﺘﺪاﺧﻞ آن ﻧﺎم اﺳﺖ .اﻳﻦ ﻗﺎﻧﻮن
ﻣﺤﺪوده را ﻣﻲ ﺗﻮا ن ﺑﺎ اﺳﺘﻔﺎده از ﺳﺎﺧﺘﻤﺎن داده ﻟﻴﺴﺖ و ﺑﺎ اﻳﺠﺎد ﻳﻚ وارده ﺟﺪﻳﺪي ﺑﺮاي
ﻧﺎم ،ﻫﺮ زﻣﺎﻧﻲ ﻛﻪ اﻋﻼن ﻣﻲ ﺷﻮد ،ﭘﻴﺎده ﺳﺎزي ﻛﺮد .وارده ﺟﺪﻳﺪ در ﻛﻠﻤﺎت ﺑﻼﻓﺎﺻﻠﻪ ﺑﻌﺪ
از اﺷﺎره ﮔﺮد availableﻗﺮار داده ﻣﻲ ﺷﻮد و اﻳﻦ اﺷﺎره ﮔﺮ ﺑﻪ ﻣﻘﺪار اﻧﺪازه رﻛﻮد ﺟﺪول
ﻧﻤﺎد اﻓﺰاﻳﺶ ﻣﻲ ﻳﺎﺑﺪ .ﭼﻮن ورودي ﻫﺎ ﺑﺎ ﺷﺮوع از اﺑﺘﺪاي آراﻳﻪ ﺑﻪ ﺗﺮﺗﻴﺐ درج ﻣﻲ ﺷﻮﻧﺪ ،
آﻧﻬﺎ ﺑﻪ ﺗﺮﺗﻴﺒﻲ ﻛﻪ اﻋﻼن ﺷﺪه اﻧﺪ ﻗﺮار دارﻧﺪ .ﺑﺎ ﺟﺴﺘﺠﻮ ازﻣﺤﻞ availableو ﺣﺮﻛﺖ ﺑﻪ
ﺳﻤﺖ اﺑﺘﺪاي آراﻳﻪ ،ﻣﻄﻤﺌﻦ ﺧﻮاﻫﻴﻢ ﺑﻮد ﻛﻪ ﺟﺪﻳﺪﺗﺮﻳﻦ وارده اﻳﺠﺎد ﺷﺪه را ﺧﻮاﻫﻴﻢ ﻳﺎﻓﺖ .
اﮔﺮ ﺟﺪول ﻧﻤﺎد داري nﻧﺎم ﺑﺎﺷﺪ ،ﻣﻴﺰان ﻛﺎر ﻻزم ﺑﺮاي درج ﻛﺮدن ﻳﻚ ﻧﺎم ﺟﺪﻳﺪ ﺛﺎﺑﺖ
ﺧﻮاﻫﺪ ﺑﻮد ﭼﻨﺎﻧﭽﻪ ﺑﺮرﺳﻲ ﺑﺮاي وﺟﻮد آن ﻧﺎم از ﻗﺒﻞ در ﺟﺪول اﻧﺠﺎم ﻧﮕﻴﺮد .اﮔﺮ ﭼﻨﺪﻳﻦ
ورودي ﺑﺮاي ﻧﺎم ﻫﺎ ﻣﺠﺎز ﻧﺒﺎﺷﺪ ،ﺑﺮاي ﻣﺸﺨﺺ ﺷﺪن ﻋﺪم وﺟﻮد ﻧﺎم در ﺟﺪول ،ﺗﻤﺎم
ﺟﺪول ﺑﺎﻳﺪ ﺟﺴﺘﺠﻮ ﺷﻮد ،در اﻳﻦ ﻓﺮاﻳﻨﺪ ﻣﻴﺰان ﻛﺎر اﻧﺠﺎم ﺷﺪه ﻣﺘﻨﺎﺳﺐ ﺑﺎ nاﺳﺖ .ﺑﻪ ﻣﻨﻈﻮر
ﻳﺎﻓﺘﻦ داده ﻫﺎي ﻣﺮﺑﻮط ﺑﻪ ﻳﻚ ﻧﺎم ،ﺑﻪ ﻃﻮر ﻣﺘﻮﺳﻂ n/2از ﻧﺎم ﻫﺎ ﺑﺎﻳﺪ ﺟﺴﺘﺠﻮ ﺷﻮد ،
ﺑﻨﺎﺑﺮاﻳﻦ ﻫﺰﻳﻨﻪ درﺧﻮاﺳﺖ ﻧﻴﺰ ﻣﺘﻨﺎﺳﺐ ﺑﺎ nاﺳﺖ .ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﻳﻨﻜﻪ درج ﻛﺮدن
١٠٠
و درﺧﻮاﺳﺖ ،زﻣﺎﻧﻲ ﻣﺘﻨﺎﺳﺐ ﺑﺎ nﻧﻴﺎز دارﻧﺪ ،ﻛﻞ ﻛﺎر اﻧﺠﺎم ﺷﺪه ﺑﺮاي درج ﻛﺮدن nﻧﺎم و
اﻧﺠﺎم eدرﺧﻮاﺳﺖ ﺣﺪاﻛﺜﺮ ) cn(n+eﻣﻲ ﺑﺎﺷﺪ ﻛﻪ cﺛﺎﺑﺘﻲ اﺳﺖ ﻧﺸﺎن دﻫﻨﺪه زﻣﺎن ﻻزم
ﺑﺮاي اﻧﺠﺎم ﭼﻨﺪ دﺳﺘﻮر زﺑﺎن ﻣﺎﺷﻴﻦ .در ﻳﻚ ﺑﺮﻧﺎﻣﻪ ﺑﺎ اﻧﺪازه ﻣﺘﻮﺳﻂ ،ﻣﻤﻜﻦ اﺳﺖ e=1000
و n=100ﺑﻨﺎﺑﺮاﻳﻦ ﭼﻨﺪ ﺻﺪ ﻫﺰار دﺳﺘﻮر ﻣﺎﺷﻴﻦ در ﻓﺮاﻳﻨﺪ ﻧﮕﻬﺪاري اﻃﻼﻋﺎت ﺻﺮف ﻣﻲ
ﺷﻮد .اﻳﻦ ﻣﻘﺪار ﻣﻤﻜﻦ اﺳﺖ ﻧﮕﺮان ﻛﻨﻨﺪه ﻧﺒﺎﺷﺪ ،زﻳﺮا در ﻣﻮرد زﻣﺎن ﻛﻤﺘﺮ از ﻳﻚ ﺛﺎﻧﻴﻪ
ﺻﺤﺒﺖ ﻣﻲ ﺷﻮد .درﻫﺮ ﺻﻮرت ،اﮔﺮ eو nدر 10ﺿﺮب ﺷﻮﻧﺪ ،ﻫﺰﻳﻨﻪ در 100ﺿﺮب
ﺧﻮاﻫﺪ ﺷﺪ .و زﻣﺎن ﻧﮕﻬﺪاري اﻃﻼﻋﺎت ﻋﺎﻣﻠﻲ ﺑﺎز دارﻧﺪه ﺧﻮاﻫﺪ ﺑﻮد .ﻣﺤﺎﺳﺒﻪ زﻣﺎن اﺟﺮا ،
اﻃﻼﻋﺎت زﻳﺎدي را در راﺑﻄﻪ ﺑﺎ اﻳﻨﻜﻪ ﻛﺎﻣﭙﺎﻳﻠﺮ زﻣﺎن را در ﻛﺠﺎ ﺻﺮف ﻣﻲ ﻧﻤﺎﻳﺪ ،در اﺧﺘﻴﺎر
ﻗﺮار ﻣﻲ دﻫﺪ و ﻣﻲ ﺗﻮاﻧﺪ ﺑﻪ ﻣﻨﻈﻮر ﺗﺸﺨﻴﺺ اﻳﻨﻜﻪ آﻳﺎ زﻣﺎن ﺑﻴﺶ از ﺣﺪ ﺑﺮاي ﺟﺴﺘﺠﻮ
ﺟﺪاول درﻫﻢ
روش ﻣﺘﻔﺎوﺗﻲ در ﺟﺴﺘﺠﻮ ﺑﻪ ﻧﺎم ﺟﺴﺘﺠﻮي در ﻫﻢ در ﺑﺴﻴﺎري از ﻛﺎﻣﭙﺎﻳﻠﺮﻫﺎ ﺑﻜﺎر ﮔﺮﻓﺘﻪ ﺷﺪه
اﺳﺖ .در اﻳﻨﺠﺎ ﺣﺎﻟﺖ ﺳﺎده اي ﺑﻪ ﻧﺎم درﻫﻢ ﺑﺎز ﺑﺮرﺳﻲ ﻣﻲ ﮔﺮدد ﻛﻪ » ﺑﺎز « اﺷﺎره ﺑﻪ اﻳﻦ
وﻳﮋﮔﻲ دارد ﻛﻪ ﻣﺤﺪودﻳﺘﻲ ﺑﺮاي ﺗﻌﺪاد ورودي ﻫﺎﻳﻲ ﻛﻪ ﻣﻲ ﺗﻮاﻧﻨﺪ در ﺟﺪول ﻗﺮار ﮔﻴﺮﻧﺪ
وﺟﻮد ﻧﺪارد .ﺣﺘﻲ اﻳﻦ ﻃﺮح ﻧﻴﺰ ﻗﺎﺑﻠﻴﺖ اﻧﺠﺎم eدرﺧﻮاﺳﺖ ﺑﺎ nﻧﺎم را در زﻣﺎﻧﻲ ﻣﺘﻨﺎﺳﺐ ﺑﺎ
n(n+e)/mﺑﺎ ﻫﺮ ﺛﺎﺑﺖ دﻟﺨﻮاه mرا ﻓﺮاﻫﻢ ﻣﻲ ﺳﺎزد .ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﻳﻦ ﻛﻪ mﻣﻲ ﺗﻮاﻧﺪ ﺑﻪ ﻫﺮ
اﻧﺪازه دﻟﺨﻮاه ﺑﺰرگ ﺑﺎﺷﺪ ،ﺣﺪاﻛﺜﺮ ﺗﺎ nاﻳﻦ روش ﺑﺴﻴﺎر ﻛﺎراﺗﺮ از ﻟﻴﺴﺖ ﻫﺎي ﺧﻄﻲ اﺳﺖ
١٠١
و روش اﻧﺘﺨﺎب ﺷﺪه ﺳﺎﺧﺖ ﺟﺪاول ﻧﻤﺎد در اﻛﺜﺮ ﻣﻮارد اﺳﺖ .ﻫﻤﺎﻧﻄﻮر ﻛﻪ اﻧﺘﻈﺎر ﻣﻲ رود
ﻓﻀﺎي اﺷﻐﺎل ﺷﺪه ﺗﻮﺳﻂ اﻳﻦ ﺳﺎﺧﺘﻤﺎن داده ،ﺑﺎ mاﻓﺰاﻳﺶ ﻣﻲ ﻳﺎﺑﺪ ،ﺑﻨﺎﺑﺮاﻳﻦ ﺗﻌﺎدﻟﻲ ﺑﻴﻦ
زﻣﺎن وﻓﻀﺎ اﻳﺠﺎد ﺷﺪه اﺳﺖ .ﻃﺮح اﺻﻠﻲ ﺟﺴﺘﺠﻮي در ﻫﻢ در ﺷﻜﻞ زﻳﺮ ﻧﻤﺎﻳﺶ داده ﺷﺪه
اﺳﺖ :
....
match
20
....
last action ws
32
....
210
اﻳﻦ ﺳﺎﺧﺘﻤﺎن داده داراي دو ﺑﺨﺶ اﺳﺖ :
-2ورودي ﻫﺎي ﺟﺪول ﺑﻪ ﺻﻮرت mﻟﻴﺴﺖ ﭘﻴﻮﻧﺪي ﻣﺠﺰا ﺑﻪ ﻧﺎم bucketsﺳﺎزﻣﺎن دﻫﻲ
ﺷﺪه اﻧﺪ )ﺑﻌﻀﻲ از bucketsﻫﺎ ﻣﻤﻜﻦ اﺳﺖ ﺧﺎﻟﻲ ﺑﺎﺷﻨﺪ ( .ﻫﺮ رﻛﻮرد ﺟﺪول ﻧﻤﺎد دﻗﻴﻘﺎ در
ﻳﻜﻲ از اﻳﻦ ﻟﻴﺴﺖ ﻫﺎ ﻇﺎﻫﺮ ﻣﻲ ﺷﻮد .ﺣﺎﻓﻈﻪ ﺑﺮاي رﻛﻮردﻫﺎ ﻣﻲ ﺗﻮاﻧﺪ ازآراﻳﻪ اي از رﻛﻮرد
١٠٢
ﻫﺎ داده ﺷﻮد ،ﻫﻤﺎﻧﻄﻮر ﻛﻪ در ﺑﺨﺶ ﺑﻌﺪ ﺑﺤﺚ ﺷﺪه اﺳﺖ .از ﻃﺮف دﻳﮕﺮ اﻣﻜﺎﻧﺎت
ﺗﺨﺼﻴﺺ ﺣﺎﻓﻈﻪ ﭘﻮﻳﺎي زﺑﺎن ﭘﻴﺎده ﺳﺎزي ﻛﻨﻨﺪه ،ﻣﻲ ﺗﻮاﻧﺪ ﺑﺮاي ﮔﺮﻓﺘﻦ ﻓﻀﺎﺑﺮاي رﻛﻮردﻫﺎ
ﺑﻪ ﻣﻨﻈﻮر ﺗﺸﺨﻴﺺ وﺟﻮد ﻳﻚ وارده در ﺟﺪول ﻧﻤﺎد ﺑﺮاي رﺷﺘﻪ ، sﻳﻚ ﺗﺎﺑﻊ در ﻫﻢ از hﺑﻪ
sاﺳﺘﻔﺎده ﻣﻲ ﺷﻮد ﻛﻪ) h(sﻋﺪد ﺑﻴﻦ 0ﺗﺎ m-1را ﺑﺮ ﻣﻲ ﮔﺮداﻧﺪ .اﮔﺮ sدر ﺟﺪول ﻧﻤﺎد
وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﺪ ،در ﻓﻬﺮﺳﺘﻲ ﺑﺎ ﺷﻤﺎره ) h(sﻗﺮار دارد .اﮔﺮ sﻫﻨﻮز در ﺟﺪول وﺟﻮد
ﻧﺪارد ،ﺑﺎ اﻳﺠﺎد ﻳﻚ رﻛﻮرد ﺑﺮاي sﻳﻌﻨﻲ ﻗﺮار دادن در ﺟﻠﻮي ﻓﻬﺮﺳﺖ ﺷﻤﺎره ) h(sﺑﻪ
ﺑﺎ ﻳﻚ ﺣﺴﺎب ﺳﺮاﻧﮕﺸﺘﻲ اﮔﺮ nﻧﺎم ،در ﺟﺪوﻟﻲ ﺑﺎ اﻧﺪازه mوﺟﻮد داﺷﺘﻪ ﺑﺎﺷﺪ ،ﺑﻄﻮر
ﻣﺘﻮﺳﻂ ﻓﻬﺮﺳﺖ داراي ﻃﻮﻟﻲ ﺑﺮاﺑﺮ n/mرﻛﻮرد ﻣﻲ ﺑﺎﺷﺪ .ﺑﺎ اﻧﺘﺨﺎب mﺑﻪ ﺷﻜﻠﻲ ﻛﻪ n/m
ﺑﻪ ﻳﻚ ﺛﺎﺑﺖ ﻛﻮﭼﻚ ﻣﺤﺪود ﺷﻮد ،ﻣﺜﻼ ، 2زﻣﺎن ﻻزم ﺑﺮاي دﺳﺘﺮﺳﻲ ﺑﻪ ﻳﻚ ورودي از
ﻓﻀﺎي اﺷﻐﺎل ﺷﺪه ﺗﻮﺳﻂ ﺟﺪول ﻧﻤﺎد ﺷﺎﻣﻞ mﻛﻠﻤﻪ ﺑﺮاي ﺟﺪول درﻫﻢ و cnﻛﻠﻤﻪ ﺑﺮاي
وارده ﻫﺎي ﺟﺪول اﺳﺖ ﻛﻪ cﺗﻌﺪاد ﻛﻠﻤﺎت ﺑﺮاي ﻫﺮ وارده ﺟﺪول اﺳﺖ .ﺑﻨﺎﺑﺮاﻳﻦ ﻓﻀﺎي
ﻻزم ﺑﺮاي ﺟﺪول در ﻫﻢ ﻓﻘﻂ ﺑﻪ mﺑﺴﺘﮕﻲ دارد ،و ﻓﻀﺎي ﻻزم ﺑﺮاي وارده ﺟﺪول ،ﻓﻘﻂ ﺑﻪ
١٠٣
اﻧﺘﺨﺎب mﺑﻪ ﻛﺎرﺑﺮد ﻣﻮرد ﻧﻈﺮ ﺑﺮاي ﺟﺪول ﻧﻤﺎد واﺑﺴﺘﻪ اﺳﺖ .ﺑﺎ اﻧﺘﺨﺎب ﻣﻘﺪار ﭼﻨﺪ ﺻﺪ
ﺑﺮاي mزﻣﺎن ﻣﺮاﺟﻌﻪ ﺑﻪ ﺟﺪول ﺑﻪ اﻧﺪازه ﻛﺴﺮي ﻧﺎﭼﻴﺰ از ﻛﻞ زﻣﺎن ﻣﺼﺮف ﺷﺪه ﺗﻮﺳﻂ
ﻛﺎﻣﭙﺎﻳﻠﺮ ﺧﻮاﻫﺪ ﺑﻮد ﺣﺘﻲ ﺑﺮاي ﺑﺮﻧﺎﻣﻪ ﻫﺎﻳﻲ ﺑﺎ اﻧﺪازه ﻣﺘﻮﺳﻂ .ﻫﻨﮕﺎﻣﻲ ﻛﻪ ورودي ﻛﺎﻣﭙﺎﻳﻠﺮ
ﺗﻮﺳﻂ ﺑﺮﻧﺎﻣﻪ دﻳﮕﺮي ﺗﻮﻟﻴﺪ ﻣﻲ ﺷﻮد ،ﺗﻌﺪاد ﻧﺎم ﻫﺎ ﻧﺴﺒﺖ ﺑﻪ اﻛﺜﺮ ﺑﺮﻧﺎﻣﻪ ﻫﺎي ﺗﻮﻟﻴﺪ ﺷﺪه
ﺗﻮﺳﻂ اﻧﺴﺎن ﺑﺎ ﻫﻤﺎن اﻧﺪازه اﻓﺰاﻳﺶ ﭼﺸﻤﮕﻴﺮي ﺧﻮاﻫﺪ داﺷﺖ .ﺑﻨﺎﺑﺮاﻳﻦ ﺟﺪول ﺑﺎ اﻧﺪازه
ﺑﺰرﮔﺘﺮ ﺗﺮ ﺟﻴﺢ دارد .اﻳﻦ ﺳﻮال ﻣﻮرد ﺗﻮﺟﻪ اﺳﺖ ﻛﻪ ﭼﮕﻮﻧﻪ ﺗﺎﺑﻊ در ﻫﻢ ﻃﺮاﺣﻲ ﺷﻮد ﻛﻪ ﺑﻪ
ﺳﺎدﮔﻲ ﺑﺮاي رﺷﺘﻪ ﻫﺎﻳﻲ از ﻛﺎراﻛﺘﺮﻫﺎ ﻗﺎﺑﻞ ﻣﺤﺎﺳﺒﻪ ﺑﺎﺷﺪ و رﺷﺘﻪ ﻫﺎ را ﺑﻄﻮر ﻳﻜﻨﻮاﺧﺖ در
ﻣﻴﺎن mﻟﻴﺴﺖ ﺗﻮزﻳﻊ ﻧﻤﺎﻳﺪ .ﻳﻚ روش ﻣﻨﺎﺳﺐ ﺑﺮاي ﻣﺤﺎﺳﺒﻪ ﺗﻮاﺑﻊ در ﻫﻢ ﺑﻪ ﺻﻮرت زﻳﺮ
اﺳﺖ :
-1ﻳﻚ ﻋﺪدﺻﺤﻴﺢ ﻣﺜﺒﺖ ﻣﺎﻧﻨﺪ hاز ﻛﺎراﺗﺮﻫﺎي Ck,….C2,C1در رﺷﺘﻪ sﻣﺸﺨﺺ ﻣﻲ
ﮔﺮدد .ﺗﺒﺪﻳﻞ ﻳﻚ ﻛﺎراﻛﺘﺮ ﺑﻪ ﻋﺪد ﺻﺤﻴﺢ ﻣﻌﻤﻮﻻ ﺗﻮﺳﻂ زﺑﺎن ﭘﻴﺎده ﺳﺎزي ﻛﻨﻨﺪه ﭘﺸﺘﻴﺒﺎﻧﻲ
ﻣﻲ ﮔﺮدد .ﭘﺎﺳﻜﺎل ﺗﺎﺑﻌﻲ ﺑﻪ ﻧﺎم ordراﺑﺮاي اﻳﻦ ﻣﻨﻈﻮر ﻓﺮاﻫﻢ ﻧﻤﻮده اﺳﺖ c .ﺑﻄﻮر ﺧﻮد
ﻛﺎر ﻳﻚ ﻛﺎراﻛﺘﺮ را ﺑﻪ ﻋﺪد ﺻﺤﻴﺢ ﺗﺒﺪﻳﻞ ﻣﻲ ﻧﻤﺎﻳﺪ اﮔﺮ ﻳﻚ ﻋﻤﻞ ﻣﺤﺎﺳﺒﺎﺗﻲ ﺑﺮ روي آن
-2ﻋﺪد ﺻﺤﻴﺢ hﻛﻪ در ﻣﺮﺣﻠﻪ ﻗﺒﻞ ﺑﺪﺳﺖ آﻣﺪه ﺑﻪ ﺷﻤﺎره ﻟﻴﺴﺖ ﺗﺒﺪﻳﻞ ﻣﻲ ﮔﺮدد ،ﻳﻌﻨﻲ
ﻋﺪد ﺻﺤﻴﺤﻲ ﺑﻴﻦ 0و . m-1ﻓﻘﻂ ﺑﺎ ﺗﻘﺴﻴﻢ ﺑﺮ mو اﺳﺘﻔﺎده از ﺑﺎﻗﻴﻤﺎﻧﺪه آن روﺷﻲ ﻣﻌﻘﻮل
١٠٤
اﺳﺖ .اﮔﺮ mﻋﺪد اول ﺑﺎﺷﺪ ،اﺳﺘﻔﺎده از ﺑﺎﻗﻴﻤﺎﻧﺪه ﺑﻪ ﻧﻈﺮ ﺑﻬﺘﺮ ﻋﻤﻞ ﻣﻲ ﻛﻨﺪ ﺑﻨﺎﺑﺮاﻳﻦ در
ﺟﺪول درﻫﻢ ﻣﺜﺎل زده ﺷﺪه اﻧﺘﺨﺎب 211ﺑﻪ ﺟﺎي 200اﻧﺠﺎم ﻣﻲ ﺷﻮد .
ﺗﻮاﺑﻊ در ﻫﻢ ﻛﻪ ﺑﻪ ﺗﻤﺎم ﻛﺎراﻛﺘﺮﻫﺎي رﺷﺘﻪ ﺗﻮﺟﻪ دارﻧﺪ ،ﻛﻤﺘﺮ از ﺗﻮاﺑﻌﻲ ﻛﻪ ﻓﻘﻂ ﺑﻪ ﺗﻌﺪادي
از ﻛﺎراﻛﺘﺮﻫﺎي اﻧﺘﻬﺎ ﻳﺎ وﺳﻂ رﺷﺘﻪ ﺗﻮﺟﻪ دارﻧﺪ دﭼﺎر ﺧﻄﺎ ﻣﻲ ﺷﻮﻧﺪ .ﺑﺨﺎﻃﺮ آورﻳﺪ ورودي
ﺑﻪ ﻛﺎﻣﭙﺎﻳﻠﺮ ﻣﻤﻜﻦ اﺳﺖ ﺗﻮﺳﻂ ﻳﻚ ﺑﺮﻧﺎﻣﻪ اﻳﺠﺎد ﺷﻮد و ﺑﻨﺎﺑﺮاﻳﻦ ﺑﻪ ﻣﻨﻈﻮر ﺟﻠﻮﮔﻴﺮي از
ﺗﻨﺎﻗﺾ ﺑﺎ ﻧﺎم ﻫﺎي اﺳﺘﻔﺎده ﺷﺪه ﺗﻮﺳﻂ اﺷﺨﺎص ﻳﺎ ﺑﺮﻧﺎﻣﻪ دﻳﮕﺮ ﻣﻤﻜﻦ اﺳﺖ داراي ﺷﻜﻞ
ﺧﺎص ﺑﺎﺷﻨﺪ اﺷﺨﺎص ﺗﻤﺎﻳﻞ ﺑﻪ دﺳﺘﻪ ﺑﻨﺪي ﻧﺎم ﻫﺎ دارﻧﺪ ﻣﺎﻧﻨﺪ اﻧﺘﺨﺎب ﻫﺎي :
ﻳﻚ روش ﺳﺎده ﺑﺮاي ﻣﺤﺎﺳﺒﻪ ،hﺟﻤﻊ ﻧﻤﻮدن ﻣﻘﺎدﻳﺮ ﺻﺤﻴﺢ ﻛﺎراﻛﺘﺮﻫﺎي رﺷﺘﻪ اﺳﺖ .اﻳﺪه
ﺑﻬﺘﺮ ،ﺿﺮب ﻣﻘﺪار ﻗﺒﻠﻲ hﺑﺎ ﺛﺎﺑﺖ aﻗﺒﻞ از ﺟﻤﻊ ﺑﺎ ﻛﺎراﻛﺘﺮ ﺑﻌﺪي اﺳﺖ .ﻳﻌﻨﻲ
درﻫﻢ ﻛﻪ ﺷﻤﺎره ﻟﻴﺴﺖ را ﻣﺸﺨﺺ ﻣﻲ ﻧﻤﺎﻳﺪ h mod mاﺳﺖ ( ﺗﻨﻬﺎ ﺟﻤﻊ ﻛﺎراﻛﺘﺮﻫﺎ ﺑﺎ
ﻳﻜﺪﻳﮕﺮ ،ﺣﺎﻟﺘﻲ اﺳﺖ ﻛﻪ a=1ﻣﻲ ﺑﺎﺷﺪ .ﻳﻚ اﺳﺘﺮاﺗﮋي ﻣﺸﺎﺑﻪ اﻳﻦ اﺳﺖ ﻛﻪ ciﻫﺎ ﺑﺠﺎي
ﺑﺮاي اﻋﺪاد ﺻﺤﻴﺢ 32ﺑﻴﺘﻲ ،اﮔﺮ a = 65599ﺑﺎﺷﺪ ،ﻛﻪ ﻳﻚ ﻋﺪد اول ﻧﺰدﻳﻚ 216اﺳﺖ
در اﻳﻦ ﺻﻮرت ﺑﺰودي در ﻣﺤﺎﺳﺒﻪ a hi-lﺳﺮ رﻳﺰ رخ ﺧﻮاﻫﺪ داد .ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﻳﻨﻜﻪ aﻋﺪد
اول اﺳﺖ ،ﺻﺮﻓﻨﻈﺮ ﻛﺮدن از ﺳﺮرﻳﺰﻫﺎ و ﺣﻔﻆ 32ﺑﻴﺖ ﻣﺮﺗﺒﻪ ﭘﺎﻳﻴﻦ ﺑﻪ ﻧﻈﺮ ﻣﻨﺎﺳﺐ اﺳﺖ .
١٠٥
در ﻳﻚ ﺳﺮي آزﻣﺎﻳﺶ ،ﺗﺎﺑﻊ درﻫﻢ hashpjwدر ﺷﻜﻞ زﻳﺮ ﺑﺮاي ﻛﺎﻣﭙﺎﻳﻠﺮ Cاز
P.J.Weinbergerﺑﺎ ﺗﻤﺎم اﻧﺪازه ﻫﺎ ﺟﺪول اﻣﺘﺤﺎن ﺷﺪه ﺑﻪ ﺷﻜﻞ ﻣﻨﺎﺳﺒﻲ ﻋﻤﻞ ﻣﻲ ﻧﻤﺎﻳﺪ.
ﺗﺎﺑﻊ ﻧﺰدﻳﻚ ،ﺗﺎﺑﻌﻲ ﺑﻮد ﻛﻪ hرا ﺑﺎ ﺿﺮب ﻣﻘﺪار ﻗﺒﻠﻲ آن در 65599و ﺻﺮف ﻧﻈﺮ از ﺳﺮ
رﻳﺰ و ﺟﻤﻊ آن ﺑﺎ ﻛﺎراﻛﺘﺮ ﺑﻌﺪي ﻣﺤﺎﺳﺒﻪ ﻧﻤﻮده ﺗﺎﺑﻊ hashpjwﺑﺎ ﺷﺮوع از h=0ﻣﺤﺎﺳﺒﻪ ﻣﻲ
ﺷﻮد .ﺑﺮاي ﻫﺮ ﻛﺎراﻛﺘﺮ cﺑﻴﺖ ﻫﺎي hﺑﻪ اﻧﺪازه 4ﻣﻜﺎن ﺑﻪ ﭼﭗ اﻧﺘﻘﺎل ﻣﻲ ﻳﺎﺑﻨﺪ و ﺑﺎ cﺟﻤﻊ
ﻣﻲ ﺷﻮﻧﺪ .اﮔﺮ ﻫﺮ ﻳﻚ از 4ﺑﻴﺖ ﻣﺮﺗﺒﻪ ﺑﺎﻻي (1) hﺑﺎﺷﻨﺪ اﻳﻦ 4ﺑﻴﺖ 24 ،ﻣﻜﺎن ﺑﻪ راﺳﺖ
١٠٦
اﻧﺘﻘﺎل داده ﻣﻲ ﺷﻮد و ﺑﺎ exclusive-or, hﻣﻲ ﮔﺮدﻧﺪ و ﻫﺮ ﻳﻚ از ﭼﻬﺎر ﺑﻴﺖ ﻣﺮ ﺗﺒﻪ ﺑﺎﻻ
ﻣﺜﺎل -1اﻟﻒ ﺑﻪ ﻣﻨﻈﻮر رﺳﻴﺪن ﺑﻪ ﺑﻬﺘﺮﻳﻦ ﻧﺘﺎﻳﺞ ،اﻧﺪازه ﺟﺪول درﻫﻢ و ورودي ﻣﻮرد
اﻧﺘﻈﺎر در زﻣﺎن ﻃﺮاﺣﻲ ﺗﺎﺑﻊ در ﻫﻢ ﺑﺎﻳﺪ ﻣﻮرد ﺗﻮﺟﻪ ﻗﺮار ﮔﻴﺮد .ﺑﺮاي ﻣﺜﺎل ﻣﻄﻠﻮب اﺳﺖ ﻛﻪ
ﻣﻘﺎدﻳﺮ در ﻫﻢ ﺑﺮاي ﻧﺎم ﻫﺎﻳﻲ ﻛﻪ ﺑﻄﻮر ﻣﻜﺮر در زﺑﺎن رخ ﻣﻲ دﻫﻨﺪ ﻣﺘﻔﺎوت ﺑﺎﺷﻨﺪ اﮔﺮ
ﻛﻠﻤﺎت ﻛﻠﻴﺪي ﻧﻴﺰ ﺑﻪ ﺟﺪول ﻧﻤﺎد وارد ﺷﻮﻧﺪ ،ﻛﻠﻤﺎت ﻛﻠﻴﺪي ﻧﻴﺰ در ﮔﺮوه ﻧﺎم ﻫﺎﻳﻲ ﻫﺴﺘﻨﺪ
ﻛﻪ ﺑﻄﻮر ﻣﻜﺮر اﺳﺘﻔﺎده ﻣﻲ ﺷﻮﻧﺪ .اﮔﺮ ﭼﻪ در ﻳﻚ ﻧﻤﻮﻧﻪ از ﺑﺮﻧﺎﻣﻪ ﻫﺎي cﻧﺎم iﺗﺎ ﺑﻴﺶ از ﺳﻪ
ﻳﻚ راه اﻣﺘﺤﺎن ﻧﻤﻮدن ﺗﺎﺑﻊ درﻫﻢ ﺗﻌﻴﻴﻦ ﺗﻌﺪاد رﺷﺘﻪ ﻫﺎﻳﻲ اﺳﺖ ﻛﻪ در ﻟﻴﺴﺖ ﻣﺸﺎﺑﻪ ﻗﺮار ﻣﻲ
ﮔﻴﺮﻧﺪ .ﺑﺎ داﺷﺘﻦ ﻳﻚ ﻓﺎﻳﻞ ﺑﻪ ﻧﺎم Fﺷﺎﻣﻞ nرﺷﺘﻪ ،ﻓﺮض ﻛﻨﻴﺪ ﺗﻌﺪاد bjرﺷﺘﻪ در ﻟﻴﺴﺖ j
ﻳﻚ اﻧﺪازه ﮔﻴﺮي از ﻣﻴﺰان ﻳﻜﻨﻮاﺧﺘﻲ رﺷﺘﻪ ﻫﺎﻳﻲ ﻛﻪ در 0 ≤ j ≤ m −1 ﻗﺮار ﮔﻴﺮﻧﺪ ،ﺑﺮاي
ﺑﺪﺳﺖ ﻣﻲ آﻳﺪ .ﻳﻚ ﺑﺎزﺑﻴﻨﻲ اﺳﺘﻘﺮاﻳﻲ ﺑﺮاي اﻳﻦ ﻋﺒﺎرت اﻳﻦ اﺳﺖ ﻛﻪ ﺑﺮاي ﻳﺎﻓﺘﻦ اوﻟﻴﻦ
وارده در ﻟﻴﺴﺖ jﻧﻴﺎز ﺑﺮرﺳﻲ ﻳﻚ ﻋﻨﺼﺮ ازﻟﻴﺴﺖ ﻣﻲ ﺑﺎﺷﺪ ،ﺑﺮاي ﻳﺎﻓﺘﻦ دوﻣﻴﻦ ﻋﻨﺼﺮ ،دو
ﻋﻀﻮ ﺑﺎﻳﺪ ﺑﺮرﺳﻲ ﺷﻮد ،و ﺑﻪ ﻫﻤﻴﻦ ﺗﺮﺗﻴﺐ ﺗﺎ ﺑﺮاي آﺧﺮﻳﻦ ﻋﻨﺼﺮ ﺑﻪ bjﺑﺮرﺳﻲ ﻧﻴﺎز اﺳﺖ .
١٠٧
ﻧﻤﺎﻳﺶ اﻃﻼﻋﺎت ﻣﺤﺪوده
وارده ﻫﺎي ﺟﺪول ﻧﻤﺎد ،ﺑﺮاي اﻋﻼن ﻧﺎم ﻫﺎ ﻣﻲ ﺑﺎﺷﻨﺪ .ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﺑﺮاي رﺧﺪادي از ﻧﺎم ،
در ﺟﺪول ﻧﻤﺎد ﺟﺴﺘﺠﻮ ﻣﻲ ﺷﻮد ،وارده ﻣﺮﺑﻮط ﺑﻪ اﻋﻼن ﻣﻨﺎﺳﺐ آن ﻧﺎم ﺑﺎﻳﺪ ﺑﺮ ﮔﺮداﻧﺪه
ﺷﻮد .ﻗﻮاﻧﻴﻦ ﻣﺤﺪوده در زﺑﺎن ﻣﺒﺪا ﻣﺸﺨﺺ ﻣﻲ ﻧﻤﺎﻳﻨﺪ ﻛﻪ ﻛﺪام اﻋﻼن ﻣﻨﺎﺳﺐ اﺳﺖ .
ﻳﻚ روش ﺳﺎده اﺳﺘﻔﺎده از ﺟﺪول ﻧﻤﺎد ﻣﺠﺰا ﺑﺮاي ﻫﺮ ﻣﺤﺪوده اﺳﺖ .در ﻧﺘﻴﺠﻪ ﺟﺪول ﻧﻤﺎد
ﺑﺮاي ﻳﻚ روﻳﻪ ﻳﺎ ﻣﺤﺪوده ﻣﻌﺎدل رﻛﻮرد ﻓﻌﺎﻟﻴﺖ در زﻣﺎن ﻛﺎﻣﭙﺎﻳﻞ اﺳﺖ .اﻃﻼﻋﺎت ﻏﻴﺮ
ﻣﺤﻠﻲ ﻳﻚ روﻳﻪ ﺑﺎ اﺳﺘﻔﺎده از ﭘﻮﻳﺶ ﺟﺪول ﻫﺎي ﻧﻤﺎد ﻣﺮﺑﻮط ﺑﻪ روﻳﻪ ﻫﺎي ﻣﻮﺟﻮد در ﺣﻴﻄﻪ
ﻗﻮاﻧﻴﻦ ﻣﺤﺪوده زﺑﺎن ﻳﺎﻓﺖ ﻣﻲ ﺷﻮد .ﺑﻄﻮر ﻣﺸﺎﺑﻪ اﻃﻼﻋﺎت ﻣﺤﻠﻲ ﻳﻚ روﻳﻪ ﻣﻲ ﺗﻮاﻧﺪ ﺑﻪ
ﮔﺮوه ﻣﺮﺑﻮط ﺑﻪ آن روﻳﻪ در درﺧﺖ ﻧﺤﻮ ﻣﺮﺑﻮط ﺑﻪ آن ﺑﺮﻧﺎﻣﻪ اﻟﺤﺎق ﮔﺮدد .ﺑﺎ اﻳﻦ ﮔﺮاﻳﺶ ،
اﻛﺜﺮ ﻗﻮاﻧﻴﻦ ﻣﺤﺪوده ﻛﻪ ﺑﻪ ﺷﻜﻞ ﻧﺰدﻳﻜﻲ ﻣﺘﺪاﺧﻞ ﻫﺴﺘﻨﺪ ،ﻣﻲ ﺗﻮاﻧﻨﺪ ﺑﺎ اﻗﺘﺒﺎس از ﺳﺎﺧﺘﻤﺎن
داده ﻫﺎي اراﺋﻪ ﺷﺪه در اﻳﻦ ﺑﺨﺶ ﭘﻴﺎده ﺳﺎزي ﮔﺮدﻧﺪ .ﻧﺎم ﻫﺎي ﻣﺤﻠﻲ ﻫﺮ روﻳﻪ ﺑﺎ اﺧﺘﺼﺎص
ﻋﺪد ﻣﻨﺤﺼﺮ ﺑﻪ ﻓﺮدي ﺑﻪ ﻫﺮ روﻳﻪ ﻗﺎﺑﻞ ﭘﻴﮕﻴﺮي اﺳﺖ .اﮔﺮ زﺑﺎن داراي ﺳﺎﺧﺘﺎر ﺑﻠﻮﻛﻲ اﺳﺖ
ﺑﻠﻮك ﻫﺎ ﻧﻴﺰ ﺑﺎﻳﺪ ﺷﻤﺎره ﮔﺬاري ﺷﻮﻧﺪ .ﺷﻤﺎره ﻫﺮ روﻳﻪ ﻣﻲ ﺗﻮاﻧﺪ ﺑﻪ ﺻﻮرت ﻧﺤﻮﮔﺮا ﺑﺎ
اﺳﺘﻔﺎده از ﻗﻮاﻧﻴﻦ ﻣﻌﻨﺎﻳﻲ ﻛﻪ اﺑﺘﺪا و اﻧﺘﻬﺎي ﻫﺮ روﻳﻪ را ﻣﺸﺨﺺ ﻣﻲ ﻧﻤﺎﻳﻨﺪ ،ﻣﺤﺎﺳﺒﻪ ﮔﺮدد.
ﺷﻤﺎره روﻳﻪ ﺑﻪ ﻋﻨﻮان ﺑﺨﺸﻲ از ﺗﻤﺎم اﻃﻼﻋﺎت ﻣﺤﻠﻲ آن روﻳﻪ ﻗﺮار ﻣﻲ ﮔﻴﺮد ،ﻧﻤﺎﻳﺶ ﻧﺎم
ﻣﺤﻠﻲ در ﺟﺪول ﻧﻤﺎد زوﺟﻲ اﺳﺖ ﺷﺎﻣﻞ ﻧﺎم و ﺷﻤﺎره روﻳﻪ) .در ﺑﻌﻀﻲ از ﻣﻮارد ﻣﺎﻧﻨﺪ آن
١٠٨
ﻫﺎﻳﻲ ﻛﻪ در زﻳﺮ ﺗﻮﺻﻴﻒ ﺷﺪه اﻧﺪ ﻇﺎﻫﺮ ﺷﺪن ﺷﻤﺎره روﻳﻪ ﺑﻪ ﻃﻮر واﻗﻌﻲ ﻟﺰوﻣﻲ ﻧﺪارد ،زﻳﺮا
ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﺑﺮاي ﻧﺎم ﺗﺎزه ﭘﻮﻳﺶ ﺷﺪه ﺑﻪ ﺟﺪول ﻧﻤﺎد ﻣﺮاﺟﻌﻪ ﻣﻲ ﺷﻮد ،ﺗﻨﻬﺎ ﻫﻨﮕﺎﻣﻲ ﻛﻪ
ﮔﺮدد و ﺷﻤﺎره ﻫﻤﺮاه آن در وارده ﺟﺪول ﻧﻤﺎد ﺑﺎ ﺷﻤﺎره روﻳﻪ اي ﻛﻪ در ﺣﺎل ﭘﺮدازش اﺳﺖ
ﺑﺮاﺑﺮ ﺑﺎﺷﺪ .اﻛﺜﺮ ﻗﻮاﻧﻴﻦ ﻣﺤﺪوده اي ﻛﻪ ﺑﻄﻮر ﻧﺰدﻳﻜﻲ ﺑﺎ ﻫﻢ ﻣﺘﺪاﺧﻠﻨﺪ ﻣﻲ ﺗﻮاﻧﻨﺪ در ﻗﺎﻟﺐ
ﻋﺒﺎرت ﻫﺎﻳﻲ از اﻋﻤﺎل زﻳﺮ ﺑﺮ روي ﻳﻚ ﻧﺎم ﭘﻴﺎده ﺳﺎزي ﺷﻮﻧﺪ :
وارده ﻫﺎي ﺣﺬف ﺷﺪه ﺑﺎﻳﺪ ﻧﮕﻬﺪاري ﺷﻮﻧﺪ ،آﻧﻬﺎ ﻓﻘﻂ از ﺟﺪول ﻧﻤﺎد ﻓﻌﺎل ﺣﺬف ﻣﻲ
ﺷﻮﻧﺪ در ﻛﺎﻣﭙﺎﻳﻠﺮ ﺗﻚ ﮔﺬره ،اﻃﻼﻋﺎت ﺟﺪول ﻧﻤﺎد در ﻣﻮرد ﻳﻚ ﻣﺤﺪوده ﺷﺎﻣﻞ ﺑﺪﻧﻪ
روﻳﻪ ،ﭘﺲ از ﭘﺎﻳﺎن ﭘﺮدازش ﺑﺪﻧﻪ روﻳﻪ ﻣﻮرد ﻧﻴﺎز ﻧﻤﻲ ﺑﺎﺷﺪ .ﺑﻪ ﻫﺮ ﺣﺎل ﻣﻤﻜﻦ اﺳﺖ در
زﻣﺎن اﺟﺮا ﻣﻮرد ﻧﻴﺎز ﺑﺎﺷﺪ ،ﻣﺨﺼﻮﺻﺎً اﮔﺮ ﻳﻚ ﺳﻴﺴﺘﻢ ﺗﺸﺨﻴﺼﻲ زﻣﺎن اﺟﺮا ﭘﻴﺎده ﺳﺎزي
ﺷﻮد .در اﻳﻦ ﺣﺎﻟﺖ ،اﻃﻼﻋﺎت ﺟﺪول ﻧﻤﺎد ،ﺑﺎﻳﺪ ﺑﻪ ﻛﺪ ﺗﻮﻟﻴﺪ ﺷﺪه ﺑﺮاي اﺳﺘﻔﺎده اﻟﺤﺎق ﮔﺮ
١٠٩
ﻫﺮ ﻳﻚ از ﺳﺎﺧﺘﻤﺎن داده ﻫﺎي ﺑﺤﺚ ﺷﺪه دراﻳﻦ ﺑﺨﺶ ،ﻟﻴﺴﺖ ﻫﺎ و ﺟﺪاول در ﻫﻢ ﻣﻲ
ﺗﻮاﻧﻨﺪ ﺑﻪ ﺷﻜﻠﻲ ﺑﻪ ﻛﺎر ﮔﺮﻓﺘﻪ ﺷﻮﻧﺪ ﻛﻪ اﻋﻤﺎل ﻓﻮق را ﺣﻤﺎﻳﺖ ﻧﻤﺎﻳﻨﺪ .
ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﻟﻴﺴﺖ ﺧﻄﻲ ﺷﺎﻣﻞ آراﻳﻪ اي از رﻛﻮرد ﻫﺎ در ﻗﺴﻤﺖ ﻫﺎي ﻗﺒﻠﻲ اﻳﻦ ﺑﺨﺶ
ﺗﻮﺻﻴﻒ ﺷﺪ ،ﺑﻪ اﻳﻦ ﻧﻜﺘﻪ اﺷﺎره ﺷﺪه ﻛﻪ ﭼﮕﻮﻧﻪ Lookupﻣﻲ ﺗﻮاﻧﺪ ﺑﺎ درج ﻛﺮدن وارده ﻫﺎ
در ﻳﻚ ﻃﺮف ﺑﻪ ﺷﻜﻠﻲ ﻛﻪ ﺗﺮﺗﻴﺐ وارده ﻫﺎ درآراﻳﻪ ﻣﺸﺎﺑﻪ ﺗﺮﺗﻴﺐ درج ﻛﺮدن وارده ﻫﺎ
ﺑﺎﺷﺪ ،ﭘﻴﺎده ﺳﺎزي ﺷﻮد .ﻳﻚ ﭘﻮﻳﺶ ﺑﺎ ﺷﺮوع از اﻧﺘﻬﺎ و اداﻣﻪ ﺑﻪ ﺳﻤﺖ اﺑﺘﺪاي آراﻳﻪ ،
ﺟﺪﻳﺘﺮﻳﻦ وارده را ﺑﺮاي آن ﻧﺎم ﻣﻲ ﻳﺎﺑﺪ .اﻳﻦ وﺿﻌﻴﺖ ﺷﺒﻴﻪ ﻟﻴﺴﺖ ﭘﻴﻮﻧﺪي ﺷﻜﻞ زﻳﺮ
a2 a0
… … …
اﺷﺎره ﮔﺮ frontﺑﻪ ﺟﺪﻳﺘﺮﻳﻦ وارده اﺿﺎﻓﻪ ﺷﺪه ﺑﻪ ﻟﻴﺴﺖ اﺷﺎره ﻣﻲ ﻧﻤﺎﻳﻨﺪ .ﭘﻴﺎده ﺳﺎزي
insertزﻣﺎن ﺛﺎﺑﺘﻲ ﻧﻴﺎز دارد زﻳﺮا وارده ﺟﺪﻳﺪ در ﺟﻠﻮي ﻟﻴﺴﺖ ﻗﺮار ﻣﻲ ﮔﻴﺮد .ﭘﻴﺎده ﺳﺎزي
Lookupﺑﺎ ﭘﻮﻳﺶ ﻟﻴﺴﺖ و ﺷﺮوع از وارده اﺷﺎره ﺷﺪه ﺗﻮﺳﻂ frontو دﻧﺒﺎل ﻧﻤﻮدن اﺗﺼﺎل
ﻫﺎ ﺗﺎ زﻣﺎن ﻳﺎﻓﺘﻦ وارد ه ﻣﻮرد ﻧﻈﺮ ﻳﺎ ﻓﺮارﺳﻴﺪن اﻧﺘﻬﺎي ﻟﻴﺴﺖ ،اﻧﺠﺎم ﻣﻲ ﮔﻴﺮد .در ﺷﻜﻞ
ﻓﻮق وارده ﺑﺮاي aﻛﻪ درﺑﻠﻮك B2اﻋﻼن ﺷﺪه اﺳﺖ ،ﻛﻪ ﺧﻮد در داﺧﻞ ﺑﻠﻮك B0ﻗﺮار
دارد ،از وارده aﻛﻪ در B0اﻋﻼن ﺷﺪه ،ﺑﻪ اﺑﺘﺪاي ﻟﻴﺴﺖ ﻧﺰدﻳﻚ ﺗﺮ اﺳﺖ .
١١٠
ﺑﺮاي ﻋﻤﻞ deleteﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ وارده ﻫﺎي ﻣﺮﺑﻮط ﺑﻪ اﻋﻼن ﻫﺎي روﻳﻪ اي ﻛﻪ در
ﻋﻤﻴﻖ ﺗﺮﻳﻦ روﻳﻪ داﺧﻠﻲ ﻗﺮار دارﻧﺪ ،در ﻧﺰدﻳﻚ ﺗﺮﻳﻦ ﻗﺴﻤﺖ ﺑﻪ اﺑﺘﺪاي ﻟﻴﺴﺖ ﻗﺮار ﻣﻲ
ﮔﻴﺮﻧﺪ .ﺑﻨﺎﺑﺮاﻳﻦ ،ﻧﻴﺎزي ﺑﻪ ﻧﮕﻬﺪاري ﺷﻤﺎره روﻳﻪ ﺑﺎ ﻫﺮ وارده ﻧﻤﻲ ﺑﺎﺷﺪ .اﮔﺮ اوﻟﻴﻦ وارده
ﺑﺮاي ﻫﺮ روﻳﻪ ﻣﺸﺨﺺ ﺷﻮد ﺗﻤﺎم وارده ﻫﺎ ﺗﺎ اوﻟﻴﻦ وارده ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﭘﺮدازش ﻣﺤﺪوده اﻳﻦ
روﻳﻪ ﺧﺎﺗﻤﻪ ﻣﻲ ﻳﺎﺑﺪ ،ﻣﻲ ﺗﻮاﻧﺪ از ﺟﺪول ﻧﻤﺎد ﻓﻌﺎل ﺣﺬف ﮔﺮدد .ﺟﺪول در ﻫﻢ ﺷﺎﻣﻞ m
ﻟﻴﺴﺖ ،ﺑﺎ اﺳﺘﻔﺎده از آراﻳﻪ دﺳﺘﻴﺎﺑﻲ ﻣﻲ ﺷﻮد .ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﻳﻨﻜﻪ ﻳﻚ ﻧﺎم ﻫﻤﻴﺸﻪ ﺑﺎ اﺳﺘﻔﺎده از
ﺗﺎﺑﻊ درﻫﻢ ﺑﻪ ﻟﻴﺴﺖ ﻣﻨﺘﻘﻞ ﻣﻲ ﺷﻮد ﻫﺮ ﻳﻚ از ﻓﻬﺮﺳﺖ ﻫﺎ ﻣﺸﺎﺑﻪ ﺷﻜﻞ ﻓﻮق ﻧﮕﻬﺪاري ﻣﻲ
ﺷﻮد .ﺑﻪ ﻫﺮ ﺣﺎل ﺑﺮاي ﭘﻴﺎده ﺳﺎزي ﻋﻤﻞ deleteﻧﻴﺎزي ﺑﻪ ﭘﻮﻳﺶ ﺗﻤﺎم ﺟﺪول در ﻫﻢ ﺑﺮاي
ﻳﺎﻓﺘﻦ ﻟﻴﺴﺘﻲ ﺷﺎﻣﻞ وارده ﻫﺎﻳﻲ ﻛﻪ ﺑﺎﻳﺪ ﺣﺬف ﺷﻮد ﻧﻤﻲ ﺑﺎﺷﺪ .روش زﻳﺮ ﻣﻲ ﺗﻮاﻧﺪ ﻣﻮرد
اﺳﺘﻔﺎده ﻗﺮار ﮔﻴﺮد .ﻓﺮض ﻛﻨﻴﺪ ﻫﺮ وارده داراي دو اﺗﺼﺎل اﺳﺖ :
-1ﻳﻚ اﺗﺼﺎل در ﻫﻢ ﻛﻪ اﻳﻦ وارده را ﺑﻪ وارده ﻫﺎي دﻳﮕﺮي ﻛﻪ ﻧﺎم آﻧﻬﺎ ﺗﻮﺳﻂ ﺗﺎﺑﻊ در ﻫﻢ
-2ﻳﻚ اﺗﺼﺎل ﻣﺤﺪوده ﻛﻪ ﺗﻤﺎم وارده ﻫﺎﻳﻲ را ﻛﻪ درﻣﺤﺪوده ﻣﺸﺎﺑﻪ ﻗﺮار دارﻧﺪ ﺑﻪ ﺻﻮرت
اﮔﺮ اﺗﺼﺎل ﻣﺤﺪوده در زﻣﺎن ﺣﺬف ﻳﻚ وارده از ﺟﺪول در ﻫﻢ دﺳﺖ ﻧﺨﻮرده ﺑﺎﻗﻲ ﺑﻤﺎﻧﺪ ،
زﻧﺠﻴﺮي ﻛﻪ ﺑﺎ اﺗﺼﺎﻻت ﻣﺤﺪوده ﺗﺸﻜﻴﻞ ﺷﺪه اﺳﺖ ،ﺟﺪول ﻧﻤﺎد ﻣﺠﺰاﻳﻲ را ﺑﺮاي ﻣﺤﺪوده
١١١
ﺣﺬف وارده از ﺟﺪول در ﻫﻢ ﺑﺎﻳﺪ ﺑﺎ دﻗﺖ اﻧﺠﺎم ﮔﻴﺮد ،زﻳﺮا ﺣﺬف ﻳﻚ وارده ﺑﺮ ﻣﻘﺪار
ﻗﺒﻠﻲ آن در ﻓﻬﺮﺳﺖ ﺗﺎﺛﻴﺮ دارد .ﺑﺨﺎﻃﺮ آورﻳﺪ ﻛﻪ ﺣﺬف وارده ﺷﻤﺎره iﺑﺎ ﺑﺮﻗﺮاري ارﺗﺒﺎط
وارده ﺷﻤﺎره i-1ﺑﺎ وارده ﺷﻤﺎره i+1اﻧﺠﺎم ﻣﻲ ﮔﻴﺮد ﺑﻨﺎﺑﺮاﻳﻦ ﺗﻨﻬﺎ اﺳﺘﻔﺎده از اﺗﺼﺎﻻت
ﻣﺤﺪوده ﺑﺮاي ﻳﺎﻓﺘﻦ وارد ﺷﻤﺎره iﻛﺎﻓﻲ ﻧﻴﺴﺖ .اﮔﺮ اﺗﺼﺎﻻت در ﻫﻢ ﻳﻚ ﻟﻴﺴﺖ ﭘﻴﻮﻧﺪي
ﺣﻠﻘﻮي را ﺗﺸﻴﻜﻞ دﻫﻨﺪ ﻛﻪ درآن آﺧﺮﻳﻦ وارده ﺑﻪ اوﻟﻴﻦ وارده اﺷﺎره ﻧﻤﺎﻳﺪ ،وارده i-1ام
ﻧﻴﺰ ﻣﻲ ﺗﻮاﻧﺪ ﻳﺎﻓﺖ ﺷﻮد .در ﻣﻘﺎﺑﻞ ﻣﻲ ﺗﻮان از ﭘﺸﺘﻪ ﺑﻪ ﻣﻨﻈﻮر ﭘﻲ ﮔﻴﺮي ﺗﻌﻴﻴﻦ ﻟﻴﺴﺖ ﻫﺎﻳﻲ
ﺷﺎﻣﻞ وارده ﻫﺎﻳﻲ ﻛﻪ ﺑﺎﻳﺪ ﺣﺬف ﺷﻮﻧﺪ اﺳﺘﻔﺎده ﻧﻤﻮد .ﻫﻨﮕﺎﻣﻲ ﻛﻪ روﻳﻪ ﺟﺪﻳﺪي ﭘﻮﻳﺶ ﻣﻲ
ﺷﻮد ،ﻳﻚ ﻋﻼﻣﺖ در ﭘﺸﺘﻪ ﻗﺮار ﻣﻲ ﮔﻴﺮد .در ﺑﺎﻻي ﻋﻼﻣﺖ ،ﺷﻤﺎره ﻟﻴﺴﺖ ﻫﺎﻳﻲ ﺷﺎﻣﻞ
وارده ﻫﺎﻳﻲ ﺑﺮاي ﻧﺎم ﻫﺎي اﻋﻼن ﺷﺪه در اﻳﻦ روﻳﻪ ﻗﺮار دارﻧﺪ .ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﭘﺮدازش اﻳﻦ
روﻳﻪ ﺧﺎﺗﻤﻪ ﻣﻲ ﻳﺎﺑﺪ ،اﻋﺪاد ﻓﻬﺮﺳﺖ ﻣﻲ ﺗﻮاﻧﻨﺪ از ﭘﺸﺘﻪ ﺧﺎرج ﺷﻮﻧﺪ ﺗﺎ زﻣﺎﻧﻲ ﻛﻪ ﻋﻼﻣﺖ آن
روﻳﻪ ﻓﺮاﺑﺮﺳﺪ.
١١٢