0% found this document useful (0 votes)
20 views112 pages

Compiler Jozve

Uploaded by

saeed khatami
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views112 pages

Compiler Jozve

Uploaded by

saeed khatami
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 112

‫جزوه اصول طراحی کامپایلر‬

‫مدرس ‪:‬فرشته رضائی‬


‫ﻓﻬﺮﺳﺖ ﻣﻄﺎﻟﺐ‬
‫ﺷﻤﺎره ﺻﻔﺤﻪ‬ ‫ﻋﻨﻮان‬
‫ﺗﻌﺮﻳﻒ ﻛﺎﻣﭙﺎﻳﻠﺮ ‪4 ........................................................................................................‬‬
‫ﻣﺮاﺣﻞ ﻛﺎﻣﭙﺎﻳﻞ ‪4.... .....................................................................................................‬‬
‫ﺧﻄﺎﭘﺮداز ‪7 .................................................................................................................‬‬
‫اﺑﺘﺪا و اﻧﺘﻬﺎي ﻛﺎﻣﭙﺎﻳﻠﺮﻫﺎ ‪7 ............................................................................................‬‬
‫ﺗﺤﻠﻴﻞ واژه اي ‪10 ..........................................................................................................‬‬
‫اﻟﮕﻮ و واژه ﺗﻮﻛﻦ ﻫﺎ ‪11 .................................................................................................‬‬
‫ﺧﻄﺎﻫﺎي واژه اي ‪12 ......................................................................................................‬‬
‫روﺷﻬﺎﻳﻲ ﺟﻬﺖ ﺑﻬﺒﻮد ﻛﺎر اﺳﻜﻨﺮ ‪13...............................................................................‬‬
‫دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل ‪16 ...................................................................................................‬‬
‫ﺗﺤﻠﻴﻞ ﻧﺤﻮي ‪23 ...........................................................................................................‬‬
‫ﺗﺠﺰﻳﻪ ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ‪25 ....................................................................................................‬‬
‫ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻨﮕﺮد ‪28 .........................................................................................................‬‬
‫اﺳﺘﻔﺎده از ﻗﺎﻋﺪه اﭘﺴﻴﻠﻮن ‪31...........................................................................................‬‬
‫ﻣﺸﻜﻞ ﭼﭗ ﮔﺮدي ‪32 ..................................................................................................‬‬
‫ﺣﺬف ﭼﭗ ﮔﺮدي ﺿﻤﻨﻲ ‪33 .......................................................................................‬‬
‫ﻓﺎﻛﺘﻮرﮔﻴﺮي از ﭼﭗ ‪34 ................................................................................................‬‬
‫زﺑﺎﻧﻬﺎي ﻏﻴﺮﻣﺴﺘﻘﻞ از ﻣﺘﻦ ‪35..........................................................................................‬‬
‫اﺳﺘﻔﺎده از دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل ﺑﺮاي ﭘﻴﺎده ﺳﺎزي ﭘﺎرﺳﺮﻫﺎي ﭘﻴﺸﮕﻮ ‪38 ..............................‬‬
‫ﺗﺠﺰﻳﻪ ﭘﻴﺸﮕﻮﻳﺎﻧﻪ ﻏﻴﺮﺑﺎزﮔﺸﺘﻲ ‪42 ..................................................................................‬‬
‫ﺗﻮاﺑﻊ ‪ First‬و ‪43 ....................................................................................... Follow‬‬
‫ﮔﺮاﻣﺮﻫﺎي )‪47 ................................................................................................. LL(1‬‬
‫روﺷﻬﺎي اﺻﻼح ﺧﻄﺎي ﻧﺤﻮي در روش ﺗﺠﺰﻳﻪ )‪49 ........................................... LL(1‬‬
‫ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ‪51 ...................................................................................................‬‬
‫ﭘﻴﺎده ﺳﺎزي روش ﺗﺠﺰﻳﻪ اﻧﺘﻘﺎل – ﻛﺎﻫﺶ ﺑﺎ اﺳﺘﻔﺎده از ﻳﻚ اﻧﺒﺎره ‪53 ...............................‬‬

‫‪٢‬‬
‫ﺷﻤﺎره ﺻﻔﺤﻪ‬ ‫ﻋﻨﻮان‬
‫اﻧﻮاع ﺗﺪاﺧﻞ در ﺗﺠﺰﻳﻪ اﻧﺘﻘﺎل – ﻛﺎﻫﺶ ‪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‬ﺗﺮﺟﻤﻪ ﻣﻲ ﻛﻨﺪ‪.‬‬

‫‪Source Program‬‬ ‫‪Compiler‬‬ ‫‪Target Program‬‬

‫‪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

Symbol-Table Error Handler


Manager
Intermediate Code Generation

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‬و ﻧﻮع ﻧﺘﻴﺠﻪ اي ﻛﻪ روﻳﻪ ﻫﺎ‬
‫ﺑﺎز ﻣﻲ ﮔﺮداﻧﻨﺪ ﺑﺎﺷﺪ‪.‬‬
‫در ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ ﺑﻪ ازاي ﻫﺮ ﺷﻨﺎﺳﻪ ﻳﻚ رﻛﻮرد وﺟﻮد دارد ﻛﻪ اﻳﻦ رﻛﻮردﻫﺎ ﺷﺎﻣﻞ‬
‫ﻣﺸﺨﺼﺎت ﺷﻨﺎﺳﻪ ﻫﺎ ﻣﻲ ﺑﺎﺷﻨﺪ‪.‬اﻳﻦ ﺟﺪول اﻣﻜﺎن دﺳﺘﻴﺎﺑﻲ ﺳﺮﻳﻊ ﺑﻪ ﺷﻨﺎﺳﻪ ﻫﺎ و ﻣﺸﺨﺼﺎت‬
‫آﻧﻬﺎ را ﺑﻪ ﻣﺎ ﻣﻲ دﻫﺪ‪.‬در ﻛﺎﻣﭙﺎﻳﻠﺮ و در ﻣﺮﺣﻠﻪ ﺗﺤﻠﻴﻞ ﻟﻐﻮي ﻛﻠﻴﻪ ﺷﻨﺎﺳﻪ ﻫﺎي ﻣﻮﺟﻮد در‬
‫ﺑﺮﻧﺎﻣﻪ اﺻﻠﻲ وارد ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ ﻣﻲ ﺷﻮﻧﺪ‪.‬در ﻣﺮﺣﻠﻪ ﻫﺎي دﻳﮕﺮ ﻛﺎﻣﭙﺎﻳﻞ اﻳﻦ اﻃﻼﻋﺎت ﺑﻪ‬
‫ﺟﺪول اﺿﺎﻓﻪ ﺧﻮاﻫﻨﺪ ﺷﺪ و ﺳﭙﺲ از آﻧﻬﺎ در ﻣﻮارد ﻣﺨﺘﻠﻒ اﺳﺘﻔﺎده ﺧﻮاﻫﺪ ﺷﺪ‪.‬‬

‫‪ 3-1‬اﺑﺘﺪا ) ‪ ( Front-End‬و اﻧﺘﻬﺎي ) ‪ ( Back-End‬ﻛﺎﻣﭙﺎﻳﻠﺮﻫﺎ‬


‫ﺑﻪ ﭼﻬﺎر ﻣﺮﺣـﻠﻪ اول ﻛﺎﻣﭙــﺎﻳﻞ و ﺑﺨﺸﻲ از ﻣﺮﺣﻠﻪ ﺑﻬــﻴﻨﻪ ﺳﺎزي ﻛﻪ ﺑﺴﺘـــﮕﻲ ﺑﻪ زﺑــﺎن ﻣﻨــﺒﻊ‬
‫) ‪ ( source Program‬دارد و ﻣﺴﺘﻘﻞ از ﻣﺎﺷﻴﻦ اﺳﺖ ‪ Front-End‬ﻛﺎﻣﭙﺎﻳﻠﺮ ﮔﻮﻳﻨﺪ‪.‬ﺑﻪ‬
‫ﺑﺨﺸﻲ از ﻣﺮﺣﻠﻪ ﺑﻬﻴﻨﻪ ﺳﺎزي و ﻣﺮﺣﻠﻪ آﺧﺮ ﻛﺎﻣﭙﺎﻳﻞ ﻛﻪ واﺑﺴﺘﻪ ﺑﻪ ﻣﺎﺷﻴﻦ ﻣﻘﺼﺪ اﺳﺖ‬
‫‪ Back-End‬ﻛﺎﻣﭙﺎﻳﻠﺮ ﻣﻲ ﮔﻮﻳﻨﺪ‪.‬ﻣﻌﻤﻮﻻ اﻳﻦ ﺑﺨﺶ از ﻛﺎﻣﭙﺎﻳﻠﺮ واﺑﺴﺘﻪ ﺑﻪ زﺑﺎن ﻣﻨﺒﻊ‬
‫ﻧﻴﺴﺖ‪.‬ﺷﻜﻞ ﺑﻌﺪ ﺗﺮﺟﻤﻪ دﺳﺘﻮر ‪ p:=i+r*60‬و ﺗﺎﺛﻴﺮ ﻫﺮ ﻣﺮﺣﻠﻪ از ﻛﺎﻣﭙﺎﻳﻠﺮ ﺑﺮ روي اﻳﻦ‬
‫دﺳﺘﻮر را ﻧﺸﺎن ﻣﻲ دﻫﺪ‪ i , p , r ).‬ﻫﻤﮕﻲ از ﻧﻮع ‪ real‬ﻫﺴﺘﻨﺪ(‬

‫‪٧‬‬
p := i + r * 60

Lexical Analyzer

id1 := id2 + id3 * 60

Syntax Analyzer

:=

id1 +

id2
*

id3 60

Semantic Analyzer

:=

id1 +

٨
id2 *

id3 inttoreal

60

Intermediate Code Generation

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‬‬

‫‪ -2‬ﺗﺤﻠﻴﻞ واژه اي ) ‪( Lexical Analysis‬‬


‫ﻧﺨﺴﺘﻴﻦ ﻣﺮﺣﻠﻪ ﻛﺎﻣﭙﺎﻳﻞ ﺗﺤﻠﻴﻞ واژه اي اﺳﺖ‪.‬ﺑﻪ واﺣﺪي از ﻛﺎﻣﭙﺎﻳﻠﺮ ﻛﻪ ﻛﺎر ﺗﺤﻠﻴﻞ واژه اي‬
‫را اﻧﺠﺎم ﻣﻲ دﻫﺪ اﺳﻜﻨﺮ )‪ ( Scanner‬ﻣﻲ ﮔﻮﻳﻨﺪ‪.‬اﺳﻜﻨﺮ ﺑﻴﻦ رﺷﺘﻪ ورودي و ﺗﺤﻠﻴﻠﮕﺮ‬
‫ﻧﺤﻮي ﻳﺎ ﭘﺎرﺳﺮ ) ‪ ( Parser‬واﻗﻊ اﺳﺖ‪.‬وﻇﻴﻔﻪ اﺻﻠﻲ اﺳﻜﻨﺮ اﻳﻦ اﺳﺖ ﻛﻪ ﺑﺎ ﺧﻮاﻧﺪن‬
‫ﻛﺎراﻛﺘﺮﻫﺎي ورودي ‪ ,‬ﺗﻮﻛﻦ ﻫﺎ را ﺗﺸﺨﻴﺺ داده و ﺑﺮاي ﭘﺎرﺳﺮ ارﺳﺎل ﻧﻤﺎﻳﺪ‪.‬راﺑﻄﻪ اﺳﻜﻨﺮ و‬
‫ﭘﺎرﺳﺮ ﺑﺼﻮرت زﻳﺮ اﺳﺖ ‪:‬‬

‫‪Read character‬‬ ‫& ‪Pass Token‬‬


‫‪Attribute‬‬

‫‪Input‬‬ ‫‪Scanner‬‬ ‫‪Parser‬‬

‫‪Put Back‬‬ ‫‪Get next‬‬


‫‪Character‬‬ ‫‪Token‬‬

‫‪Symbol‬‬
‫‪Table‬‬

‫ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل در ﺻﻮرﺗﻲ ﻛﻪ رﺷﺘﻪ ورودي ‪ A := B + C‬ﺑﺎﺷﺪ ﺗﻮﻛﻦ ﻫﺎي زﻳﺮ ﺗﺸﺨﻴﺺ‬
‫داده ﺧﻮاﻫﻨﺪ ﺷﺪ ‪:‬‬

‫‪١٠‬‬
‫آدرس ‪ < id , C‬و > ‪ < add. Op.‬و > آدرس ‪ < id , B‬و > ‪ < ass. Op.‬و > آدرس ‪< id , A‬‬
‫ﺑﻨﺎﺑﺮاﻳﻦ اﺳﻜﻨﺮ ﻋﻼوه ﺑﺮ اﻳﻦ ﻛﻪ ﺗﺸﺨﻴﺺ ﻣﻲ دﻫﺪ ﻛﻪ ﺗﻮﻛﻦ ﻳﻚ ﺷﻨﺎﺳﻪ اﺳﺖ آدرس آن‬
‫در ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ را ﻧﻴﺰ ﺑﺮاي ﭘﺎرﺳﺮ ﻣﻲ ﻓﺮﺳﺘﺪ‪.‬ﻋﻼوه ﺑﺮ اﻳﻦ اﺳﻜﻨﺮ ﻣﻲ ﺗﻮاﻧﺪ ﻣﺤﻞ ﻫﺎي‬
‫ﺧﺎﻟﻲ و ﺗﻮﺿﻴﺤﺎت ) ‪ ( Comments‬ﻣﻮﺟﻮد در ﺑﺮﻧﺎﻣﻪ اﺻﻠﻲ را ﺿﻤﻦ ﺧﻮاﻧﺪن ﺑﺮﻧﺎﻣﻪ‬
‫ﺣﺬف ﻧﻤﺎﻳﺪ‪.‬‬
‫ﺑﻪ آﺧﺮﻳﻦ ﺗﻮﻛﻨﻲ ﻛﻪ اﺳﻜﻨﺮ ﻳﺎﻓﺘﻪ اﺳﺖ ﻋﻼﻣﺖ ﭘﻴﺶ ﺑﻴﻨﻲ ) ‪ ( Lookahead Symbol‬و‬
‫ﻳﺎ ﺗﻮﻛﻦ ﺟﺎري ﮔﻔﺘﻪ ﻣﻲ ﺷﻮد‪.‬‬

‫‪ 1-2‬اﻟﮕﻮ )‪ ( Pattern‬و واژه )‪ ( Lexeme‬ﺗﻮﻛﻦ ﻫﺎ‬


‫ﺑﻪ ﻓﺮم ﻛﻠﻲ ﻛﻪ ﻳﻚ ﺗﻮﻛﻦ ﻣﻲ ﺗﻮاﻧﺪ داﺷﺘﻪ ﺑﺎﺷﺪ اﻟﮕﻮي آن ﺗﻮﻛﻦ ﻣﻲ ﮔﻮﻳﻨﺪ‪.‬ﺑﻪ ﻋﺒﺎرت‬
‫دﻳﮕﺮ در ورودي رﺷﺘﻪ ﻫﺎﻳﻲ وﺟﻮد دارﻧﺪ ﻛﻪ ﺗﻮﻛﻦ ﻳﻜﺴﺎﻧﻲ ﺑﺮاي آﻧﻬﺎ ﺗﺸﺨﻴﺺ داده ﻣﻲ‬
‫ﺷﻮد ‪.‬ﻓﺮم ﻛﻠﻲ اﻳﻦ رﺷﺘﻪ ﻫﺎ ﺗﻮﺳﻂ اﻟﮕﻮي آن ﺗﻮﻛﻦ ﺗﻮﺻﻴﻒ ﻣﻲ ﺷﻮد‪.‬‬
‫ﺑﻪ دﻧﺒﺎﻟﻪ اي از ﻧﻮﻳﺴﻪ ﻫﺎ ﻛﻪ ﺗﺸﻜﻴﻞ ﻳﻚ ﺗﻮﻛﻦ ﻣﻲ دﻫﻨﺪ واژه ) ‪ ( Lexeme‬آن ﺗﻮﻛﻦ ﻣﻲ‬
‫ﮔﻮﻳﻨﺪ‪.‬ﺟﺪول زﻳﺮ ﺣﺎوي ﭼﻨﺪ ﻧﻤﻮﻧﻪ اﻟﮕﻮ و واژه اﺳﺖ ‪:‬‬

‫‪Token‬‬ ‫‪Lexeme‬‬ ‫‪Pattern‬‬


‫‪Const‬‬ ‫‪Const‬‬ ‫‪Const‬‬
‫‪if‬‬ ‫‪if‬‬ ‫‪if‬‬
‫‪relation‬‬ ‫>< ‪< , > , <= , >= , <> , = < or > or <= or >= or‬‬
‫= ‪or‬‬
‫‪id‬‬ ‫‪pi‬‬ ‫ﺑﺎ ﺣﺮوف اﻟﻔﺒﺎ ﺷﺮوع و ﺑﺪﻧﺒﺎل آن‬
‫ﺣﺮف و رﻗﻢ ﻗﺮار ﻣﻲ ﮔﻴﺮد‬
‫‪Num‬‬ ‫‪3.1416‬‬ ‫ﻫﺮ ﺛﺎﺑﺖ ﻋﺪدي‬
‫‪literal‬‬ ‫"‪"core dumped‬‬ ‫ﻫﺮ رﺷﺘﻪ ﻧﻮﻳﺴﻪ اي ﻛﻪ ﺑﻴﻦ دو‬
‫ﻋﻼﻣﺖ '' ﻗﺮار ﮔﻴﺮد‬

‫‪١١‬‬
‫در ﺑﻌﻀﻲ وﺿﻌﻴﺖ ﻫﺎ اﺳﻜﻨﺮ ﻗﺒﻞ از اﻳﻨﻜﻪ ﺗﺼﻤﻴﻢ ﺑﮕﻴﺮد ﭼﻪ ﺗﻮﻛﻨﻲ را ﺑﻪ ﭘﺎرﺳﺮ ﺑﻔﺮﺳﺘﺪ ﻧﻴﺎز‬
‫دارد ﻛﻪ ﭼﻨﺪ ﻛﺎراﻛﺘﺮ دﻳﮕﺮ ﻧﻴﺰ از ورودي ﺑﺨﻮاﻧﺪ‪.‬ﺑﻌﻨﻮان ﻣﺜﺎل اﺳﻜﻨﺮ ﺑﺎ دﻳﺪن ﻋﻼﻣﺖ ' > '‬
‫در ورودي ﻧﻴﺎز دارد ﻛﻪ ﻛﺎراﻛﺘﺮ ورودي ﺑﻌﺪي را ﻧﻴﺰ ﺑﺨﻮاﻧﺪ در ﺻﻮرﺗﻲ ﻛﻪ اﻳﻦ ﻛﺎراﻛﺘﺮ =‬
‫ﺑﺎﺷﺪ ﺗﻮﻛﻦ ' => ' و در ﻏﻴﺮ اﻳﻨﺼﻮرت ﺗﻮﻛﻦ ' > ' ﺗﺸﺨﻴﺺ داده ﻣﻲ ﺷﻮد‪.‬در ﻣﻮرد آﺧﺮ‬
‫ﺑﺎﻳﺪ ﻛﺎراﻛﺘﺮ اﺿﺎﻓﻲ ﺧﻮاﻧﺪه ﺷﺪه دوﺑﺎره ﺑﻪ ورودي ﺑﺎزﮔﺮداﻧﺪه ﺷﻮد‪.‬‬
‫ﻳﻜﻲ دﻳﮕﺮ از ﻣﺸﻜﻼﺗﻲ ﻛﻪ اﺳﻜﻨﺮ ﺑﺎ آن روﺑﺮو اﺳﺖ در زﺑﺎﻧﻬﺎﻳﻲ ﻧﻈﻴﺮ ‪ 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‬ﺑﺴﻴﺎر ﻣﺸﻜﻞ اﺳﺖ‪.‬در اﻳﻦ ﻣﻮارد ﻣﻌﻤﻮﻻ‬
‫ﭘﺎرﺳﺮ ﺗﺸﺨﻴﺺ ﻧﻬﺎﻳﻲ را ﺧﻮاﻫﺪ داد‪.‬‬

‫‪ 2-2‬ﺧﻄﺎﻫﺎي واژه اي ) ‪( Lexical Errors‬‬


‫ﺑﻄﻮر ﻛﻠﻲ ﺧﻄﺎﻫﺎي ﻣﺤﺪودي را اﺳﻜﻨﺮ ﻣﻲ ﺗﻮاﻧﺪ ﺑﻴﺎﺑﺪ زﻳﺮا اﺳﻜﻨﺮ ﺗﻤﺎم ﺑﺮﻧﺎﻣﻪ ورودي را‬
‫ﻳﻜﺠﺎ ﻧﻤﻲ ﺑﻴﻨﺪ ﺑﻠﻜﻪ ﻫﺮ ﺑﺎر ﻗﺴﻤﺖ ﻛﻮﭼﻜﻲ از ﺑﺮﻧﺎﻣﻪ ﻣﻨﺒﻊ را ﻣﻲ ﺑﻴﻨﺪ‪.‬ﺑﻌﻨﻮان ﻣﺜﺎل ﻫﺮﮔﺎه‬
‫رﺷﺘﻪ ‪ fi‬در ﻳﻚ ﺑﺮﻧﺎﻣﻪ ‪ C‬ﺑﺮاي ﺑﺎر اول ﻣﺸﺎﻫﺪه ﺷﻮد اﺳﻜﻨﺮ ﻗﺎدر ﻧﻴﺴﺖ ﺗﺸﺨﻴﺺ دﻫﺪ ﻛﻪ‬
‫آﻳﺎ ‪ fi‬ﻳﻚ اﻣﻼي ﻏﻠﻂ از ﻛﻠﻤﻪ ﻛﻠﻴﺪي ‪ if‬اﺳﺖ ﻳﺎ ﻳﻚ ﻣﺘﻐﻴﺮ‪.‬‬
‫))‪fi(x = = f(x‬‬

‫‪١٢‬‬
‫از آﻧﺠﺎﻳﻲ ﻛﻪ ‪ fi‬ﻳﻚ ﻣﺘﻐﻴﺮ ﻣﺠﺎز اﺳﺖ اﺳﻜﻨﺮ اﻳﻦ ﺗﻮﻛﻦ را ﺑﻪ ﻋﻨﻮان ﻳﻚ ﺷﻨﺎﺳﻪ ﺑﻪ ﭘﺎرﺳﺮ‬
‫ﻣﻲ ﻓﺮﺳﺘﺪ ﺗﺎ اﻳﻨﻜﻪ ﭘﺎرﺳﺮ در اﻳﻦ ﻣﻮرد ﺗﺼﻤﻴﻢ ﺑﮕﻴﺮد‪.‬اﻣﺎ ﻣﻤﻜﻦ اﺳﺖ ﺧﻄﺎﻫﺎﻳﻲ ﭘﻴﺶ ﺑﻴﺎﻳﺪ‬
‫ﻛﻪ اﺳﻜﻨﺮ ﻗﺎدر ﺑﻪ اﻧﺠﺎم ﻫﻴﭻ ﻋﻤﻠﻲ ﻧﺒﺎﺷﺪ‪.‬در اﻳﻦ ﺣﺎﻟﺖ ﺑﺮﻧﺎﻣﻪ ﺧﻄﺎﭘﺮداز )‪Error-‬‬
‫‪ ( Handler‬ﻓﺮاﺧﻮاﻧﺪه ﻣﻲ ﺷﻮد ﺗﺎ آن ﺧﻄﺎ را ﺑﻪ ﻧﺤﻮي ﺑﺮﻃﺮف ﻛﻨﺪ‪.‬روﺷﻬﺎي ﻣﺨﺘﻠﻔﻲ‬
‫ﺑﺮاي اﻳﻨﻜﺎر وﺟﻮد دارد ﻛﻪ ﺳﺎده ﺗﺮﻳﻦ آﻧﻬﺎ روﺷﻲ ﻣﻮﺳﻮم ﺑﻪ " ‪" Panic Mode‬‬
‫اﺳﺖ‪.‬در اﻳﻦ روش آﻧﻘﺪر از رﺷﺘﻪ ورودي ﺣﺬف ﻣﻲ ﺷﻮد ﺗﺎ اﻳﻨﻜﻪ ﻳﻚ ﺗﻮﻛﻦ درﺳﺖ‬
‫ﺗﺸﺨﻴﺺ داده ﺷﻮد‪.‬‬
‫ﺳﺎﻳﺮ روﺷﻬﺎي ﺗﺼﺤﻴﺢ ﺧﻄﺎ ) ‪ ( Error-Recovery‬ﻋﺒﺎرﺗﻨﺪ از ‪:‬‬
‫‪ -1‬ﺣﺬف ﻳﻚ ﻛﺎراﻛﺘﺮ ﻏﻴﺮﻣﺠﺎز ) ﺗﺒﺪﻳﻞ =‪ :$‬ﺑﻪ =‪( :‬‬
‫‪ -2‬وارد ﻛﺮدن ﻳﻚ ﻛﺎراﻛﺘﺮ ﮔﻢ ﺷﺪه ) ﺗﺒﺪﻳﻞ ‪ :‬ﺑﻪ =‪( :‬‬
‫‪ -3‬ﺗﻌﻮﻳﺾ ﻛﺮدن ﻳﻚ ﻛﺎراﻛﺘﺮ ﻏﻠﻂ ﺑﺎ ﻳﻚ ﻛﺎراﻛﺘﺮ درﺳﺖ ) ﺗﺒﺪﻳﻞ ‪ ::‬ﺑﻪ =‪( :‬‬
‫‪ -4‬ﺟﺎﺑﺠﺎ ﻛﺮدن دو ﻛﺎراﻛﺘﺮ ﻣﺠﺎز ) ﺗﺒﺪﻳﻞ ‪ =:‬ﺑﻪ =‪( :‬‬

‫‪ 3-2‬روﺷﻬﺎﻳﻲ ﺟﻬﺖ ﺑﻬﺒﻮد ﻛﺎر اﺳﻜﻨﺮ‬


‫‪ 1-3-2‬اﺳﺘﻔﺎده از ﺑﺎﻓﺮ‬
‫در ﺑﺴﻴﺎري از زﺑﺎﻧﻬﺎ اﺳﻜﻨﺮ ﺑﺮاي ﺗﺸﺨﻴﺺ ﻧﻬﺎﻳﻲ ﺗﻮﻛﻦ ﻫﺎ و ﻣﻄﺎﺑﻘﺖ دادن آن ﺑﺎ اﻟﮕﻮﻫﺎي‬
‫ﻣﻮﺟﻮد ﻧﻴﺎز دارد ﻛﻪ ﭼﻨﺪ ﻛﺎراﻛﺘﺮ ﺟﻠﻮﺗﺮ را ﻧﻴﺰ ﻣﻮرد ﺑﺮرﺳﻲ ﻗﺮار دﻫﺪ‪.‬از آﻧﺠﺎﻳﻲ ﻛﻪ ﻣﻘﺪار‬
‫زﻳﺎدي از زﻣﺎن در ﺟﺎﺑﺠﺎﻳﻲ ﻛﺎراﻛﺘﺮﻫﺎ ﺳﭙﺮي ﻣﻲ ﺷﻮد از ﺗﻜﻨﻴﻚ ﻫﺎي ﺑﺎﻓﺮﻳﻨﮓ ﺑﺮاي‬
‫ﭘﺮدازش ﻛﺎراﻛﺘﺮﻫﺎي ورودي اﺳﺘﻔﺎده ﻣﻲ ﮔﺮدد‪.‬‬
‫در ﻳﻜﻲ از اﻳﻦ ﺗﻜﻨﻴﻚ ﻫﺎ از ﻳﻚ ﺑﺎﻓﺮ ﻛﻪ ﺑﻪ دو ﻧﻴﻤﻪ ‪ N‬ﻛﺎراﻛﺘﺮي ﺗﻘﺴﻴﻢ ﺷﺪه اﺳﺖ‬
‫اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﻨﺪ ﻛﻪ در آن ‪ N‬ﺗﻌﺪاد ﻛﺎراﻛﺘﺮﻫﺎﻳﻲ اﺳﺖ ﻛﻪ در روي ﻳﻚ ﺑﻠﻮك از‬
‫دﻳﺴﻚ ﺟﺎي ﻣﻲ ﮔﻴﺮﻧﺪ‪.‬ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﺑﺎ ﻳﻚ ﻓﺮﻣﺎن ‪ read‬ﺑﺠﺎي ﺧﻮاﻧﺪن ﻳﻚ ﻛﺎراﻛﺘﺮ ﻣﻲ‬
‫ﺗﻮان ‪ N‬ﻛﺎراﻛﺘﺮ ورودي را در ﻫﺮ ﻧﻴﻤﻪ ﺑﺎﻓﺮ وارد ﻛﺮد‪.‬درﺻﻮرﺗﻴﻜﻪ ورودي ﺷﺎﻣﻞ ﺗﻌﺪاد‬
‫ﻛﺎراﻛﺘﺮﻫﺎي ﻛﻤﺘﺮ از ‪ N‬ﺑﺎﺷﺪ ﺑﻌﺪ از ﺧﻮاﻧﺪن اﻳﻦ ﻛﺎراﻛﺘﺮﻫﺎ ﻳﻚ ﻛﺎراﻛﺘﺮ ﺧﺎص ‪ eof‬ﻧﻴﺰ‬

‫‪١٣‬‬
‫وارد ﺑﺎﻓﺮ ﻣﻲ ﮔﺮدد‪.‬ﻛﺎراﻛﺘﺮ ‪ eof‬ﺑﻴﺎﻧﮕﺮ ﭘﺎﻳﺎن ﻓﺎﻳﻞ ﻣﻨﺒﻊ ﺑﻮده و ﺑﺎ ﺳﺎﻳﺮ ﻛﺎراﻛﺘﺮﻫﺎي ورودي‬
‫ﺑﻪ ﻧﻮﻋﻲ ﺗﻔﺎوت دارد‪.‬‬

‫‪E‬‬ ‫=‬ ‫‪M‬‬ ‫*‬ ‫‪C‬‬ ‫*‬ ‫*‬ ‫‪2‬‬ ‫‪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‬ﻣﻲ ﻛﻨﻴﻢ‪.‬‬

‫‪ 2-3-2‬اﺳﺘﻔﺎده از ﻧﮕﻬﺒﺎﻧﻬﺎ ) ‪( Sentinels‬‬


‫در ﺣﺎﻟﺖ ﻗﺒﻞ ﺑﺎ ﺟﻠﻮرﻓﺘﻦ ﻧﺸﺎﻧﻪ روي ‪ Forward‬ﺑﺎﻳﺪ ﭼﻚ ﻣﻲ ﺷﺪ ﻛﻪ آﻳﺎ اﻳﻦ ﻧﺸﺎﻧﻪ رو ﺑﻪ‬
‫اﻧﺘﻬﺎي ﻳﻚ ﻧﻴﻤﻪ از ﺑﺎﻓﺮ رﺳﻴﺪه اﺳﺖ ﻳﺎ ﺧﻴﺮ ‪.‬در ﺻﻮرﺗﻴﻜﻪ ﺑﻪ اﻧﺘﻬﺎي ﻳﻚ ﻧﻴﻤﻪ ﺑﺎﻓﺮ رﺳﻴﺪه‬
‫ﺑﺎﺷﺪ ﺑﺎﻳﺪ ﻧﻴﻤﻪ دﻳﮕﺮ را دوﺑﺎره ﺑﺎر ﻣﻲ ﻛﺮدﻳﻢ‪.‬در اﻟﮕﻮرﻳﺘﻢ ﻓﻮق ﻫﻤﺎن ﻃﻮرﻳﻜﻪ ﻣﺸﺎﻫﺪه ﺷﺪ‬
‫ﺑﺮاي ﻫﺮ ﺟﻠﻮروي ﻧﺸﺎﻧﻪ روي ‪ Forward‬دو ﻋﻤﻞ ﻣﻘﺎﻳﺴﻪ اﻧﺠﺎم ﻣﻲ ﺷﻮد‪.‬ﻣﻲ ﺗﻮان اﻳﻦ دو‬
‫را ﺑﻪ ﻳﻚ ﺑﺎر ﺗﺴﺖ ﺗﺒﺪﻳﻞ ﻛﺮد‪.‬ﺑﺮاي اﻳﻦ ﻛﺎر ﺑﺎﻳﺪ در اﻧﺘﻬﺎي ﻫﺮ ﻧﻴﻤﻪ ﺑﺎﻓﺮ ﻳﻚ ﻛﺎراﻛﺘﺮ‬
‫ﻧﮕﻬﺒﺎن ﻗﺮار دﻫﻴﻢ ) ‪ Sentinel‬ﺑﻪ ﻋﻨﻮان ﻗﺴﻤﺘﻲ از ﺑﺮﻧﺎﻣﻪ ﻣﻨﺒﻊ ﻧﺨﻮاﻫﺪ ﺑﻮد (‬

‫‪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.‬‬

‫دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل‬ ‫‪4-2‬‬


‫ﺑﺮاي ﭘﻴﺎده ﺳﺎزي دﺳﺘﻲ ﻳﻚ اﺳﻜﻨﺮ از اﺑﺰاري ﺑﻨﺎم دﻳﺎﮔﺮام اﻧﺘﻘﺎل ) ‪Transition‬‬
‫‪ ( Diagram‬ﻛﻤﻚ ﻣﻲ ﮔﻴﺮﻳﻢ‪.‬ﻳﻚ دﻳﺎﮔﺮام اﻧﺘﻘﺎل در واﻗﻊ ﻳﻚ ﮔﺮاف ﺟﻬﺖ دار اﺳﺖ‬
‫ﻛﻪ ﻫﺮﻳﻚ از ﮔﺮه ﻫﺎي آن ﻣﻌﺮف ﻳﻚ وﺿﻌﻴﺖ ) ‪ ( State‬اﺳﺖ‪.‬ﻳﻜﻲ از وﺿﻌﻴﺖ ﻫﺎ ﺑﻌﻨﻮان‬
‫وﺿﻌﻴﺖ ﺷﺮوع و ﻳﻜﻲ ) ﻳﺎ ﭼﻨﺪ ﺗﺎ ( از آﻧﻬﺎ ﺑﻌﻨﻮان وﺿﻌﻴﺖ ) ﻫﺎي ( ﺧﺎﺗﻤﻪ ﻣﺸﺨﺺ ﻣﻲ‬
‫ﮔﺮدد‪.‬ﺑﺮﭼﺴﺐ ) ‪ ( Label‬ﻫﺎﻳﻲ روي ﻟﺒﻪ ﻫﺎي ﻳﻚ دﻳﺎﮔﺮام اﻧﺘﻘﺎل ﻗﺮار داده ﻣﻲ ﺷﻮد ﻛﻪ‬
‫ﻣﺸﺨﺺ ﻣﻲ ﻛﻨﺪ در ﭼﻪ ﺻﻮرﺗﻲ ﻣﻲ ﺗﻮان از ﻳﻚ وﺿﻌﻴﺖ ﺑﻪ وﺿﻌﻴﺖ دﻳﮕﺮ رﻓﺖ‪.‬ﻫﺮ‬
‫دﻳﺎﮔﺮام اﻧﺘﻘﺎل ﻣﻌﺮف ﻳﻚ زﺑﺎن اﺳﺖ‪.‬ﺑﺎ ﺧﻮاﻧﺪن ﻛﺎراﻛﺘﺮﻫﺎي ﻳﻚ رﺷﺘﻪ ﺗﻄﺒﻴﻖ آﻧﻬﺎ ﺑﺎ‬
‫ﺑﺮﭼﺴﺐ ﻫﺎي دﻳﺎﮔﺮام اﻧﺘﻘﺎل ﻣﻌﺮف ﻳﻚ زﺑﺎن و ﭘﻴﻤﺎﻳﺶ آن دﻳﺎﮔﺮام ﻣﻲ ﺗﻮان ﻣﺸﺨﺺ‬
‫ﻧﻤﻮد ﻛﻪ آﻳﺎ آن رﺷﺘﻪ ﻣﺘﻌﻠﻖ ﺑﻪ زﺑﺎن ﻣﻮردﻧﻈﺮ اﺳﺖ ﻳﺎ ﺧﻴﺮ‪.‬ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل دﻳﺎﮔﺮام اﻧﺘﻘﺎل زﻳﺮ‬
‫و ﻋﺒﺎرت ﻣﻨﻈﻢ ‪ (a|b)* abb‬ﻫﺮ دو زﺑﺎﻧﻲ را ﺗﻮﺻﻴﻒ ﻣﻲ ﻛﻨﻨﺪ ﻛﻪ ﺷﺎﻣﻞ رﺷﺘﻪ ﻫﺎي‬
‫ﺗﺸﻜﻴﻞ ﺷﺪه از ﻋﻼﺋﻢ " ‪ " a‬و " ‪ " b‬ﻛﻪ ﺑﻪ زﻳﺮرﺷﺘﻪ " ‪ " abb‬ﺧﺘﻢ ﻣﻲ ﺷﻮﻧﺪ اﺳﺖ‪.‬‬

‫‪a‬‬

‫‪١٦‬‬
‫‪a‬‬ ‫‪b‬‬ ‫‪b‬‬
‫‪Start‬‬ ‫‪S0‬‬ ‫‪S1‬‬ ‫‪S2‬‬ ‫‪S3‬‬

‫‪b‬‬

‫دﻳﺎﮔﺮام ﻓﻮق ﻳﻚ دﻳﺎﮔﺮام ﻏﻴﺮﻗﻄﻌﻲ ) ‪ ( NonDeterministic‬اﺳﺖ‪.‬ﻳﻚ دﻳﺎﮔﺮام اﻧﺘﻘﺎل‬


‫ﻏﻴﺮ ﻗﻄﻌﻲ دﻳﺎﮔﺮاﻣﻲ اﺳﺖ ﻛﻪ ﻳﻜﻲ از دو ﺧﺎﺻﻴﺖ زﻳﺮ را داﺷﺘﻪ ﺑﺎﺷﺪ ‪:‬‬
‫• ﻟﺒﻪ ﻫﺎي ﺧﺎرج ﺷﺪه از ﺑﺮﺧﻲ از وﺿﻌﻴﺖ ﻫﺎي آن ﺑﺮﭼﺴﺐ ﻣﺸﺎﺑﻪ داﺷﺘﻪ ﺑﺎﺷﻨﺪ‪.‬‬
‫• ﻟﺒﻪ ﻫﺎي داراي ﺑﺮﭼﺴﺐ ‪ €‬وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﺪ ) ﺑﺮﭼﺴﺐ ‪ €‬ﺑﻪ اﻳﻦ ﻣﻌﻨﻲ اﺳﺖ ﻛﻪ‬
‫ﺑﺪون ﺗﻮﺟﻪ ﺑﻪ ورودي ﻣﻲ ﺗﻮاﻧﻴﻢ از ﻳﻚ وﺿﻌﻴﺖ ﺑﻪ وﺿﻌﻴﺘﻲ دﻳﮕﺮ ﺑﺮوﻳﻢ (‬
‫دﻳﺎﮔﺮام ﻓﻮق ﻏﻴﺮ ﻗﻄﻌﻲ اﺳﺖ زﻳﺮا از وﺿﻌﻴﺖ ‪ S0‬دو ﻟﺒﻪ ﺑﺎ ﺑﺮﭼﺴﺐ ﻣﺸﺘﺮك " ‪ " a‬ﺧﺎرج‬
‫ﺷﺪه اﺳﺖ‪.‬‬
‫درﺻﻮرﺗﻴﻜﻪ دﻳﺎﮔﺮاﻣﻲ ﻏﻴﺮﻗﻄﻌﻲ ﻧﺒﺎﺷﺪ آﻧﺮا ﻗﻄﻌﻲ ) ‪ ( Deterministic‬ﮔﻮﻳﻨﺪ‪.‬ﻫﻤﭽﻨﻴﻦ‬
‫ﻫﺮ دﻳﺎﮔﺮام ﻏﻴﺮﻗﻄﻌﻲ را ﻣﻲ ﺗﻮان ﺑﻪ ﻳﻚ دﻳﺎﮔﺮام ﻗﻄﻌﻲ ﻣﻌﺎدل ﺗﺒﺪﻳﻞ ﻛﺮد‪.‬ﻣﺜﻼ دﻳﺎﮔﺮام زﻳﺮ‬
‫ﻣﻌﺎدل ﻓﻮق ﻟﻴﻜﻦ ﻗﻄﻌﻲ اﺳﺖ ‪:‬‬

‫‪١٧‬‬
‫‪a‬‬ ‫‪a‬‬
‫‪b‬‬

‫‪a‬‬ ‫‪b‬‬ ‫‪b‬‬


‫‪Start‬‬ ‫‪S0‬‬ ‫‪S1‬‬ ‫‪S2‬‬ ‫‪S3‬‬

‫‪a‬‬
‫‪b‬‬

‫ﺑﺮﻧﺎﻣﻪ اي ﻛﻪ از ﻳﻚ دﻳﺎﮔﺮام ﻗﻄﻌﻲ اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ ﭘﻴﺎده ﺳﺎزي راﺣﺖ ﺗﺮي ﻧﺴﺒﺖ ﺑﻪ ﺑﺮﻧﺎﻣﻪ‬
‫ﻣﺒﺘﻨﻲ ﺑﺮ ﻳﻚ دﻳﺎﮔﺮام ﻏﻴﺮﻗﻄﻌﻲ دارد‪.‬ﺑﺮﻧﺎﻣﻪ ﻣﺒﺘﻨﻲ ﺑﺮ ﻳﻚ دﻳﺎﮔﺮام ﻏﻴﺮﻗﻄﻌﻲ ﺑﺎﻳﺴﺘﻲ داراي‬
‫ﻗﺎﺑﻠﻴﺖ ﭘﻲ ﺟﻮﻳﻲ ﻳﺎ ‪ Backtracking‬ﺑﺎﺷﺪ‪.‬از ﻃﺮف دﻳﮕﺮ دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل ﻗﻄﻌﻲ‬
‫ﻣﻌﻤﻮﻻ ﺗﻌﺪاد وﺿﻌﻴﺖ ﺑﻴﺸﺘﺮي ﻧﺴﺒﺖ ﺑﻪ دﻳﺎﮔﺮام ﻏﻴﺮﻗﻄﻌﻲ ﻣﻌﺎدل ﺧﻮد دارﻧﺪ‪ .‬ﺑﻨﺎﺑﺮاﻳﻦ ﺑﺮاي‬
‫ﭘﻴﺎده ﺳﺎزي ﻳﻚ اﺳﻜﻨﺮ اﺑﺘﺪا دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل ﻣﻌﺮف اﻟﮕﻮي ﺗﻮﻛﻦ ﻫﺎي زﺑﺎن ﻣﻮردﻧﻈﺮ‬
‫رﺳﻢ ﻣﻲ ﮔﺮدد‪.‬اﻳﻦ دﻳﺎﮔﺮام ﻫﺎ ﺑﺮاي ﺑﺪﺳﺖ آوردن اﻃﻼﻋﺎت در ﻣﻮرد ﻛﺎراﻛﺘﺮﻫﺎﻳﻲ ﻛﻪ‬
‫ﺑﻮﺳﻴﻠﻪ ﻧﺸﺎﻧﻪ روي ‪ Forward‬در ورودي ﺑﺎﻳﺪ دﻳﺪه ﺷﻮﻧﺪ اﺳﺘﻔﺎده ﻣﻲ ﮔﺮدد‪.‬ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ‬
‫ﻛﻪ ﻫﻤﺎﻧﻄﻮر ﻛﻪ ﻛﺎراﻛﺘﺮﻫﺎي ورودي ﺧﻮاﻧﺪه ﻣﻲ ﺷﻮﻧﺪ از ﻳﻚ وﺿﻌﻴﺖ در دﻳﺎﮔﺮام ﺑﻪ‬
‫وﺿﻌﻴﺘﻲ دﻳﮕﺮ ﺣﺮﻛﺖ ﻣﻲ ﻛﻨﻴﻢ ﺗﺎ اﻳﻨﻜﻪ ﺑﻪ ﻳﻚ وﺿﻌﻴﺖ ﻧﻬﺎﻳﻲ ﺑﺮﺳﻴﻢ‪.‬ﭘﻴﻤﺎﻳﺶ دﻳﺎﮔﺮام از‬
‫وﺿﻌﻴﺖ ﺷﺮوع )‪ ( Start‬آﻏﺎز ﻣﻲ ﺷﻮد‪.‬‬
‫‪a‬‬ ‫‪c‬‬

‫‪Start‬‬ ‫‪S0‬‬ ‫‪S1‬‬ ‫‪S2‬‬


‫ﺣﺎﻟﺖ ﺷﺮوع‬ ‫ﺣﺎﻟﺖ ﻧﻬﺎﻳﯽ‬

‫‪b‬‬

‫‪١٨‬‬
‫ﻫﻨﮕﺎﻣﻴﻜﻪ در وﺿﻌﻴﺖ ﻓﻌﻠﻲ ﻟﺒﻪ اي ﻛﻪ ﺑﺮﭼﺴﺐ آن ﻣﺴﺎوي ﻛﺎراﻛﺘﺮ ورودي اﺳﺖ ﻗﺮار‬
‫داﺷﺘﻪ ﺑﺎﺷﻴﻢ‪ ،‬از آن ﺣﺎﻟﺖ ﺑﻮﺳﻴﻠﻪ آن ﻟﺒﻪ ﺑﻪ ﺣﺎﻟﺖ ﺑﻌﺪي ﻣﻲ روﻳﻢ و در ﻏﻴﺮ اﻳﻨﺼﻮرت‬
‫ﺗﻮﻛﻦ ﺗﻮﺳﻂ اﻳﻦ دﻳﺎﮔﺮام ﻗﺎﺑﻞ ﺗﺸﺨﻴﺺ ﻧﺨﻮاﻫﺪ ﺑﻮد‪.‬‬
‫ﺑﺮﭼﺴﺐ " ‪ " other‬در روي ﻟﺒﻪ ﻳﻚ وﺿﻌﻴﺖ ﺑﻴﺎﻧﮕﺮ ﻫﺮ ﻛﺎراﻛﺘﺮي اﺳﺖ ﻛﻪ ﺗﻮﺳﻂ ﻟﺒﻪ‬
‫ﻫﺎي دﻳﮕﺮ آن وﺿﻌﻴﺖ ذﻛﺮ ﻧﺸﺪه اﻧﺪ‪.‬‬
‫ﻣﺜﺎل‪ -‬دﻳﺎﮔﺮام ﺗﺸﺨﻴﺺ ﺗﻮﻛﻦ " => " ﺑﺼﻮرت زﻳﺮ اﺳﺖ ‪:‬‬

‫>‬ ‫=‬
‫‪Start‬‬ ‫‪S2‬‬
‫‪S0‬‬ ‫‪S1‬‬

‫‪other‬‬ ‫*‬
‫‪S3‬‬

‫‪ :Other‬ﻳﻌﻨﻲ ﻫﺮ ﻛﺎراﻛﺘﺮ دﻳﮕﺮ ﻏﻴﺮ از =‬

‫ﻋﻼﻣﺖ * ﻳﻌﻨﻲ اﻳﻨﻜﻪ ﺑﺎﻳﺴﺘﻲ آﺧﺮﻳﻦ ورودي ﺑﻪ ﺑﺎﻓﺮ ﺑﺎزﮔﺮدد‪.‬‬


‫درﺻﻮرﺗﻲ ﻛﻪ در ﻛﻠﻴﻪ دﻳﺎﮔﺮام ﻫﺎ ﻧﺘﻮان ﻳﻚ ﺗﻮﻛﻦ را ﺗﺸﺨﻴﺺ داد ﻳﻚ ﺧﻄﺎي واژه اي‬
‫روي داده اﺳﺖ و ﺑﺎﻳﺪ ﺑﺮﻧﺎﻣﻪ ﺧﻄﺎﭘﺮداز ﻓﺮاﺧﻮاﻧﻲ ﺷﻮد‪.‬‬

‫ﻣﺜﺎل – دﻳﺎﮔﺮام ﺗﺸﺨﻴﺺ ﺷﻨﺎﺳﻪ ﻫﺎي زﺑﺎن ﭘﺎﺳﻜﺎل‬

‫‪letter‬‬ ‫‪other‬‬
‫*‬
‫‪Start‬‬ ‫‪S0‬‬ ‫‪S1‬‬ ‫‪S2‬‬

‫‪Letter / digit‬‬

‫ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﻳﻨﻜﻪ ﻛﻠﻤﺎت ﻛﻠﻴﺪي از ﻳﻚ دﻧﺒﺎﻟﻪ ﻛﺎراﻛﺘﺮي ﺗﺸﻜﻴﻞ ﺷﺪه اﻧﺪ ﻟﺬا ﻣﻲ ﺗﻮان از‬
‫دﻳـﺎﮔﺮام ﻓﻮق ﺑﺮاي ﺗﺸـﺨﻴﺺ ﻛﻠﻤﺎت ﻛﻠﻴـﺪي ﻧﻴﺰ اﺳـﺘﻔﺎده ﻧﻤﻮد‪.‬ﺗـﺸﺨﻴﺺ ﻛﻠـﻤﺎت ﻛﻠﻴﺪي‬

‫‪١٩‬‬
‫و ﺷﻨﺎﺳﻪ ﻫﺎ ﺗﻮﺳﻂ ﻳﻚ دﻳﺎﮔﺮام ﺑﺎﻋﺚ ﻛﺎﻫﺶ ﺗﻌﺪاد وﺿﻌﻴﺖ ﻫﺎي دﻳﺎﮔﺮام اﻧﺘﻘﺎل اﺳﻜﻨﺮ‬
‫ﻣﻲ ﮔﺮدد‪.‬ﺑﺮاي آﻧﻜﻪ ﻛﻠﻤﺎت ﻛﻠﻴﺪي را از ﺷﻨﺎﺳﻪ ﻫﺎي ﻫﻤﻨﺎﻣﺸﺎن ﺟﺪا ﺳﺎزﻳﻢ ﻳﻜﻲ از ﺳﺎده‬
‫ﺗﺮﻳﻦ روﺷﻬﺎ اﻳﻦ اﺳﺖ ﻛﻪ در اﺑﺘﺪا در ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ ﻛﻠﻤﺎت ﻛﻠﻴﺪي را وارد ﻛﻨﻴﻢ‪.‬ﺑﻪ اﻳﻦ‬
‫ﺗﺮﺗﻴﺐ ﺑﺎ رﺟﻮع ﺑﻪ ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ ﻣﻲ ﺗﻮان درﻳﺎﻓﺖ ﻛﻪ ﺗﻮﻛﻦ ﻣﻮرد ﻧﻈﺮ ﺷﻨﺎﺳﻪ اﺳﺖ ﻳﺎ‬
‫ﻛﻠﻤﻪ ﻛﻠﻴﺪي‪.‬‬
‫ﺟﺪول‬ ‫‪if‬‬ ‫‪.....‬‬ ‫‪K‬‬
‫ﻧﺸﺎﻧﻪ هﺎ‬

‫‪ : K‬ﺑﻴﺎﻧﮕﺮ ‪ Keyword‬ﺑﻮدن ﻣﻲ ﺑﺎﺷﺪ‬

‫ﺑﺮاي اﻳﻦ ﻛﺎر از دو ﺗﺎﺑﻊ ) (‪ gettoken‬و ) (‪ install-id‬اﺳﺘﻔﺎده ﻣﻲ ﺷﻮد‪.‬ﺗﺎﺑﻊ ‪install-‬‬


‫) (‪ id‬ﺟﺪول ﻋﻼﺋﻢ را ﺟﺴﺘﺠﻮ ﻣﻲ ﻛﻨﺪ و اﮔﺮ واژه ﺗﻮﻛﻦ در ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ ﺑﻌﻨﻮان ﻛﻠﻤﻪ‬
‫ﻛﻠﻴﺪي آﻣﺪه ﺑﺎﺷﺪ اﻳﻦ ﺗﺎﺑﻊ ﻋﺪد ﺻﻔﺮ را ﺑﺎزﻣﻲ ﮔﺮداﻧﺪ‪.‬در ﺻﻮرﺗﻲ ﻛﻪ واژه ﻳﻚ ﻣﺘﻐﻴﺮ‬
‫ﺗﺸﺨﻴﺺ داده ﺷﻮد و در ﺟﺪول ﻫﻢ ﻣﻮﺟﻮد ﺑﺎﺷﺪ اﻳﻦ ﺗﺎﺑﻊ آدرس آن ﻣﺘﻐﻴﺮ در ﺟﺪول ﻧﺸﺎﻧﻪ‬
‫ﻫﺎ را ﺑﻮﺳﻴﻠﻪ ﻳﻚ اﺷﺎره ﮔﺮ ﺑﺎز ﻣﻲ ﮔﺮداﻧﺪ‪.‬اﮔﺮ ﭼﻨﺎﻧﭽﻪ واژه در ﺟﺪول ﻣﻮﺟﻮد ﻧﺒﺎﺷﺪ ﺑﻌﻨﻮان‬
‫ورودي ﺟﺪﻳﺪ ﺑﻪ ﺟﺪول ﻋﻼﺋﻢ وارد ﺷﺪه و ﻣﻄﺎﺑﻖ ﺣﺎﻟﺖ ﻗﺒﻞ آدرس آن ﺑﺎزﮔﺮداﻧﺪه ﺧﻮاﻫﺪ‬
‫ﺷﺪ‪.‬‬
‫) (‪ gettoken‬ﻧﻴﺰ ﺑﻄﻮر ﻣﺸﺎﺑﻬﻲ ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ را ﺟﺴﺘﺠﻮ ﻛﺮده و در ﺻﻮرﺗﻲ ﻛﻪ واژه‬
‫ﻣﻮردﻧﻈﺮ ﻳﻚ ﻛﻠﻤﻪ ﻛﻠﻴﺪي ﺑﺎﺷﺪ ﺗﻮﻛﻦ ﻣﺘﻨﺎﻇﺮش را ﻣﺴﺘﻘﻴﻤﺎ ﺑﻪ ﭘﺎرﺳﺮ ﻣﻲ ﻓﺮﺳﺘﺪ و در ﻏﻴﺮ‬
‫اﻳﻨﺼﻮرت ﺗﻮﻛﻦ " ‪ " id‬را اﻧﺘﻘﺎل ﻣﻲ دﻫﺪ‪.‬ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ در ﺻﻮرﺗﻲ ﻛﻪ ﺗﻌﺪاد ﻛﻠﻤﺎت‬

‫‪٢٠‬‬
‫ﻛﻠﻴﺪي ﺗﻐﻴﻴﺮ ﻛﻨﺪ دﻳﺎﮔﺮام اﻧﺘﻘﺎل ﺑﺪون ﺗﻐﻴﻴﺮ ﺑﺎﻗﻲ ﺧﻮاﻫﺪ ﻣﺎﻧﺪ و ﺑﻪ راﺣﺘﻲ ﻣﻲ ﺗﻮان اﻳﻦ ﺗﻐﻴﻴﺮ‬
‫را در ﺟﺪول ﻧﺸﺎﻧﻪ ﻫﺎ اﻳﺠﺎد ﻛﺮد‪.‬‬
‫در اﻳﻨﺠﺎ ذﻛﺮ ﭼﻨﺪ ﻧﻜﺘﻪ در ﻣﻮرد ﻧﺤﻮه ﻗﺮار دادن دﻳﺎﮔﺮام اﻧﺘﻘﺎل ﺗﻮﻛﻦ ﻫﺎي ﻣﺨﺘﻠﻒ‬
‫ﺿﺮوري اﺳﺖ‪.‬اول آﻧﻜﻪ اﺳﻜﻨﺮ ﺑﺎﻳﺪ ﻫﻤﻮاره ﺳﻌﻲ ﻛﻨﺪ ﻃﻮﻻﻧﻲ ﺗﺮﻳﻦ ﺗﻮﻛﻦ ﻣﻤﻜﻦ را‬
‫ﺗﺸﺨﻴﺺ دﻫﺪ‪.‬ﻣﺜﻼ دﻳﺎﮔﺮام ﺗﺸﺨﻴﺺ اﻋﺪاد اﻋﺸﺎري ﺑﺎﻳﺴﺘﻲ ﻗﺒﻞ از دﻳﺎﮔﺮاﻣﻲ ﺑﺎﺷﺪ ﻛﻪ اﻋﺪاد‬
‫ﺻﺤﻴﺢ را ﺗﺸﺨﻴﺺ ﻣﻲ دﻫﺪ‪.‬ﻫﻤﭽﻨﻴﻦ دﻳﺎﮔﺮام ﺗﺸﺨﻴﺺ ﺗﻮﻛﻦ ﻫﺎﻳﻲ ﻛﻪ ﻣﻮرد اﺳﺘﻔﺎده‬
‫ﺑﻴﺸﺘﺮي در ﺑﺮﻧﺎﻣﻪ ﻫﺎ دارﻧﺪ ) ﻣﺜﻼ ‪ (... , tab , space‬ﺑﺎﻳﺪ ﻗﺒﻞ از دﻳﺎﮔﺮام اﻧﺘﻘﺎل ﺗﻮﻛﻦ ﻫﺎي‬
‫ﻛﻤﻴﺎب ﺗﺮ ﻗﺮار ﮔﻴﺮد ﺗﺎ در ﻧﻬﺎﻳﺖ ﺗﺴﺖ ﻛﻤﺘﺮي ﺑﺮاي ﺗﺸﺨﻴﺺ ﺗﻮﻛﻦ ﻫﺎ اﻧﺠﺎم ﺷﻮد‪.‬ﻣﺜﻼ‬
‫ﺷﻜﻞ زﻳﺮ ﺗﺮﺗﻴﺐ ﻗﺮار ﮔﺮﻓﺘﻦ دﻳﺎﮔﺮام ﻫﺎ ﺑﺮاي ﺗﺸﺨﻴﺺ اﻋﺪاد را ﻧﺸﺎن ﻣﻲ دﻫﺪ‪.‬ﺷﻤﺎره‬
‫وﺿﻌﻴﺖ ﻫﺎ ﺑﻴﺎﻧﮕﺮ ﺗﺮﺗﻴﺐ دﻳﺎﮔﺮام ﻫﺎ اﺳﺖ‪.‬‬

‫‪digit‬‬
‫‪digit‬‬ ‫‪.‬‬ ‫‪digit‬‬ ‫‪E‬‬ ‫‪+ or -‬‬

‫‪Start‬‬ ‫‪0‬‬ ‫‪1‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬

‫‪E‬‬ ‫‪digit‬‬
‫‪digit‬‬

‫*‬
‫‪7‬‬ ‫‪6‬‬
‫‪other‬‬
‫‪digit‬‬
‫‪digit‬‬ ‫‪digit‬‬

‫‪digit‬‬ ‫‪.‬‬ ‫‪digit‬‬ ‫‪other‬‬


‫*‬
‫‪Start‬‬ ‫‪8‬‬ ‫‪9‬‬ ‫‪10‬‬ ‫‪11‬‬ ‫‪12‬‬

‫‪digit‬‬

‫‪other‬‬
‫‪digit‬‬ ‫*‬

‫‪Start‬‬ ‫‪13‬‬ ‫‪14‬‬ ‫‪15‬‬

‫‪٢١‬‬
‫ﺑﻌﺪ از اﻳﻨﻜﻪ دﻳﺎﮔﺮام ﺗﺸﺨﻴﺺ ﺗﻮﻛﻦ ﻫﺎ رﺳﻢ ﺷﺪ ﺑﻪ راﺣﺘﻲ ﻣﻲ ﺗﻮان آﻧﺮا ﺑﺎ دﺳﺘﻮر ‪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‬ﻛﺎﻣﭙﺎﻳﻠﺮﻫﺎ ﺑﺼﻮرت ﭘﻴﻤﺎﻧﻪ اي را راﺣﺖ ﺗﺮ ﻣﻲ ﺳﺎزد‪.‬‬

‫‪ – 3‬ﺗﺤﻠﻴﻞ ﻧﺤﻮي ) ‪( Syntax Analysis‬‬


‫در ﻣﺮﺣﻠﻪ ﺗﺤﻠﻴﻞ ﻧﺤﻮي ﺑﺮﻧﺎﻣﻪ ورودي از ﻧﻈﺮ دﺳﺘﻮري ﺑﺮرﺳﻲ ﻣﻲ ﺷﻮد‪.‬ﺗﺤﻠﻴﻠﮕﺮ ﻧﺤﻮي ﻳﺎ‬
‫ﭘﺎرﺳﺮ ﺑﺮﻧﺎﻣﻪ ورودي را ﻛﻪ ﺑﺼﻮرت دﻧﺒﺎﻟﻪ اي از ﺗﻮﻛﻦ ﻫﺎ اﺳﺖ از اﺳﻜﻨﺮ ﮔﺮﻓﺘﻪ و ﺗﻌﻴﻴﻦ ﻣﻲ‬
‫ﻛﻨﺪ ﻛﻪ آﻳﺎ اﻳﻦ ﺟﻤﻠﻪ ﻣﻲ ﺗﻮاﻧﺪ ﺑﻮﺳﻴﻠﻪ ﮔﺮاﻣﺮ زﺑﺎن ﻣﻮردﻧﻈﺮ ﺗﻮﻟﻴﺪ ﺷﻮد ﻳﺎ ﺧﻴﺮ ؟‬
‫راﺑﻄﻪ ﭘﺎرﺳﺮ و اﺳﻜﻨﺮ ﺑﺼﻮرت زﻳﺮ اﺳﺖ ‪:‬‬

‫‪token‬‬

‫‪Source‬‬ ‫‪Lexical‬‬ ‫‪Parser‬‬


‫‪Program‬‬ ‫‪Analyzer‬‬
‫‪get next token‬‬

‫‪Symbol-Table‬‬

‫ﺑﻄﻮر ﻛﻠﻲ دو ﻧﻮع روش ﺗﺤﻠﻴﻞ ﻧﺤﻮي وﺟﻮد دارد ‪:‬‬


‫‪ – 1‬روﺷﻬﺎي ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ) ‪( Top-Down‬‬
‫‪ – 2‬روﺷﻬﺎي ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ) ‪( Bottom-Up‬‬

‫‪٢٣‬‬
‫روﺷﻬﺎي ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ‪ ,‬درﺧﺖ ﺗﺠﺰﻳﻪ ) ‪ ( Parse Tree‬را از ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ﻣﻲ ﺳﺎزﻧﺪ در‬
‫ﺣﺎﻟﻴﻜﻪ روﺷﻬﺎي ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﺑﺮﻋﻜﺲ ﻋﻤﻞ ﻣﻲ ﻛﻨﻨﺪ ﻳﻌﻨﻲ درﺧﺖ ﺗﺠﺰﻳﻪ را از ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ‬
‫ﺗﻮﻟﻴﺪ ﻣﻲ ﻛﻨﻨﺪ‪.‬در ﻫﺮ دو روش ورودي از ﭼﭗ ﺑﻪ راﺳﺖ و در ﻫﺮ ﻗﺪم ﻓﻘﻂ ﻳﻚ ﺗﻮﻛﻦ‬
‫ﺑﺮرﺳﻲ ﻣﻲ ﺷﻮد‪.‬‬
‫ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ‪.‬‬
‫‪1,2 E → E + T | T‬‬
‫‪3,4 T → T * F | F‬‬
‫‪5,6 F → ( E ) | id‬‬
‫درﺧﺖ ﺗﺠﺰﻳﻪ ﺟﻤﻠﻪ ‪ id + id‬ﺑﺼﻮرت زﻳﺮ ﺧﻮاﻫﺪ ﺑﻮد ﻛﻪ در آن رﻳﺸﻪ درﺧﺖ ﻋﻼﻣﺖ‬
‫ﺷﺮوع ﮔﺮاﻣﺮ اﺳﺖ‪ ).‬ﺗﻮﺿﻴﺢ اﻳﻨﻜﻪ از اﻳﻦ ﭘﺲ اﮔﺮ ﻋﻼﻣﺖ ﺷﺮوع ﮔﺮاﻣﺮي ﺑﺼﻮرت ﺻﺮﻳﺢ‬
‫ﻣﺸﺨﺺ ﻧﺸﻮد ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺳﻤﺖ ﭼﭗ اوﻟﻴﻦ ﻗﺎﻋﺪه ﮔﺮاﻣﺮ ﺑﻌﻨﻮان ﻋﻼﻣﺖ ﺷﺮوع آن در ﻧﻈﺮ‬
‫ﮔﺮﻓﺘﻪ ﻣﻲ ﺷﻮد‪(.‬‬

‫‪E‬‬

‫‪E‬‬ ‫‪+‬‬ ‫‪T‬‬

‫‪T‬‬ ‫‪F‬‬

‫‪F‬‬ ‫‪id‬‬

‫‪id‬‬

‫ﻣﻬﻤﺘﺮﻳﻦ روﺷﻬﺎي ﺗﺠﺰﻳﻪ ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ﻋﺒﺎرﺗﻨﺪ از ‪:‬‬


‫‪ – 1‬روش ﭘﺎﺋﻴﻨﮕﺮد ) ‪( Recursive Descent‬‬
‫‪ – 2‬روش )‪ LL(1‬ﻛﻪ ﺣﺎﻟﺖ ﺧﺎﺻﻲ از روش ﻛﻠﻲ )‪ LL(K‬اﺳﺖ‪.‬‬

‫‪٢٤‬‬
‫در روش )‪ LL(1‬ﻣﻨﻈﻮر از ' ‪ ' L‬اول اﻳﻦ اﺳﺖ ﻛﻪ ورودي از ﺳﻤﺖ ﭼﭗ ﺑﻪ راﺳﺖ‬
‫ﺧﻮاﻧﺪه ﺷﺪه و ﺑﺮرﺳﻲ ﻣﻲ ﮔﺮدد و ' ‪ ' L‬دوم ﻳﻌﻨﻲ ﭘﺎرﺳﺮ از ﺑﺴﻂ ﭼﭗ )‪Left-Most-‬‬
‫‪ ( Derivation‬اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ و ' ‪ ' 1‬ﻧﻴﺰ ﺑﻴﺎﻧﮕﺮ اﻳﻦ اﺳﺖ ﻛﻪ در ﻫﺮ ﻗﺪم از ﺗﺠﺰﻳﻪ ﻓﻘﻂ‬
‫ﻳﻚ ﺗﻮﻛﻦ ﺑﺮرﺳﻲ ﺧﻮاﻫﺪ ﺷﺪ‪.‬‬

‫ﻣﻬﻤﺘﺮﻳﻦ روﺷﻬﺎي ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﻋﺒﺎرﺗﻨﺪ از ‪:‬‬


‫‪ – 1‬روش ﺗﻘﺪم ﻋﻤﻠﮕﺮ ) ‪( Operator Precedence‬‬
‫‪ – 2‬روش ﺗﻘﺪم ﺳﺎده ) ‪( Simple Precedence‬‬
‫‪ – 3‬روش )‪ LR(1‬ﻛﻪ ﺧﻮد داراي ﺳﻪ ﻓﺮم ﻣﺨﺘﻠﻒ ﺑﺎ ﻧﺎﻣﻬﺎي )‪, LALR(1) , SLR(1‬‬
‫)‪ CLR(1‬اﺳﺖ‪.‬‬

‫ﺗﻤﺎﻣﻲ روﺷﻬﺎي ﻓﻮق ﺑﻪ ﻧﻮﻋﻲ از ﻳﻚ روش ﻛﻠﻲ ﺑﻪ ﻧﺎم اﻧﺘﻘﺎل – ﻛﺎﻫﺶ ) ‪Shift-‬‬
‫‪ ( Reduce‬ﭘﻴﺮوي ﻣﻲ ﻛﻨﻨﺪ‪.‬اﮔﺮﭼﻪ روﺷﻬﺎي ﺗﺠﺰﻳﻪ ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ﺑﺮاي ﭘﻴﺎده ﺳﺎزي دﺳﺘﻲ‬
‫ﻣﻨﺎﺳﺐ ﺗﺮﻧﺪ اﻣﺎ اﺑﺰارﻫﺎﻳﻲ وﺟﻮد دارد ﻛﻪ ﺑﺎ ﻛﻤﻚ آﻧﻬﺎ ﻣﻲ ﺗﻮان ﺑﻄﻮر ﺧﻮدﻛﺎر ﭘﺎرﺳﺮﻫﺎي‬
‫ﻗﻮي ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﺗﻮﻟﻴﺪ ﻧﻤﻮد‪.‬در )‪ LR(1‬ﻣﻨﻈﻮر از ' ‪ ' L‬ﻳﻌﻨﻲ ﭘﺎرﺳﺮ ورودي را از ﭼﭗ ﺑﻪ‬
‫راﺳﺖ ﻣﻲ ﺧﻮاﻧﺪ و ﻣﻨﻈﻮر از ' ‪ ' R‬اﻳﻦ اﺳﺖ ﻛﻪ ﭘﺎرﺳﺮ از ﻋﻜﺲ ﺑﺴﻂ راﺳﺖ ) ‪Right-‬‬
‫‪ ( Most-Derivation‬اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ‪.‬‬

‫‪ 1-3‬ﺗﺠﺰﻳﻪ ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ) ‪( Top-Down Parsing‬‬


‫در ﺣﺎﻟﺖ ﻛﻠﻲ ﻳﻚ ﭘﺎرﺳﺮ ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ﺑﺎﻳﺴﺘﻲ ﺑﺘﻮاﻧﺪ در ﺻﻮرت ﻟﺰوم ﻋﻤﻞ ﭘﻴﺠﻮﺋﻲ‬
‫) ‪ ( Back Tracking‬اﻧﺠﺎم دﻫﺪ‪.‬ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ‪.‬‬
‫‪S → cAd‬‬
‫‪A → ab | a‬‬

‫ﺑﺮاي ﺗﺠﺰﻳﻪ رﺷﺘﻪ ورودي ‪ cad‬ﭘﺎرﺳﺮ ﺑﺼﻮرت زﻳﺮ ﻋﻤﻞ ﻣﻲ ﻛﻨﺪ ‪:‬‬

‫‪٢٥‬‬
‫‪S‬‬

‫‪c‬‬ ‫‪A‬‬ ‫‪d‬‬

‫‪a‬‬ ‫‪b‬‬

‫ﭼﻮن ‪ d‬ﺑﺎ ‪ b‬ﺑﺮاﺑﺮ ﻧﻴﺴﺖ ﭘﺎرﺳﺮ ﻧﻴﺎز دارد ﻛﻪ ﻳﻚ ﻣﺮﺣﻠﻪ ﺑﻪ ﻋﻘﺐ ﺑﺎزﮔﺮدد و ﻗﺎﻋﺪه دﻳﮕﺮ را‬
‫ﻣﻮرد ﺑﺮرﺳﻲ ﻗﺮار دﻫﺪ‪.‬‬
‫‪S‬‬

‫‪c‬‬ ‫‪A‬‬ ‫‪d‬‬

‫‪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‬‬

‫‪array [ num dot dot num ] of integer‬‬ ‫‪type‬‬

‫‪array‬‬ ‫[‬ ‫] ‪Simple‬‬ ‫‪of‬‬ ‫‪Type‬‬

‫‪٢٧‬‬
‫‪array [ num dot dot num ] of integer‬‬ ‫‪type‬‬

‫‪array‬‬ ‫[‬ ‫] ‪Simple‬‬ ‫‪of‬‬ ‫‪Type‬‬

‫ﻣﻌﻤﻮﻻ اﻧﺘﺨﺎب ﻳﻚ ﻗﺎﻋﺪه ﺑﺮاي ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﺼﻮرت ﺳﻌﻲ و ﺧﻄﺎ ) ‪Trial-and-‬‬


‫‪ ( Error‬اﻧﺠﺎم ﻣﻲ ﺷﻮد‪.‬ﺑﺪﻳﻦ ﻣﻌﻨﻲ ﻛﻪ در ﺻﻮرﺗﻲ ﻛﻪ ﻗﺎﻋﺪه اﻧﺘﺨﺎﺑﻲ اول ﻣﻨﺎﺳﺐ اداﻣﻪ‬
‫ﻋﻤﻞ ﺗﺠﺰﻳﻪ ﻧﺒﺎﺷﺪ ﺑﺎ ﻋﻤﻞ ﻋﻘﺒﮕﺮد ﻳﺎ ﭘﻴﺠﻮﺋﻲ ﻗﺎﻋﺪه دﻳﮕﺮي اﻧﺘﺨﺎب ﻣﻲ ﮔﺮدد‪.‬‬

‫‪ 2-3‬ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻨﮕﺮد ) ‪( Recursive Descent Parsing‬‬


‫ﻳﻜﻲ از اﻧﻮاع ﭘﺎرﺳﺮﻫﺎ ﻛﻪ ﺑﺼﻮرت ﭘﻴﺸﮕﻮﻳﺎﻧﻪ ﻋﻤﻞ ﻣﻲ ﻛﻨﺪ ﭘﺎرﺳﺮ ﭘﺎﺋﻴﻨﮕﺮد ) ‪Recursive‬‬
‫‪ ( Descent‬اﺳﺖ‪.‬اﻳﻦ ﭘﺎرﺳﺮ ﺑﺼﻮرت ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ﻋﻤﻞ ﻣﻲ ﻛﻨﺪ و در آن ﻳﻚ ﻣﺠﻤﻮﻋﻪ‬
‫روﻳﻪ ﻫﺎ ﺑﻄﻮر ﺑﺎزﮔﺸﺘﻲ رﺷﺘﻪ ورودي را ﻣﻮرد ﭘﺮدازش ﻗﺮار ﻣﻲ دﻫﻨﺪ‪.‬اﻳﻦ روﻳﻪ ﻫﺎ ﻛﻪ ﺑﺮاي‬
‫ﭘﺮدازش رﺷﺘﻪ ورودي ﻓﺮاﺧﻮاﻧﻲ ﻣﻲ ﺷﻮﻧﺪ ﻳﻚ درﺧﺖ ﭘﺎرس ﺑﺮاي ورودي اﻳﺠﺎد ﻣﻲ‬
‫ﻛﻨﻨﺪ‪.‬ﻳﻚ ﭘﺎرﺳﺮ ﭘﺎﺋﻴﻨﮕﺮد ﺑﻪ ازاي ﻫﺮ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻳﻚ روﻳﻪ دارد ﻛﻪ دو ﻛﺎر اﻧﺠﺎم ﻣﻲ دﻫﺪ ‪:‬‬
‫‪ -1‬ﺗﺼﻤﻴﻢ ﻣﻲ ﮔﻴﺮد ﻛﻪ از ﻛﺪام ﻗﺎﻋﺪه ﮔﺮاﻣﺮ اﺳﺘﻔﺎده ﺷﻮد‪.‬‬
‫‪ -2‬از ﻗﺎﻋﺪه اﻧﺘﺨﺎب ﺷﺪه اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ‪.‬‬
‫ﻋﻼوه ﺑﺮ روﻳﻪ ﻫﺎﻳﻲ ﻛﻪ ﺑﻪ ازاي ﻫﺮ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ وﺟﻮد دارد ﻳﻚ ﭘﺎرﺳﺮ ﭘﺎﺋﻴﻨﮕﺮد از روﻳﻪ‬
‫دﻳﮕﺮي ﺑﻨﺎم ‪ match‬ﺑﺮاي ﺗﻄﺒﻴﻖ ﺗﻮﻛﻦ ﻫﺎي ورودي و ﭘﺎﻳﺎﻧﻪ ﻫﺎي درﺧﺖ ﺗﺠﺰﻳﻪ در ﺣﺎل‬
‫ﺳﺎﺧﺖ اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ‪.‬ﺑﻌﻨﻮان ﻣﺜﺎل ﭘﺎرﺳﺮ ﭘﺎﺋﻴﻨﮕﺮد ﺑﺮاي ﮔﺮاﻣﺮ ﺗﻌﺮﻳﻒ ‪ Type‬در زﺑﺎن‬
‫ﭘﺎﺳﻜﺎل دو روﻳﻪ ﺑﺮاي ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎي ‪ Type‬و ‪ Simple‬ﺧﻮاﻫﺪ داﺷﺖ‪.‬روﻳﻪ ‪ match‬ﻧﻴﺰ‬
‫ﺑﺼﻮرت زﻳﺮ اﺳﺖ ‪:‬‬

‫‪Procedure‬‬ ‫;) ‪match ( t : token‬‬


‫‪begin‬‬

‫‪٢٨‬‬
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‬‬

‫ﺑﺮاي ﮔﺮاﻣﺮ ‪ Type‬ﺧﻮاﻫﻴﻢ داﺷﺖ ‪:‬‬


‫}‪First(Simple) = {integer,char,num‬‬
‫}↑{ = )‪First(↑ id‬‬
‫}‪First(array[Simple] of Type) = {array‬‬
‫}‪First(Type) = {↑,array,integer,char,num‬‬

‫ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ در ﮔﺮاﻣﺮي ﻛﻪ دو ﻗﺎﻋﺪه ﺑﺼﻮرت ‪ A → α‬و ‪ A → β‬داﺷﺘﻪ ﺑﺎﺷﺪ‬


‫ﭘﺎرﺳﺮ ﭘﺎﺋﻴﻨﮕﺮد ﺑﺎ اﺳﺘﻔﺎده از ‪ First‬ﺳﻤﺖ راﺳﺖ اﻳﻦ ﻗﻮاﻋﺪ ‪ ,‬ﻗﺎﻋﺪه ﻣﻨﺎﺳﺐ را ﺗﻌﻴﻴﻦ ﻣﻲ‬
‫ﻛﻨﺪ ﺑﺪون اﻳﻨﻜﻪ ﻧﻴﺎز ﺑﻪ ﻋﻤﻞ ﻋﻘﺒﮕﺮد داﺷﺘﻪ ﺑﺎﺷﺪ اﻟﺒﺘﻪ ﻣﺸﺮوط ﺑﺮ اﻳﻨﻜﻪ در ﭼﻨﻴﻦ‬
‫ﮔﺮاﻣﺮﻫﺎﻳﻲ ﺷﺮط زﻳﺮ ﺑﺮﻗﺮار ﺑﺎﺷﺪ ‪:‬‬
‫‪First(α) ∩ First(β) = ф‬‬

‫‪٣٠‬‬
‫‪ 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‬‬

‫‪begin‬‬ ‫‪STS‬‬ ‫‪end‬‬

‫‪ε‬‬

‫ﺳﭙﺲ ﺗﻮﻛﻦ ‪ end‬ﻧﻴﺰ ﺑﺎ ﭘﺎﻳﺎﻧﻪ ‪ end‬در درﺧﺖ ﺗﺠﺰﻳﻪ ﺗﻄﺒﻴﻖ ﻣﻲ ﺷﻮد و ﻋﻤﻞ ﺗﺠﺰﻳﻪ ﺧﺎﺗﻤﻪ‬
‫ﻣﻲ ﻳﺎﺑﺪ‪.‬‬

‫‪٣١‬‬
‫‪ST‬‬

‫‪begin‬‬ ‫‪STS‬‬ ‫‪end‬‬

‫‪ε‬‬
‫ﺑﺎﻳﺪ ﺗﻮﺟﻪ داﺷﺖ ﻛﻪ اﻧﺘﺨﺎب ﻗﺎﻋﺪه ‪ ε‬ﺑﺮاي ﺑﺴﻂ ‪ STS‬ﺗﻨﻬﺎ در ﺻﻮرﺗﻲ ﻛﻪ ﺗﻮﻛﻦ ﺟﺎري در‬
‫آن ﻟﺤﻈﻪ ‪ end‬ﺑﺎﺷﺪ اﻧﺘﺨﺎب درﺳﺘﻲ ﺧﻮاﻫﺪ ﺑﻮد‪.‬‬

‫‪ 4-3‬ﻣﺸﻜﻞ ﭼﭗ ﮔﺮدي ) ‪( Left Recursion‬‬


‫ﮔﺮاﻣﺮي را ﭼﭗ ﮔﺮد ﮔﻮﻳﻨﺪ اﮔﺮ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺳﻤﺖ ﭼﭗ ﻳﻚ ﻗﺎﻋﺪه ﺑﻪ ﻋﻨﻮان اوﻟﻴﻦ ﻋﻼﻣﺖ‬
‫ﺳﻤﺖ راﺳﺖ آن ﻗﺎﻋﺪه ﻇﺎﻫﺮ ﺷﺪه ﺑﺎﺷﺪوﺑﻌﺒﺎرت دﻳﮕﺮ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ اي در ﮔﺮاﻣﺮ وﺟﻮد داﺷﺘﻪ‬
‫ﺑﺎﺷﺪ ﻛﻪ ﻗﺎﻋﺪه اي ﺑﺼﻮرت ‪ A → Aα‬داﺷﺘﻪ ﺑﺎﺷﺪ‪.‬‬
‫روﺷﻬﺎي ﭘﺎرس ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ را ﻧﻤﻲ ﺗﻮان ﺑﺮاي ﮔﺮاﻣﺮي ﻛﻪ ﭼﭗ ﮔﺮدي داﺷﺘﻪ ﺑﺎﺷﺪ ﺑﻜﺎر‬
‫ﺑﺮد‪.‬از اﻳﻨﺮو ﺑﺎﻳﺪ ﭼﭗ ﮔﺮدي ﮔﺮاﻣﺮ را ﺣﺬف ﻛﻨﻴﻢ ﻳﻌﻨﻲ ﮔﺮاﻣﺮ را ﺑﻪ ﮔﺮاﻣﺮ ﻣﻌﺎدﻟﻲ ﺗﺒﺪﻳﻞ‬
‫ﻛﻨﻴﻢ ﻛﻪ در آن ﭼﭙﮕﺮدي وﺟﻮد ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ‪.‬ﺑﺮاي ﻣﺜﺎل ﮔﺮاﻣﺮ ﭼﭗ ﮔﺮد ‪A → Aα | β‬‬
‫را ﻣﻲ ﺗﻮان ﺑﻔﺮم زﻳﺮ ﻛﻪ ﭼﭗ ﮔﺮدي ﻧﺪارد ﺗﺒﺪﻳﻞ ﻧﻤﻮد ‪:‬‬
‫'‪A → βA‬‬
‫‪A' → αA' | ε‬‬
‫ﻫﺮ دو ﮔﺮاﻣﺮ ﻓﻮق رﺷﺘﻪ ﻫﺎﻳﻲ ﺑﻔﺮم *‪ αβ‬را ﺗﻮﺻﻴﻒ ﻣﻲ ﻛﻨﻨﺪ‪.‬‬
‫روش ﻛﻠﻲ ﺣﺬف ﭼﭗ ﮔﺮدي ﺑﺼﻮرت زﻳﺮ اﺳﺖ ) ﺗﻮﺟﻪ ﻛﻨﻴﺪ ﻛﻪ اﻫﻤﻴﺘﻲ ﻧﺪارد ﻛﻪ ﭼﻪ‬
‫ﺗﻌﺪاد از ﻗﻮاﻋﺪ ﭼﭗ ﮔﺮد ﺑﺎﺷﻨﺪ( ‪:‬‬
‫ﺑﻄﻮر ﻛﻠﻲ اﮔﺮ داﺷﺘﻪ ﺑﺎﺷﻴﻢ ‪:‬‬
‫‪A → Aα1 | Aα2 | … | Aαm | β1 | β2 | … | βn‬‬

‫‪٣٢‬‬
‫در ﻗﻮاﻋﺪ ﻓﻮق ﻓﺮض ﺑﺮ اﻳﻦ اﺳﺖ ﻛﻪ ‪ βi‬ﻫﺎ ﻧﺒﺎﻳﺪ ﺑﺎ ‪ A‬ﺷﺮوع ﺷﻮﻧﺪ و ﻫﻴﭽﻜﺪام از ‪ αi‬ﻫﺎ‬
‫ﻧﺒﺎﻳﺪ‪ ε‬ﺑﺎﺷﻨﺪ‪.‬در اﻳﻨﺼﻮرت ﻣﻲ ﺗﻮان ﻗﻮاﻋﺪ زﻳﺮ را ﺑﺠﺎي ﻗﻮاﻋﺪ ﭼﭗ ﮔﺮدي ﻓﻮق ﺑﻜﺎر ﺑﺮد‪.‬‬

‫'‪A → β1A' | β2A' | … | βnA‬‬


‫‪A' → α1A' | α2A' | … | αmA' | ε‬‬

‫اﻳﻨﮕﻮﻧﻪ ﭼﭗ ﮔﺮدي ﻫﺎ را " ﭼﭗ ﮔﺮدي آﺷﻜﺎر " ) ‪( Immediate Left recursion‬‬


‫ﮔﻮﻳﻨﺪ‪.‬‬
‫ﻣﻤﻜﻦ اﺳﺖ ﭼﭗ ﮔﺮدي در ﺑﻴﺶ از ﻳﻚ ﻗﺪم ﻇﺎﻫﺮ ﺷﻮد ﻛﻪ ﺑﻪ آن ﭼﭗ ﮔﺮدي ﺿﻤﻨﻲ‬
‫ﮔﻮﻳﻨﺪ‪.‬ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل ﮔﺮاﻣﺮ زﻳﺮ داراي ﭼﭗ ﮔﺮدي ﺿﻤﻨﻲ اﺳﺖ‪:‬‬
‫‪S → Aa | b‬‬
‫‪A → Ac | Sd‬‬
‫در ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ‪ S‬ﭼﭗ ﮔﺮدي ﺿﻤﻨﻲ دارﻳﻢ زﻳﺮا ‪:‬‬
‫‪S ═› Aa ═› Sda‬‬

‫‪ 5-3‬ﺣﺬف ﭼﭗ ﮔﺮدي ﺿﻤﻨﻲ ) ‪( Implicit Left Recursion‬‬


‫ورودي اﻟﮕﻮرﻳﺘﻢ ﮔﺮاﻣﺮ ‪ G‬ﺑﺎ اﻳﻦ ﺷﺮط ﻛﻪ ﻗﺎﻋﺪه اﭘﺴﻴﻠﻮن ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ و ﻫﻴﭻ دوري ﻧﻴﺰ‬
‫‪+‬‬
‫در ﮔﺮاﻣﺮ ﻣﻮﺟﻮد ﻧﺒﺎﺷﺪ ﻳﻌﻨﻲ ﺑﺴﻄﻲ ﺑﺼﻮرت ‪ A ═› A‬در ﮔﺮاﻣﺮ ﻧﺒﺎﺷﺪ‪.‬ﺧﺮوﺟﻲ اﻟﮕﻮرﻳﺘﻢ‬
‫ﮔﺮاﻣﺮي ﻣﻌﺎدل ﮔﺮاﻣﺮ ‪ G‬اﻣﺎ ﻓﺎﻗﺪ ﭼﭗ ﮔﺮدي اﺳﺖ‪.‬اﺑﺘﺪا ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎي ﮔﺮاﻣﺮ را ﺑﻪ ﺗﺮﺗﻴﺐ‬
‫دﻟﺨﻮاه ‪ A1,A2,…,An‬ﻣﺮﺗﺐ ﻣﻲ ﻛﻨﻴﻢ‪.‬ﺳﭙﺲ اﻋﻤﺎل زﻳﺮ را ﺑﺼﻮرت ﻣﺸﺨﺺ ﺷﺪه در‬
‫ﺣﻠﻘﻪ ﻫﺎي ﺗﻜﺮار اﺟﺮا ﻣﻲ ﻛﻨﻴﻢ ‪:‬‬

‫‪For‬‬ ‫‪i := 1 to n do begin‬‬


‫‪For j := 1 to i-1 do begin‬‬
‫ﺑﺠﺎي ﻫﺮ ﻗﺎﻋﺪه ﺑﻪ ﺷﻜﻞ ‪ Ai → Ajγ‬ﻗﻮاﻋﺪ ‪ Ai → δ1γ | δ2γ | … | δkγ‬را ﻗﺮار دﻫﻴﺪ ﻛﻪ‬
‫در آن ‪ Aj → δ1 | δ2 | … | δk‬ﻗﻮاﻋﺪ ﻓﻌﻠﻲ ‪ Aj‬ﻫﺴﺘﻨﺪ‬
‫‪end‬‬
‫ﺣﺎل ﭼﭗ ﮔﺮدي آﺷﻜﺎر ﻗﻮاﻋﺪ ‪ Ai‬را ﺣﺬف ﻛﻨﻴﺪ‬
‫‪end‬‬

‫‪٣٣‬‬
‫ﺣﺎل ﺑﻌﻨﻮان ﻧﻤﻮﻧﻪ اﻟﮕﻮرﻳﺘﻢ ﻓﻮق را ﺑﺮاي ﮔﺮاﻣﺮ زﻳﺮ ﺑﻜﺎر ﻣﻲ ﺑﺮﻳﻢ ‪:‬‬
‫‪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' | ε‬‬

‫‪ 6-3‬ﻓﺎﻛﺘﻮرﮔﻴﺮي از ﭼﭗ ) ‪( Left factoring‬‬


‫ﺑﺎ اﺳﺘﻔﺎده از ﻓﺎﻛﺘﻮرﮔﻴﺮي از ﭼﭗ ﻣﻲ ﺗﻮان ﮔﺮاﻣﺮﻫﺎﻳﻲ ﻛﻪ در آﻧﻬﺎ ﺑﺮاي ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ‪ A‬دو‬
‫ﻗﺎﻋﺪه ﺑﺼﻮرت ‪ A → αβ1‬و ‪ A → αβ2‬وﺟﻮد دارد را ﻃﻮري ﺗﻐﻴﻴﺮ داد ﻛﻪ ﺑﺘﻮان‬
‫ﭘﺎرس ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ را ﺑﺮاي اﻳﻦ ﮔﺮاﻣﺮﻫﺎ اﺳﺘﻔﺎده ﻛﺮد‪.‬‬
‫ﻣﺸﻜﻞ اﻳﻦ ﻗﺒﻴﻞ ﮔﺮاﻣﺮﻫﺎ در اﻳﻦ اﺳﺖ ﻛﻪ روﺷﻦ ﻧﻴﺴﺖ ﻛﻪ از ﻛﺪاﻣﻴﻚ از اﻳﻦ ﻗﻮاﻋﺪ ﺑﺎﻳﺪ‬
‫ﺑﺮاي ﺑﺴﻂ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ‪ A‬اﺳﺘﻔﺎده ﻛﺮد‪.‬ﺑﻌﻨﻮان ﻣﺜﺎل ‪:‬‬
‫‪Stmt → if exp then Stmt else Stmt‬‬
‫‪| if Exp then Stmt‬‬
‫ﺑﺎ دﻳﺪن ‪ if‬در ورودي ﺑﻼﻓﺎﺻﻠﻪ ﻧﻤﻲ ﺗﻮان ﮔﻔﺖ ﻛﻪ از ﻛﺪام ﻗﺎﻋﺪه ﺑﺮاي ﺑﺴﻂ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ‬
‫‪ Stmt‬ﻣﻲ ﺗﻮان اﺳﺘﻔﺎده ﻛﺮد‪.‬در ﺣﺎﻟﺖ ﻛﻠﻲ اﮔﺮ ‪ A → αβ1 | αβ2‬دو ﻗﺎﻋﺪه ﻣﻮﺟﻮد در‬
‫ﮔﺮاﻣﺮي ﺑﺎﺷﻨﺪ و ورودي ﺑﺎ رﺷﺘﻪ ‪ α‬ﺷﺮوع ﺷﺪه ﺑﺎﺷﺪ ﻧﻤﻲ ﺗﻮان ﮔﻔﺖ ﻛﻪ ‪ A‬را ﺑﺎﻳﺪ‬
‫ﺑﺼﻮرت ‪ αβ1‬ﺑﺴﻂ داد و ﻳﺎ ﺑﺼﻮرت ‪. αβ2‬ﺑﺮاي رﻓﻊ اﻳﻦ ﻣﺸﻜﻞ از ‪ α‬ﻓﺎﻛﺘﻮر ﻣﻲ‬
‫ﮔﻴﺮﻳﻢ‪.‬ﮔﺮاﻣﺮ را ﺑﺼﻮرت زﻳﺮ ﺗﺒﺪﻳﻞ ﻣﻲ ﻛﻨﻴﻢ ‪:‬‬

‫‪٣٤‬‬
‫'‪A → αA‬‬
‫‪A' → β1 | β2‬‬

‫اﻟﮕﻮرﻳﺘﻢ ﻓﺎﻛﺘﻮرﮔﻴﺮي از ﭼﭗ در ﺣﺎﻟﺖ ﻛﻠﻲ ﺑﺼﻮرت زﻳﺮ اﺳﺖ ‪:‬‬


‫ﻗﻮاﻋﺪ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ ‪:‬‬
‫‪A → αβ1 | αβ2 | … | αβm | δ1 | δ2 | … | δn‬‬
‫ﻛﻪ در آن ‪ δi‬ﺑﻴﺎﻧﮕﺮ ﻗﻮاﻋﺪي اﺳﺖ ﻛﻪ ﺳﻤﺖ راﺳﺖ ﻫﻴﭻ ﻛﺪام ﺑﺎ ‪ α‬آﻏﺎز ﻧﺸﺪه اﺳﺖ‪.‬ﺑﺎ‬
‫ﻓﺎﻛﺘﻮرﮔﻴﺮي از ﭼﭗ ﻗﻮاﻋﺪ زﻳﺮ ﺣﺎﺻﻞ ﻣﻲ ﺷﻮﻧﺪ ‪:‬‬
‫‪A → αA' | δ1 | δ2 | … | δn‬‬
‫‪A' → β1 | β2 | … | βm‬‬
‫ﻛﻪ در آن '‪ A‬ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺟﺪﻳﺪ اﺳﺖ‪.‬‬

‫ﻣﺜﺎل‪ :‬ﻗﻮاﻋﺪ ﮔﺮاﻣﺮي ﺑﺼﻮرت زﻳﺮ ﻣﻲ ﺑﺎﺷﺪ ‪:‬‬


‫‪S→iEtS‬‬
‫‪S→iEtSeS|a‬‬
‫‪E→b‬‬
‫اﻳﻦ ﻗﻮاﻋﺪ ﺑﻌﺪ از اﻧﺠﺎم ﻋﻤﻞ ﻓﺎﻛﺘﻮرﮔﻴﺮي از ﭼﭗ ﺑﺼﻮرت زﻳﺮ ﺗﺒﺪﻳﻞ ﻣﻲ ﺷﻮﻧﺪ ‪:‬‬
‫‪S → i E t S S' | a‬‬
‫‪S' → e S | ε‬‬
‫‪E→b‬‬

‫‪ 7-3‬زﺑﺎﻧﻬﺎي ﻏﻴﺮ ﻣﺴﺘﻘﻞ از ﻣﺘﻦ ) ‪( Non-Context Free Languages‬‬


‫زﺑﺎﻧﻬﺎﻳﻲ ﻫﺴﺘﻨﺪ ﻛﻪ ﻧﻤﻲ ﺗﻮان آﻧﻬﺎ را ﺗﻮﺳﻂ ﻳﻚ ﮔﺮاﻣﺮ ﻣﺴﺘﻘﻞ از ﻣﺘﻦ ﺗﻮﺻﻴﻒ‬
‫ﻛﺮد‪.‬ﻫﻤﭽﻨﻴﻦ ﻣﺤﺪودﻳﺖ ﻫﺎﻳﻲ در زﺑﺎﻧﻬﺎي ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ وﺟﻮد دارد ﻛﻪ ﻧﻤﻲ ﺗﻮان آﻧﻬﺎ را‬
‫ﺗﻮﺳﻂ ﮔﺮاﻣﺮﻫﺎي ﻣﺴﺘﻘﻞ از ﻣﺘﻦ اﻋﻤﺎل ﻛﺮد‪.‬ﺑﻪ ﻣﺜﺎﻟﻬﺎي زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ ‪.‬‬

‫ﻣﺜﺎل ‪ – 1‬زﺑﺎن زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ ‪:‬‬


‫} *)‪L1 = { wcw | w is in (a|b‬‬

‫‪٣٥‬‬
‫اﻳﻦ زﺑﺎن رﺷﺘﻪ ﻫﺎﻳﻲ ﺑﺼﻮرت ‪ aabcaab‬ﺗﻮﻟﻴﺪ ﻣﻲ ﻛﻨﺪ‪.‬اﻳﻦ رﺷﺘﻪ ﻫﺎ را ﻣﻲ ﺗﻮان ﻣﺸﺎﺑﻪ اﻳﻦ‬
‫ﻣﺤﺪودﻳﺖ در زﺑﺎﻧﻬﺎي ﺑﺮﻧﺎﻣﻪ ﺳﺎزي در ﻧﻈﺮ ﮔﺮﻓﺖ ﻛﻪ ﺗﻌﺮﻳﻒ ﻣﺘﻐﻴﺮﻫﺎ ﺑﺎﻳﺴﺘﻲ ﻗﺒﻞ از‬
‫اﺳﺘﻔﺎده از آﻧﻬﺎ ﻗﺮار ﮔﻴﺮد‪.‬ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﻛﻪ ‪ w‬اول در ‪ wcw‬ﺑﻴﺎﻧﮕﺮ ﺗﻌﺮﻳﻒ ﻣﺘﻐﻴﺮ ﺑﻮده و‬
‫‪ w‬دوم ﻧﺸﺎن دﻫﻨﺪه اﺳﺘﻔﺎده از ﻣﺘﻐﻴﺮ ﻣﻲ ﺑﺎﺷﺪ‪.‬‬
‫‪declaration‬‬ ‫‪aab‬‬
‫‪begin‬‬ ‫‪w‬‬
‫‪.‬‬
‫‪c‬‬ ‫‪.‬‬
‫‪.‬‬
‫‪end‬‬
‫… = ‪aab‬‬
‫‪w‬‬

‫ﻣﺜﺎل ‪ – 2‬زﺑﺎن } ‪ L2 = { anbn1cndn1 | n1 >= 1 and n >= 1‬ﻧﻴﺰ ﻣﺴﺘﻘﻞ از‬


‫ﻣﺘﻦ ﻧﻴﺴﺖ‪.‬اﻳﻦ زﺑﺎن رﺷﺘﻪ ﻫﺎﻳﻲ ﺑﺼﻮرت ‪ a+b+c+d+‬ﺗﻮﻟﻴﺪ ﻣﻲ ﻛﻨﺪ ﻛﻪ در آﻧﻬﺎ ﺗﻌﺪاد‬
‫ﺗﻜﺮار ‪ a‬ﺑﺎ ‪ c‬ﺑﺮاﺑﺮ اﺳﺖ و ﺗﻌﺪاد ﺗﻜﺮارﻫﺎي ‪ b‬ﺑﺎ ‪ d‬ﺑﺮاﺑﺮ اﺳﺖ‪.‬اﻳﻦ زﺑﺎن ﻣﺸﺎﺑﻪ اﻳﻦ‬
‫ﻣﺤﺪودﻳﺖ در زﺑﺎﻧﻬﺎي ﺑﺮﻧﺎﻣﻪ ﺳﺎزي اﺳﺖ ﻛﻪ ﺗﻌﺪاد ﭘﺎراﻣﺘﺮﻫﺎي رﺳﻤﻲ در ﺗﻌﺮﻳﻒ ﻳﻚ‬
‫روﻳﻪ ﺑﺎﻳﺴﺘﻲ ﺑﺎ ﺗﻌﺪاد آرﮔﻮﻣﺎﻧﻬﺎ در ﻓﺮاﺧﻮاﻧﻲ ﻫﻤﺎن روﻳﻪ ﺑﺮاﺑﺮ ﺑﺎﺷﺪ‪.‬در اﻳﻨﺠﺎ ﻣﻲ ﺗﻮان ‪ an‬و‬
‫‪ bn1‬را ﺑﻴﺎﻧﮕﺮ ﭘﺎراﻣﺘﺮﻫﺎي رﺳﻤﻲ در ﺗﻌﺮﻳﻒ دو روﻳﻪ ﻛﻪ ﺑﻪ ﺗﺮﺗﻴﺐ داراي ‪ n‬و‪ n1‬ﭘﺎراﻣﺘﺮ‬
‫ورودي ﻫﺴﺘﻨﺪ در ﻧﻈﺮ ﮔﺮﻓﺖ‪.‬ﺑﻪ ﻫﻤﻴﻦ ﺗﺮﺗﻴﺐ ‪ dn1‬و ‪ cn‬را ﻣﻲ ﺗﻮان ﺑﻌﻨﻮان ﺗﻌﺪاد‬
‫آرﮔﻮﻣﺎﻧﻬﺎ در ﻓﺮاﺧﻮاﻧﻲ اﻳﻦ روﻳﻪ ﻫﺎ در ﻧﻈﺮ ﮔﺮﻓﺖ ‪:‬‬
‫‪dcl‬‬ ‫) ‪proc1( a,a,a‬‬
‫‪dcl‬‬ ‫) ‪proc2( b,b‬‬
‫‪.‬‬
‫‪.‬‬
‫‪.‬‬
‫‪Call‬‬ ‫) ‪proc1( c,c,c‬‬
‫‪Call‬‬ ‫) ‪proc2( d,d‬‬

‫‪٣٦‬‬
‫ﻣﺜﺎل ‪ – 3‬زﺑﺎن } ‪ L3 = { anbncn | n >= 1‬ﻧﻴﺰ ﻣﺴﺘﻘﻞ از ﻣﺘﻦ ﻧﻴﺴﺖ‪.‬اﻳﻦ زﺑﺎن رﺷﺘﻪ‬
‫ﻫﺎﻳﻲ ﺑﻔﺮم ‪ a+b+c+‬ﺗﻮﻟﻴﺪ ﻣﻲ ﻛﻨﺪ ﻛﻪ در آﻧﻬﺎ ﺗﻌﺪاد ‪ c , b , a‬ﺑﺮاﺑﺮ اﺳﺖ‪.‬اﻳﻦ زﺑﺎن ﻣﺸﺎﺑﻪ‬
‫ﻣﺴﺎﻟﻪ اﻳﺠﺎد ﻛﻠﻤﺎﺗﻲ ﻛﻪ در زﻳﺮ آﻧﻬﺎ ﺧﻂ ﻛﺸﻴﺪه ﺷﺪه ﺑﺎﺷﺪ )‪( Underlined Word‬‬
‫اﺳﺖ‪ .‬اﻳﻨﮕﻮﻧﻪ ﻛﻠﻤﺎت ﺑﻪ اﻳﻦ ﺻﻮرت ﭼﺎپ ﻣﻲ ﺷﻮﻧﺪ ﻛﻪ اﺑﺘﺪا ﻛﺎراﻛﺘﺮﻫﺎي ﻳﻚ ﻛﻠﻤﻪ ﭼﺎپ‬
‫ﺷﺪه و ﺑﺪﻧﺒﺎل آن ﺑﻪ ﺗﻌﺪاد ﻛﺎراﻛﺘﺮﻫﺎي آن ﻛﻠﻤﻪ ﺑﻪ ﻋﻘﺐ ﺑﺮﮔﺸﺘﻪ ) ﺑﺎ ﻛﻤﻚ ﻛﺎراﻛﺘﺮ‬
‫‪ ( BackSpace‬و ﺳﭙﺲ ﺑﻪ ﻫﻤﺎن ﺗﻌﺪاد ﻛﺎراﻛﺘﺮ " _ " ﭼﺎپ ﻣﻲ ﺷﻮد‪.‬ﻣﺜﻼ ﻛﻠﻤﻪ ‪Read‬‬
‫‪.‬در زﺑﺎن ‪ L3‬اﮔﺮ ‪ a‬ﺑﻴﺎﻧﮕﺮ ﺣﺮوف ‪ b ,‬ﺑﻴﺎﻧﮕﺮ ﻛﺎراﻛﺘﺮ ‪ BackSpace‬و ‪ c‬ﻧﻴﺰ ﺑﻴﺎﻧﮕﺮ‬
‫ﻛﺎراﻛﺘﺮ " _ " ﺑﺎﺷﺪ آﻧﮕﺎه اﻳﻦ زﺑﺎن ﻛﻠﻤﺎت ‪ Underlined‬را ﺗﻮﻟﻴﺪ ﻣﻲ ﻛﻨﺪ‪.‬‬

‫ﮔﺮاﻣﺮﻫﺎﻳﻲ وﺟﻮد دارﻧﺪ ﻛﻪ ﺑﺎ وﺟﻮد ﺷﺒﺎﻫﺖ ﺑﺴﻴﺎر ﺑﻪ ﮔﺮاﻣﺮﻫﺎي ‪ L3 , L2 , L1‬در ﻣﺜﺎﻟﻬﺎي‬


‫ﻓﻮق ‪ ,‬ﻣﺴﺘﻘﻞ از ﻣﺘﻦ ﻫﺴﺘﻨﺪ‪.‬ﺑﻌﻨﻮان ﻣﺜﺎل زﺑﺎن '‪ L1‬ﻛﻪ ﺑﺼﻮرت زﻳﺮ ﺗﻌﺮﻳﻒ ﺷﺪه اﺳﺖ‬
‫ﻣﺴﺘﻘﻞ از ﻣﺘﻦ اﺳﺖ ‪:‬‬
‫} *) ‪L1' = { wcwR | w is in (a | b‬‬

‫اﻳﻦ زﺑﺎن را ﻣﻲ ﺗﻮان ﺑﻮﺳﻴﻠﻪ ﮔﺮاﻣﺮ زﻳﺮ ﺗﻮﻟﻴﺪ ﻛﺮد ‪:‬‬


‫‪S → aSa | bSb | c‬‬

‫زﺑﺎن } ‪ 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‬‬

‫‪E :‬‬ ‫‪T‬‬ ‫'‪E‬‬ ‫‪2‬‬


‫‪0‬‬ ‫‪1‬‬

‫‪E' :‬‬ ‫‪+‬‬ ‫‪T‬‬ ‫'‪E‬‬


‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬

‫‪ε‬‬

‫‪٣٨‬‬
‫‪F‬‬ ‫'‪T‬‬
‫‪T :‬‬ ‫‪7‬‬ ‫‪8‬‬ ‫‪9‬‬

‫*‬ ‫‪F‬‬ ‫'‪T‬‬


‫‪T' :‬‬ ‫‪10‬‬ ‫‪11‬‬ ‫‪12‬‬
‫‪13‬‬

‫‪ε‬‬

‫(‬ ‫‪E‬‬ ‫)‬


‫‪F :‬‬ ‫‪14‬‬ ‫‪15‬‬ ‫‪16‬‬ ‫‪10‬‬

‫‪id‬‬

‫ﮔﺎﻫﻲ اوﻗﺎت دﻳﺎﮔﺮام ﻫﺎي اﻧﺘﻘﺎل را ﻣﻲ ﺗﻮان ﺳﺎده ﺗﺮ ﻛﺮد‪.‬ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل دﻳﺎﮔﺮام ﻫﺎي ﻓﻮق‬
‫را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ‪.‬‬

‫‪E' :‬‬ ‫‪+‬‬ ‫‪T‬‬ ‫'‪E‬‬


‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬

‫‪ε‬‬

‫‪+‬‬ ‫‪T‬‬
‫‪E' :‬‬ ‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬
‫‪ε‬‬
‫‪ε‬‬
‫‪6‬‬
‫‪T‬‬

‫‪E' :‬‬ ‫‪+‬‬


‫‪3‬‬ ‫‪4‬‬

‫‪ε‬‬
‫‪6‬‬

‫‪٣٩‬‬
‫و ﺑﺎ ﺟﺎﮔﺬاري اﻳﻦ دﻳﺎﮔﺮام ﺳﺎده ﺷﺪه در دﻳﺎﮔﺮام ‪ E‬ﺧﻮاﻫﻴﻢ داﺷﺖ ‪:‬‬

‫'‪E‬‬
‫‪E :‬‬ ‫‪T‬‬
‫‪2‬‬
‫‪0‬‬ ‫‪1‬‬

‫‪T‬‬

‫‪T‬‬
‫‪E :‬‬ ‫‪0‬‬ ‫‪3‬‬ ‫‪4‬‬
‫‪+‬‬

‫‪ε‬‬

‫‪6‬‬
‫‪+‬‬

‫‪E :‬‬ ‫‪0‬‬ ‫‪T‬‬ ‫‪ε‬‬


‫‪3‬‬ ‫‪6‬‬

‫ﺑﻪ ﻫﻤﻴﻦ ﺗﺮﺗﻴﺐ ﺑﺮاي دﻳﺎﮔﺮام ﻫﺎي '‪ T‬و‪ T‬ﺧﻮاﻫﻴﻢ داﺷﺖ ‪:‬‬

‫‪T' :‬‬ ‫‪F‬‬ ‫'‪T‬‬


‫*‬
‫‪10‬‬ ‫‪11‬‬ ‫‪12‬‬ ‫‪13‬‬

‫‪ε‬‬

‫‪ε‬‬

‫‪F‬‬
‫*‬
‫‪T' :‬‬ ‫‪10‬‬ ‫‪11‬‬ ‫‪12‬‬

‫‪ε‬‬

‫‪13‬‬
‫‪F‬‬

‫‪T' :‬‬ ‫*‬


‫‪10‬‬ ‫‪11‬‬

‫‪ε‬‬
‫‪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‬اﺳﺘﻔﺎده ﻣﻲ ﮔﺮدد‪.‬ﺑﺎﻓﺮ ورودي ﺷﺎﻣﻞ رﺷﺘﻪ اي اﺳﺖ ﻛﻪ ﺑﺎﻳﺪ ﺗﺠﺰﻳﻪ‬
‫ﺷﻮد‪.‬در اﻧﺘﻬﺎي رﺷﺘﻪ ورودي ﻋﻼﻣﺘﻲ ﻣﺜﻼ ‪ $‬ﻗﺮار ﻣﻲ ﮔﻴﺮد‪.‬ﺳﺎﺧﺘﺎر ﻛﻠﻲ اﻳﻦ ﻧﻮع ﭘﺎرﺳﺮ‬
‫ﺑﻔﺮم زﻳﺮ اﺳﺖ ‪:‬‬

‫‪a‬‬ ‫‪+‬‬ ‫‪b‬‬ ‫‪$‬‬ ‫‪input‬‬

‫‪X‬‬

‫‪Y‬‬ ‫ﺑﺮﻧﺎﻣﻪ ﭘﺎرس ﭘﻴﺸﮕﻮ‬ ‫‪Output‬‬

‫‪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‬ﺧﺎﻟﻲ‬
‫ﺑﺎﺷﺪ ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي رخ داده اﺳﺖ‪.‬‬

‫‪ 10 -3‬ﺗﻮاﺑﻊ ‪ First‬و ‪Follow‬‬


‫ﺑﺮاي ﭘﺮ ﻛﺮدن ﺟﺪول ﭘﺎرس از ﺗﻮاﺑﻌﻲ ﺑﻪ ﻧﺎﻣﻬﺎي ‪ First‬و ‪ Follow‬اﺳﺘﻔﺎده ﻣﻲ‬
‫ﺷﻮد‪.‬ﻫﻤﺎﻧﮕﻮﻧﻪ ﻛﻪ ﻗﺒﻼ ﺗﻮﺿﻴﺢ داده ﺷﺪ )‪ First(α‬ﻣﺠﻤﻮﻋﻪ ﭘﺎﻳﺎﻧﻪ ﻫﺎﻳﻲ اﺳﺖ ﻛﻪ ﺑﻌﻨﻮان‬
‫ﺳﻤﺖ ﭼﭗ ﺗﺮﻳﻦ ﻋﻼﻣﺖ رﺷﺘﻪ ﻫﺎي ﺑﺪﺳﺖ آﻣﺪه از ‪ α‬ﻗﺮار ﻣﻲ ﮔﻴﺮﻧﺪ‪.‬در ﺻﻮرﺗﻴﻜﻪ‬
‫‪ α ═* › ε‬در اﻳﻨﺼﻮرت ‪ ε‬ﻧﻴﺰ ﺟﺰو )‪ First(α‬ﺧﻮاﻫﺪ ﺑﻮد‪.‬در اداﻣﻪ اﻟﮕﻮرﻳﺘﻢ ﻣﺤﺎﺳﺒﻪ ‪First‬‬
‫ﻳﻚ ﻋﻼﻣﺖ ﻣﺜﻞ ‪ X‬ﺗﻮﺿﻴﺢ داده ﺷﺪه اﺳﺖ‪.‬اﮔﺮﭼﻪ اﻟﮕﻮرﻳﺘﻢ در ﻣﻮرد ﻳﻚ ﻋﻼﻣﺖ ﺑﻴﺎن‬
‫ﻣﻲ ﮔﺮدد ﻟﻴﻜﻦ ﺑﺎ ﻛﻤﻚ آن ﻣﻲ ﺗﻮان ﻣﺠﻤﻮﻋﻪ ‪ First‬را ﺑﺮاي رﺷﺘﻪ ﻫﺎ ﻧﻴﺰ ﻣﺤﺎﺳﺒﻪ ﻧﻤﻮد‪.‬‬
‫ﺑﺮاي ﭘﻴﺪا ﻛﺮدن )‪ First(X‬ﺑﺼﻮرت زﻳﺮ ﻋﻤﻞ ﻣﻲ ﺷﻮد) ‪ X‬ﻣﻲ ﺗﻮاﻧﺪ ﻳﻚ ﭘﺎﻳﺎﻧﻪ ﻳﺎ ﻳﻚ‬
‫ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﺎﺷﺪ ( ‪:‬‬
‫‪ -1‬اﮔﺮ ‪ X‬ﻳﻚ ﭘﺎﻳﺎﻧﻪ ﺑﺎﺷﺪ در آﻧﺼﻮرت }‪First(X) = {X‬‬
‫‪ -2‬اﮔﺮ ﻗﺎﻋﺪه اي ﺑﺼﻮرت ‪ X → ε‬در ﮔﺮاﻣﺮ ﺑﺎﺷﺪ ‪ ε‬را ﺑﻪ )‪ First(X‬اﺿﺎﻓﻪ ﻣﻲ‬
‫ﻛﻨﻴﻢ‪.‬‬

‫‪٤٣‬‬
‫‪ -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 → aB‬‬
‫‪B → Cb | d‬‬
‫‪C→ε|c‬‬

‫} ‪First(A) = {a} , First(B) = { c,b,d‬‬ ‫‪,‬‬ ‫} ‪First(C) = {ε , c‬‬

‫ﺑﺮاي ﺑﺪﺳﺖ آوردن )‪ 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‬اﺿﺎﻓﻪ ﻣﻲ ﻛﻨﻴﻢ‪.‬‬

‫ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ ‪:‬‬


‫‪1‬‬ ‫'‪E → T E‬‬

‫‪٤٤‬‬
‫‪2–3‬‬ ‫‪E' → + T E' | ε‬‬
‫‪4‬‬ ‫'‪T → F T‬‬
‫‪5–6‬‬ ‫‪T' → * F T' | ε‬‬
‫‪7–8‬‬ ‫‪F → ( E ) | id‬‬

‫ﻣﺠﻤﻮﻋﻪ ﻫﺎي ‪Follow‬‬ ‫ﻣﺠﻤﻮﻋﻪ ﻫﺎي ‪First‬‬


‫} ‪Follow(E) = { ) , $‬‬ ‫} ‪First(E) = { ( , id‬‬
‫} ‪Follow(T) = { + , ) , $‬‬ ‫} ‪First(T) = { ( , id‬‬
‫} ) ‪Follow(F) = { * , + , $ ,‬‬ ‫} ‪First(F) = { ( , id‬‬
‫} ‪Follow(E') = { ) , $‬‬ ‫} ‪First(E') = { ε , +‬‬
‫} ‪Follow(T') = { + , ) , $‬‬ ‫} * ‪First(T') = { ε ,‬‬

‫ﻧﺤﻮه ﺗﺸﻜﻴﻞ ﺟﺪول ﭘﺎرس ﺑﺮاي ﭘﺎرﺳﺮ ﻫﺎي ﭘﻴﺸﮕﻮ ‪:‬‬


‫‪ -1‬ﺑﺮاي ﻫﺮ ﻗﺎﻋﺪه ﺑﺼﻮرت ‪ A → α‬در ﮔﺮاﻣﺮ ﻗﺪﻣﻬﺎي ‪ 2‬و ‪ 3‬را اﻧﺠﺎم ﻣﻲ دﻫﻴﻢ‪.‬‬
‫‪ -2‬ﺑﺮاي ﻫﺮ ﭘﺎﻳﺎﻧﻪ ‪ a‬در )‪ , First(α‬ﺷﻤﺎره ﻗﺎﻋﺪه ‪ A → α‬را ﺑﻪ ﺧﺎﻧﻪ ]‪M [ A , a‬‬
‫اﺿﺎﻓﻪ ﻣﻲ ﻛﻨﻴﻢ‪.‬‬
‫‪ -3‬اﮔﺮ ‪ ε‬در )‪ First(α‬وﺟﻮد داﺷﺖ ﺷﻤﺎره ﻗﺎﻋﺪه ‪ A → α‬را در ﺧﺎﻧﻪ ﻫﺎي [ ‪M‬‬
‫] ‪ M [ A , b‬ﺑﻪ ازاي ﻫﺮ )‪ b Є Follow(A‬ﻗﺮار ﻣﻲ دﻫﻴﻢ‪.‬‬

‫ﺟﺪول ﭘﺎرس ﮔﺮاﻣﺮ ﻣﺜﺎل ﻗﺒﻞ ﺑﻔﺮم زﻳﺮ اﺳﺖ ‪:‬‬


‫‪id‬‬ ‫(‬ ‫)‬
‫‪+‬‬ ‫*‬ ‫‪$‬‬

‫‪E‬‬ ‫‪1‬‬ ‫‪1‬‬


‫'‪E‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪3‬‬
‫‪T‬‬ ‫‪4‬‬ ‫‪4‬‬
‫'‪T‬‬ ‫‪6‬‬ ‫‪5‬‬ ‫‪6‬‬ ‫‪6‬‬
‫‪F‬‬ ‫‪8‬‬ ‫‪7‬‬

‫‪٤٥‬‬
: ‫ را ﺗﺠﺰﻳﻪ ﻣﻲ ﻛﻨﻴﻢ‬id + id * id ‫ﺣﺎل ﺑﺎ اﺳﺘﻔﺎده از ﺟﺪول ﻓﻮق ﻋﺒﺎرت‬

‫ﻣﺤﺘﻮاي اﻧﺒﺎره‬ ‫ورودي‬ ‫ﻗﻮاﻋﺪ اﺳﺘﻔﺎده ﺷﺪه‬


$E id + id * id $ E → T E'
$ E' T id + id * id $ T → F T'
$ E' T' F id + id * id $ F → id
$ E' T' id id + id * id $
$ E' T' + id * id $ T' → ε
$ E' + id * id $ E' → + T E'
$ E' T + + id * id $
$ E' T id * id $ T → F T'
$ E' T' F id * id $ F → id
$ E' T' id id * id $
$ E' T' * id $ T' → * F T'
$ E' T' F * * id $ F → id
$ E' T' F id $
$ E' T' id id $
$ E' T' $ T' → ε
$ E' $ E' → ε
$ $ ‫ﭘﺎﻳﺎن ﭘﺎرس‬

٤٦
‫ﮔﺮاﻣﺮﻫﺎي )‪LL(1‬‬
‫در ﺻﻮرﺗﻴﻜﻪ از روش ﻓﻮق ﺑﺮاي اﻳﺠﺎد ﺟﺪول ﭘﺎرس ﮔﺮاﻣﺮﻫﺎي ﮔﻨﮓ و ﻳﺎ ﭼﭗ ﮔﺮد‬
‫اﺳﺘﻔﺎده ﺷﻮد در ﺑﺮﺧﻲ از ﺧﺎﻧﻪ ﻫﺎي ﺟﺪول ﭘﺎرس ﺑﻴﺶ از ﻳﻚ ﺷﻤﺎره ﻗﺎﻋﺪه ﺧﻮاﻫﻴﻢ‬
‫داﺷﺖ‪.‬ﺑﻌﺒﺎرت دﻳﮕﺮ اﮔﺮ در ﺧﺎﻧﻪ ﻫﺎي ﺟﺪول ﭘﺎرس ﻳﻚ ﮔﺮاﻣﺮ ﻣﺴﺘﻘﻞ از ﻣﺘﻦ ﺣﺪاﻛﺜﺮ‬
‫ﻳﻚ ﺷﻤﺎره ﻗﺎﻋﺪه ﺑﺎﺷﺪ ﮔﺮاﻣﺮ ﻣﺮﺑﻮﻃﻪ را )‪ LL(1‬ﮔﻮﻳﻨﺪ‪.‬‬
‫ﻣﺜﺎل – ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ ‪:‬‬
‫‪1–2‬‬ ‫‪S → i E t S S' | a‬‬
‫‪3–4‬‬ ‫‪S' → e S | ε‬‬
‫‪5‬‬ ‫‪E→b‬‬

‫ﺟﺪول ﭘﺎرس ﮔﺮاﻣﺮ ﻓﻮق ﺑﺼﻮرت زﻳﺮ اﺳﺖ ‪:‬‬


‫‪i‬‬ ‫‪t‬‬ ‫‪a‬‬ ‫‪e‬‬ ‫‪b‬‬ ‫‪$‬‬

‫‪S‬‬ ‫‪1‬‬ ‫‪2‬‬


‫'‪S‬‬ ‫‪3,4‬‬ ‫‪4‬‬
‫‪E‬‬ ‫‪5‬‬

‫اﻳﻦ ﮔﺮاﻣﺮ )‪ LL(1‬ﻧﻴﺴﺖ زﻳﺮا در ﺧﺎﻧﻪ ]‪ M[S',e‬ﺟﺪول ﺗﺠﺰﻳﻪ آن دو ﺷﻤﺎره ﻗﺎﻋﺪه ﻗﺮار‬
‫دارد‪.‬‬

‫ﺑﺮاي ﭘﻲ ﺑﺮدن ﺑﻪ )‪ LL(1‬ﺑﻮدن ﻳﻚ ﮔﺮاﻣﺮ ﻻزم ﻧﻴﺴﺖ ﻛﻪ ﺣﺘﻤﺎ ﺟﺪول ﺗﺠﺰﻳﻪ آن ﺑﺪﺳﺖ‬
‫آﻳﺪ‪.‬ﺑﺎ ﺑﺮرﺳﻲ ﺷﺮاﻳﻂ زﻳﺮ ﻧﻴﺰ ﻣﻲ ﺗﻮان )‪ LL(1‬ﺑﻮدن ﻳﻚ ﮔﺮاﻣﺮ را ﺑﺮرﺳﻲ ﻧﻤﻮد‪.‬ﺑﻌﺒﺎرت‬
‫دﻳﮕﺮ ﮔﺮاﻣﺮي )‪ LL(1‬اﺳﺖ ﻛﻪ ﺷﺮاﻳﻂ زﻳﺮ در ﻣﻮرد ﻗﻮاﻋﺪ ﺑﺼﻮرت ‪ A → α | β‬آن‬
‫ﺻﺪق ﻛﻨﺪ ‪:‬‬

‫‪First(β) ∩ First(α) = ф .1‬‬


‫‪ .2‬ﺣﺪاﻛﺜﺮ ﻳﻜﻲ از رﺷﺘﻪ ﻫﺎي ‪ α‬و ‪ β‬رﺷﺘﻪ ‪ ε‬را ﺗﻮﻟﻴﺪ ﻛﻨﻨﺪ‪.‬‬
‫‪ .3‬اﮔﺮ ‪ α ═›* ε‬در آﻧﺼﻮرت ‪First(β) ∩ Follow(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(+TE') = { + } , First(ε) = { ε } , { ε } ∩ { + } = ф‬‬


‫‪First(+TE') = { + } , Follow(E') = { $,) } , { + } ∩ { $,) } = ф‬‬
‫و ﺑﺮاي ﻏﻴﺮﭘﺎﻳﺎﻧﻪ '‪ T‬دارﻳﻢ ‪:‬‬
‫‪First(*FT') = { * } , First(ε) = { ε } , { ε } ∩ { * } = ф‬‬
‫‪First(*FT') = { * } , Follow(T') = { $,),+ } , { * } ∩ { $,),+ } = ф‬‬
‫ﺣﺎل ﺷﺮاﻳﻂ را ﺑﺮاي ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ‪ F‬ﺑﺮرﺳﻲ ﻣﻲ ﻛﻨﻴﻢ ‪:‬‬
‫‪First((E)) = { ( } , First(id) = { id } , { ( } ∩ { id } = ф‬‬
‫ﻟﺬا ﮔﺮاﻣﺮ )‪ LL(1‬اﺳﺖ‪.‬‬

‫ﺣﺎل ﺑﻌﻨﻮان ﻳﻚ ﻣﺜﺎل دﻳﮕﺮ ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ ‪:‬‬


‫‪S → i e t S S' | a‬‬
‫‪S' → e S | ε‬‬
‫‪E→b‬‬

‫ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ آﻧﻜﻪ ‪ ε‬ﻋﻀﻮ )'‪ First(S‬اﺳﺖ اﮔﺮ ﺷﺮط ﺳﻮم را در ﻣﻮرد ﻏﻴﺮﭘﺎﻳﺎﻧﻪ '‪ S‬ﭼﻚ‬
‫ﻛﻨﻴﻢ ﻣﺸﺨﺺ ﺧﻮاﻫﺪ ﺷﺪ ﻛﻪ اﻳﻦ ﮔﺮاﻣﺮ )‪ LL(1‬ﻧﻴﺴﺖ‪.‬‬
‫‪First(eS) = { e } , Follow(S') = { e,$ } , { e,$ } ∩ { e } ≠ ф‬‬

‫ﮔﺮاﻣﺮﻫﺎﻳﻲ ﻛﻪ ﭼﭗ ﮔﺮدي داﺷﺘﻪ ﺑﺎﺷﻨﺪ )‪ LL(1‬ﻧﻴﺴﺘﻨﺪ‪.‬ﺑﺮﺧﻲ از ﮔﺮاﻣﺮﻫﺎ را ﻣﻲ ﺗﻮان ﺑﺎ‬


‫ﺗﺒﺪﻳﻞ ﻛﺮد‪.‬وﻟﻲ‬ ‫ﺣﺬف ﭼﭗ ﮔﺮدي و ﻓﺎﻛﺘﻮرﮔﻴﺮي از ﭼﭗ ﺑﻪ ﮔﺮاﻣﺮ )‪LL(1‬‬

‫‪٤٨‬‬
‫ﻓﺎﻛﺘﻮرﮔﻴﺮي و ﺣﺬف ﭼﭗ ﮔﺮدي ﺑﺎﻋﺚ از ﺑﻴﻦ رﻓﺘﻦ ﺧﻮاﻧﺎﻳﻲ ﮔﺮاﻣﺮﻫﺎ ﻣﻲ ﺷﻮﻧﺪ‪.‬در ﺿﻤﻦ‬
‫ﺗﻮﻟﻴﺪ ﻛﺪ را ﻧﻴﺰ ﻣﺸﻜﻞ ﺗﺮ ﻣﻲ ﻧﻤﺎﻳﻨﺪ‪.‬‬

‫‪ 11 -3‬روش ﻫﺎي اﺻﻼح ﺧﻄﺎي ﻧﺤﻮي در روش ﺗﺠﺰﻳﻪ )‪LL(1‬‬

‫از ﻣﻬﻤﺘﺮﻳﻦ روﺷﻬﺎي اﺻﻼح ﺧﻄﺎي ﻗﺎﺑﻞ اﺳﺘﻔﺎده در ﺗﺠﺰﻳﻪ )‪ 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‬‬

‫ﻣﺠﻤﻮﻋﻪ ‪ Follow‬ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎ را ﺑﻪ ﻋﻨﻮان ﻣﺠﻤﻮﻋﻪ ‪ Synchronizing‬آﻧﻬﺎ در ﻧﻈﺮ‬


‫ﮔﺮﻓﺘﻪ و در ﺟﺪول ﺗﺠﺰﻳﻪ در ﻣﻘﺎﺑﻞ ‪ Follow‬ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎ ﺑﺎ ﮔﺬاردن ﻋﻼﻣﺘﻲ ﻣﺜﻞ " ‪" S‬‬
‫ﻣﺠﻤﻮﻋﻪ ‪ Synchronizing‬ﻫﺮ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ را ﻣﻌﻴﻦ ﻣﻲ ﻛﻨﻴﻢ‪.‬ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﺟﺪول ﭘﺎرس‬
‫ﮔﺮاﻣﺮ ﻓﻮق ﺑﺼﻮرت زﻳﺮ در ﺧﻮاﻫﺪ آﻣﺪ ‪:‬‬

‫‪id‬‬ ‫‪+‬‬ ‫*‬ ‫(‬ ‫)‬ ‫‪$‬‬

‫‪E‬‬ ‫‪1‬‬ ‫‪1‬‬ ‫‪S‬‬ ‫‪S‬‬


‫'‪E‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪3‬‬
‫‪T‬‬ ‫‪4‬‬ ‫‪S‬‬ ‫‪4‬‬ ‫‪S‬‬ ‫‪S‬‬
‫'‪T‬‬ ‫‪6‬‬ ‫‪5‬‬ ‫‪6‬‬ ‫‪6‬‬
‫‪F‬‬ ‫‪8‬‬ ‫‪S‬‬ ‫‪S‬‬ ‫‪7‬‬ ‫‪S‬‬ ‫‪S‬‬

‫ﺑﺮاي اﺻﻼح ﺧﻄﺎ ﺑﻪ روش ‪ Panic Mode‬در اﻟﮕﻮرﻳﺘﻢ ﺗﺠﺰﻳﻪ )‪ LL(1‬ﺑﺼﻮرت‬


‫زﻳﺮ ﻋﻤﻞ ﻣﻲ ﻛﻨﻴﻢ ‪:‬‬
‫‪ -1‬اﮔﺮ ﭘﺎرﺳﺮ ﺧﺎﻧﻪ ] ‪ M [ A , a‬را ﺧﺎﻟﻲ ﺑﺒﻴﻨﺪ ﻋﻼﻣﺖ ‪ a‬را در ورودي ﻧﺎدﻳﺪه ﻣﻲ‬
‫ﮔﻴﺮد‪.‬‬
‫‪ -2‬اﮔﺮ در ﻣﺤﻞ ﺧﺎﻧﻪ ] ‪ M [ A , a‬ﻋﻼﻣﺖ " ‪ " S‬ﺑﺎﺷﺪ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﺎﻻي اﻧﺒﺎره ﺣﺬف‬
‫ﻣﻲ ﺷﻮد‪.‬ﻣﺸﺮوط ﺑﺮآﻧﻜﻪ ‪ A‬ﺗﻨﻬﺎ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻣﻮﺟﻮد در اﻧﺒﺎره ﻧﺒﺎﺷﺪ‪.‬‬
‫‪ -3‬اﮔﺮ ﭘﺎﻳﺎﻧﻪ ﺑﺎﻻي اﻧﺒﺎره ﺑﺎ ورودي ﺟﺎري ﺗﻄﺒﻴﻖ ﻧﻜﻨﺪ ﭘﺎﻳﺎﻧﻪ ﺑﺎﻻي اﻧﺒﺎره ﺣﺬف ﻣﻲ‬
‫ﺷﻮد‪.‬‬

‫‪٥٠‬‬
‫‪ 12 -‬ﺗﺠﺰﻳﻪ ﭘﺎﻳﻴﻦ ﺑﻪ ﺑﺎﻻ ) ‪( Bottom-Up Parsing‬‬ ‫‪3‬‬
‫ﻳﻚ روش ﻛﻠﻲ ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﺑﻪ روش اﻧﺘﻘﺎل – ﻛﺎﻫﺶ ) ‪( Shift – Reduce‬‬
‫اﺳﺖ‪.‬در اﻳﻦ روش ﻋﻜﺲ ﺗﺠﺰﻳﻪ ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ ﻋﻤﻞ ﻣﻲ ﺷﻮد‪.‬ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﻛﻪ از رﺷﺘﻪ‬
‫ورودي ﺷﺮوع ﻛﺮده و ﺳﺎﺧﺖ درﺧﺖ ﺗﺠــﺰﻳﻪ از ﺑﺮگ ﻫﺎ آﻏﺎز ﮔــﺸﺘﻪ و ﺑﻪ ﻃﺮف رﻳــﺸﻪ‬
‫) ﻋﻼﻣﺖ ﺷﺮوع ( ﭘﻴﺶ ﻣﻲ رود‪.‬‬
‫ﺗﺮﺗﻴﺐ ﺑﻜﺎرﮔﻴﺮي ﻗﻮاﻋﺪ در ﭘﺎرس ﺑﺎﻻ ﺑﻪ ﭘﺎﺋﻴﻦ درﺳﺖ ﻣﻄﺎﺑﻖ ﺑﺴﻂ ﭼﭗ اﺳﺖ درﺣﺎﻟﻴﻜﻪ‬
‫ﺗﺮﺗﻴﺐ ﺑﻜﺎرﮔﻴﺮي ﻗﻮاﻋﺪ در اﻛﺜﺮ روﺷﻬﺎي ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ درﺳﺖ ﻋﻜﺲ ﺑﺴﻂ راﺳﺖ‬
‫اﺳﺖ‪.‬ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ ‪:‬‬
‫‪1‬‬ ‫‪S → aABe‬‬
‫‪2–3‬‬ ‫‪A → Abc | b‬‬
‫‪4‬‬ ‫‪B→d‬‬
‫ﺟﻤﻠﻪ ‪ abbcde‬را ﻣﻮرد ﺑﺮرﺳﻲ ﻗﺮار ﻣﻲ دﻫﻴﻢ‪.‬ﺑﺴﻂ راﺳﺖ اﻳﻦ ﺟﻤﻠﻪ ﺑﺼﻮرت زﻳﺮ اﺳﺖ ‪:‬‬

‫‪1‬‬ ‫‪4‬‬ ‫‪2‬‬ ‫‪3‬‬

‫‪S ═› aABe ═› aAde ═› aAbcde ═› abbcde‬‬


‫‪rm‬‬ ‫‪rm‬‬ ‫‪rm‬‬ ‫‪rm‬‬

‫ﻛﻪ در آن ﺗﺮﺗﻴﺐ ﺑﻜﺎرﮔﻴﺮي ﻗﻮاﻋﺪ ﮔﺮاﻣﺮ ﺑﺼﻮرت ‪ ) 1,4,2,3‬از ﭼﭗ ﺑﻪ راﺳﺖ (‬


‫اﺳﺖ‪.‬ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻي رﺷﺘﻪ ﻓﻮق در ﺟﺪول زﻳﺮ آﻣﺪه اﺳﺖ ‪:‬‬

‫دﺳﺘﮕﻴﺮه‬ ‫ﺷﻤﺎره ﻗﺎﻋﺪه‬ ‫ﻓﺮم ﺟﻤﻠﻪ اي ﺗﺤﺖ ﺗﺠﺰﻳﻪ‬ ‫ﻣﺮﺣﻠﻪ ﺗﺠﺰﻳﻪ‬


‫‪S‬‬
‫‪aABe‬‬ ‫‪1‬‬ ‫‪aABe‬‬ ‫‪4‬‬
‫‪d‬‬ ‫‪4‬‬ ‫‪aAde‬‬ ‫‪3‬‬
‫‪Abc‬‬ ‫‪2‬‬ ‫‪aAbcde‬‬ ‫‪2‬‬
‫‪b‬‬ ‫‪3‬‬ ‫‪abbcde‬‬ ‫‪1‬‬

‫‪٥١‬‬
‫ﺑﻪ اﻳﻦ ﺗﺮﺗﻴﺐ ﺟﻤﻠﻪ ‪ abbcde‬ﺑﻪ ﻋﻼﻣﺖ ﺷﺮوع ﮔﺮاﻣﺮ ﻛﺎﻫﺶ ﻣﻲ ﻳﺎﺑﺪ‪.‬ﺗﺮﺗﻴﺐ ﻋﻤﻠﻴﺎت در‬
‫اﻳﻦ ﻛﺎﻫﺶ درﺳﺖ ﺑﺮﻋﻜﺲ ﺑﺴﻂ راﺳﺖ ﺻﻮرت ﮔﺮﻓﺘﻪ اﺳﺖ‪.‬در ﻫﺮ ﻣﺮﺣﻠﻪ از ﻛﺎﻫﺶ در‬
‫ﭘﺎرس ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ اﻳﻦ ﻣﺸﻜﻞ وﺟﻮد دارد ﻛﻪ ﭘﺎرﺳﺮ ﻛﺪام زﻳﺮ رﺷﺘﻪ را ﺑﻪ ﻋﻨﻮان دﺳﺘﮕﻴﺮه‬
‫اﻧﺘﺨﺎب و ﺳﭙﺲ از ﻛﺪام ﻗﺎﻋﺪه ﺑﺮاي ﻛﺎﻫﺶ آن اﺳﺘﻔﺎده ﻧﻤﺎﻳﺪ‪.‬در اداﻣﻪ ﺑﻪ اراﺋﻪ ﭼﻨﺪ ﺗﻌﺮﻳﻒ‬
‫در ارﺗﺒﺎط ﺑﺎ ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﻣﻲ ﭘﺮدازﻳﻢ ‪:‬‬
‫ﻋﺒﺎرت ) ‪ : ( Phrase‬ﺑﺨﺸﻲ از ﻳﻚ ﻓﺮم ﺟﻤﻠﻪ اي اﺳﺖ ﻛﻪ از ﻳﻚ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﻮﺟﻮد‬
‫آﻣﺪه ﺑﺎﺷﺪ‪.‬ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل در ﺑﺴﻂ زﻳﺮ ‪ β‬ﻳﻚ ﻋﺒﺎرت ﻣﺤﺴﻮب ﻣﻲ ﺷﻮد‪.‬‬
‫*‬ ‫‪+‬‬
‫═‪S‬‬ ‫‪› αAγ ═› αβγ‬‬

‫ﻋﺒﺎرت ﺳﺎده ) ‪ : ( Simple Phrase‬ﻋﺒﺎرﺗﻲ اﺳﺖ ﻛﻪ در ﻳﻚ ﻗﺪم ﺑﻮﺟﻮد آﻣﺪه‬


‫ﺑﺎﺷﺪ‪.‬ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل در ﺑﺴﻂ زﻳﺮ ‪ β‬ﻳﻚ ﻋﺒﺎرت ﺳﺎده اﺳﺖ‪.‬‬
‫*‬
‫‪S ═› αАγ ═› αβγ‬‬

‫دﺳﺘﮕﻴﺮه ) ‪ : ( Handle‬ﻋﺒﺎرت ﺳﺎده اي اﺳﺖ ﻛﻪ در ﺟﻬﺖ ﻋﻜﺲ ﻳﻚ ﺑﺴﻂ راﺳﺖ‬


‫ﺗﻮﻟﻴﺪ ﺷﺪه ﺑﺎﺷﺪ‪.‬در ﻣﺜﺎل زﻳﺮ ‪ β‬ﻳﻚ دﺳﺘﮕﻴﺮه اﺳﺖ‪.‬ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ از آﻧﺠﺎﺋﻴﻜﻪ‬
‫دﺳﺘﮕﻴﺮه در راﺑﻄﻪ ﺑﺎ ﺑﺴﻂ راﺳﺖ ﻣﻄﺮح اﺳﺖ ﺳﻤﺖ راﺳﺖ دﺳﺘﮕﻴﺮه ﻫﻴﭻ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ اي‬
‫ﻧﻴﺴﺖ‪.‬ﺑﻪ ﻫﻤﻴﻦ ﺧﺎﻃﺮ در ﻣﺜﺎل زﻳﺮ از " ‪ " x‬ﺑﺮاي ﻧﻤﺎﻳﺶ زﻳﺮ رﺷﺘﻪ ﺳﻤﺖ راﺳﺖ دﺳﺘﮕﻴﺮه‬
‫اﺳﺘﻔﺎده ﺷﺪه اﺳﺖ‪.‬‬
‫*‬
‫‪S ═› αAx ═› αβx‬‬

‫اﮔﺮ ﮔﺮاﻣﺮ ﻣﻮرد اﺳﺘﻔﺎده ﮔﻨﮓ ﻧﺒﺎﺷﺪ در ﻫﺮ ﻣﺮﺣﻠﻪ از ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﺗﻨﻬﺎ ﻳﻚ دﺳﺘﮕﻴﺮه‬
‫وﺟﻮد دارد‪.‬ﻟﻴﻜﻦ در ﺻﻮرت اﺳﺘﻔﺎده از ﻳﻚ ﮔﺮاﻣﺮ ﮔﻨﮓ ﻣﻤﻜﻦ اﺳﺖ در ﺑﻌﻀﻲ از ﻗﺪم ﻫﺎ‬
‫ﺑﻴﺸﺘﺮ از ﻳﻚ دﺳﺘﮕﻴﺮه ﻣﻮﺟﻮد ﺑﺎﺷﺪ‪.‬ﺑﻪ ﻣﺜﺎل زﻳﺮ ﺗﻮﺟﻪ ﻛﻨﻴﺪ ‪:‬‬
‫‪1–4‬‬ ‫‪E → E + E | E * E | ( E ) | id‬‬

‫از آﻧﺠﺎ ﻛﻪ ﮔﺮاﻣﺮ ﻓﻮق ﮔﻨﮓ اﺳﺖ ﺑﺮاي ﺟﻤﻠﻪ ‪ id + id * id‬دو ﺑﺴﻂ راﺳﺖ و در ﻧﺘﻴﺠﻪ‬
‫دو ﻣﺴﻴﺮ ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ وﺟﻮد دارد‪.‬اﻳﻦ دو ﺑﺴﻂ در اداﻣﻪ ﻧﺸﺎن داده ﻣﻲ ﺷﻮد‪.‬ﻫﻤﺎﻧﮕﻮﻧﻪ‬
‫ﻛﻪ ﻣﺸﺎﻫﺪه ﻣﻲ ﺷﻮد در ﻗﺪم ﺳﻮم ﺗﺠﺰﻳﻪ دو اﻧﺘﺨﺎب ﺑﺮاي دﺳﺘﮕﻴﺮه وﺟﻮد دارد‪.‬‬

‫‪٥٢‬‬
‫‪E ═› E + E‬‬ ‫‪E ═› E * E‬‬

‫‪═› E + E * E‬‬ ‫‪═› E * id‬‬

‫‪═› E + E * id‬‬ ‫‪═› E + E * id‬‬

‫‪═› E + id * id‬‬ ‫‪═› E + id * id‬‬

‫‪═› id + id * id‬‬ ‫‪═› id + id * id‬‬

‫‪ 13 -3‬ﭘﻴﺎده ﺳﺎزي روش ﺗﺠﺰﻳﻪ اﻧﺘﻘﺎل – ﻛﺎﻫﺶ ﺑﺎ اﺳﺘﻔﺎده از ﻳﻚ اﻧﺒﺎره‬


‫در اﻳﻦ روش از ﻳﻚ اﻧﺒﺎره و ﻳﻚ ﺑﺎﻓﺮ ورودي ﺟﻬﺖ ﻧﮕﻬﺪاري رﺷﺘﻪ اي ﻛﻪ ﺑﺎﻳﺪ ﺗﺠﺰﻳﻪ‬
‫ﺷﻮد اﺳﺘﻔﺎده ﻣﻲ ﮔﺮدد‪.‬در وﺿﻌﻴﺖ ﺷﺮوع ﺗﺠﺰﻳﻪ ﺑﻪ اﻧﺘﻬﺎي ورودي ﻳﻚ ﻋﻼﻣﺖ " ‪" $‬‬
‫اﺿﺎﻓﻪ ﻣﻲ ﮔﺮدد ﻛﻪ ﺧﺎﺗﻤﻪ رﺷﺘﻪ ورودي ﺑﺮاي ﭘﺎرﺳﺮ ﻣﺸﺨﺺ ﻣﻲ ﮔﺮدد‪.‬درون اﻧﺒﺎره ﻧﻴﺰ‬
‫ﻳﻚ ﻋﻼﻣﺖ " ‪ " $‬وارد ﻣﻲ ﮔﺮدد‪.‬‬
‫ﭘﺎرﺳﺮ آﻧﻘﺪر دو ﻋﻤﻞ اﻧﺘﻘﺎل و ﻛﺎﻫﺶ را اﻧﺠﺎم ﻣﻲ دﻫﺪ ﻛﻪ ﻳﺎ ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي ﻣﺸﺎﻫﺪه‬
‫ﮔﺮدد و ﻳﺎ اﻳﻨﻜﻪ ﺑﻪ وﺿﻌﻴﺖ ﺧﺎﺗﻤﻪ ﭘﺎرس ﺑﺮﺳﺪ‪.‬وﺿﻌﻴﺖ ﺧﺎﺗﻤﻪ ﺗﺠﺰﻳﻪ ﺑﻪ اﻳﻦ ﺻﻮرت اﺳﺖ‬
‫ﻛﻪ ﺗﻮﻛﻦ ﺟﺎري ﻋﻼﻣﺖ " ‪ " $‬اﺳﺖ و درون اﻧﺒﺎره ﻧﻴﺰ ﺗﻨﻬﺎ ﻋﻼﻣﺖ ﺷﺮوع ﮔﺮاﻣﺮ ﺑﺮ روي‬
‫ﻋﻼﻣﺖ " ‪ " $‬ﻛﻪ در اﺑﺘﺪاي ﺗﺠﺰﻳﻪ وارد اﻧﺒﺎره ﮔﺮدﻳﺪه اﺳﺖ ﻗﺮار دارد‪.‬‬
‫ﺑﻄﻮر رﺳﻤﻲ ﺗﺮ اﻋﻤﺎﻟﻲ ﻛﻪ ﻳﻚ ﭘﺎرﺳﺮ اﻧﺘﻘﺎل – ﻛﺎﻫﺶ اﻧﺠﺎم ﻣﻲ دﻫﺪ ﻋﺒﺎرﺗﻨﺪ از ‪:‬‬
‫‪ -1‬اﻧﺘﻘﺎل ) ‪ : ( Shift‬ﺗﻌﺪادي از ﻋﻼﺋﻢ ورودي ﺑﻪ ﺑﺎﻻي اﻧﺒﺎره اﻧﺘﻘﺎل ﻣﻲ ﻳﺎﺑﺪ‪.‬ﻋﻤﻞ‬
‫اﻧﺘﻘﺎل ﺗﺎ زﻣﺎﻧﻲ اداﻣﻪ ﻣﻲ ﻳﺎﺑﺪ ﻛﻪ دﺳﺘﮕﻴﺮه در ﺑﺎﻻي اﻧﺒﺎره ﺗﺸﺨﻴﺺ داده ﺷﻮد‪.‬‬
‫‪ -2‬ﻛﺎﻫﺶ ) ‪ : ( Reduce‬دﺳﺘﮕﻴﺮه اي در ﺑﺎﻻي اﻧﺒﺎره ﻇﺎﻫﺮ ﺷﺪه اﺳﺖ‪.‬دﺳﺘﮕﻴﺮه از‬
‫ﺑﺎﻻي اﻧﺒﺎره ﺣﺬف و ﺑﺠﺎي آن ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺳﻤﺖ ﭼﭗ ﻗﺎﻋﺪه اي ﻛﻪ ﺳﻤﺖ راﺳﺖ آن‬
‫ﻣﻄﺎﺑﻖ دﺳﺘﮕﻴﺮه اﺳﺖ وارد اﻧﺒﺎره ﻣﻲ ﺷﻮد‪.‬‬
‫‪ -3‬ﻗﺒﻮل ورودي ) ‪ : ( Accept‬ﭘﺎرﺳﺮ ﭘﺎﻳﺎن ﻣﻮﻓﻘﻴﺖ آﻣﻴﺰ ﺗﺠﺰﻳﻪ را اﻋﻼم ﻣﻲ ﻛﻨﺪ‪.‬‬
‫‪ -4‬ﺗﺸﺨﻴﺺ ﺧﻄﺎ ) ‪ : ( Error‬ﭘﺎرﺳﺮ ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي را ﺗﺸﺨﻴﺺ داده و روﻳﻪ‬
‫ﺧﻄﺎﭘﺮداز را ﻓﺮا ﻣﻲ ﺧﻮاﻧﺪ‪.‬‬

‫‪٥٣‬‬
‫ﺑﻪ ﻋﻨﻮان ﻧﻤﻮﻧﻪ ﺗﺠﺰﻳﻪ رﺷﺘﻪ ‪ id + id * id‬ﺑﻪ روش اﻧﺘﻘﺎل – ﻛﺎﻫﺶ ﺑﺼﻮرت زﻳﺮ اﺳﺖ ‪:‬‬

‫ﻣﺤﺘﻮاي اﻧﺒﺎره‬ ‫ﺑﺎﻗﻴﻤﺎﻧﺪه ورودي‬ ‫ﻋﻤﻞ اﻧﺠﺎم ﺷﺪه‬


‫‪$‬‬ ‫‪id + id * id $‬‬ ‫اﻧﺘﻘﺎل ‪id‬‬
‫‪$ id‬‬ ‫‪+ id * id $‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪E → id‬‬
‫‪$E‬‬ ‫‪+ id * id $‬‬ ‫اﻧﺘﻘﺎل ‪+‬‬
‫‪$E+‬‬ ‫‪id * id $‬‬ ‫اﻧﺘﻘﺎل ‪id‬‬
‫‪$ E + id‬‬ ‫‪* id $‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪E → id‬‬
‫‪$E+E‬‬ ‫‪* id $‬‬ ‫اﻧﺘﻘﺎل *‬
‫*‪$E+E‬‬ ‫‪id $‬‬ ‫اﻧﺘﻘﺎل ‪id‬‬
‫‪$ E + E * id‬‬ ‫‪$‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪E → id‬‬
‫‪$E+E*E‬‬ ‫‪$‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪E → E*E‬‬
‫‪$E+E‬‬ ‫‪$‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪E → E+E‬‬
‫‪$E‬‬ ‫‪$‬‬ ‫‪Accept‬‬

‫در ﺗﺠﺰﻳﻪ ﺑﻪ روش اﻧﺘﻘﺎل – ﻛﺎﻫﺶ ﻣﺸﻜﻼت زﻳﺮ وﺟﻮد دارد ‪:‬‬
‫‪ -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‬‬

‫ﺗﺬﻛﺮ ‪ :‬ﻳﻜﻲ از ﺷﺮاﻳﻄﻲ ﻛﻪ ﺑﺎﻳﺪ وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﺪ ﺗﺎ ﺑﺘﻮان از روش ﺗﺠﺰﻳﻪ ﺗﻘﺪم – ﻋﻤﻠﮕﺮ‬
‫اﺳﺘﻔﺎده ﻧﻤﻮد اﻳﻦ اﺳﺖ ﻛﻪ ﮔﺮاﻣﺮ ﺑﺎﻳﺪ ﻳﻚ ﮔﺮاﻣﺮ ﻋﻤﻠﮕﺮ ﺑﺎﺷﺪ‪.‬‬

‫ﻣﻌﺎﻳﺐ روش ﭘﺎرس ﺗﻘﺪم – ﻋﻤﻠﮕﺮ‬


‫روش ﺗﻘﺪم – ﻋﻤﻠﮕﺮ ﻋﻠﻴﺮﻏﻢ داﺷﺘﻦ ﻣﺰﻳﺖ ﭘﻴﺎده ﺳﺎزي آﺳﺎن داراي ﻣﻌﺎﻳﺒﻲ ﻧﻴﺰ اﺳﺖ‪.‬اﻳﻦ‬
‫ﻣﻌﺎﻳﺐ ﻋﺒﺎرﺗﻨﺪ از ‪:‬‬
‫‪ -1‬ﺑﻪ دﻟﻴﻞ ﻣﺤﺪودﻳﺖ ﻫﺎﻳﻲ ﻛﻪ دارد ﮔﺮاﻣﺮﻫﺎي ﻛﻤﻲ وﺟﻮد دارﻧﺪ ﻛﻪ ﺑﺘﻮان از اﻳﻦ‬
‫روش ﺑﺮاي آﻧﻬﺎ اﺳﺘﻔﺎده ﻛﺮد‪.‬‬
‫‪ -2‬در ﻣﻮرد اﭘﺮاﺗﻮرﻫﺎﻳﻲ ﻣﺎﻧﻨﺪ ' – ' )‪ ( minus‬ﻛﻪ داراي دو ﺗﻘﺪم ﻣﺘﻔﺎوﺗﻨﺪ ) ﺑﺴﺘﻪ ﺑﻪ‬
‫اﻳﻨﻜﻪ ﻣﻨﻬﺎي ‪ unary‬اﺳﺖ ﻳﺎ ‪ ( binary‬اﻳﻦ روش ﻛﺎر ﻧﻤﻲ ﻛﻨﺪ‪.‬‬
‫‪ -3‬روش ﭼﻨﺪان دﻗﻴﻘﻲ ﻧﻴﺴﺖ‪.‬ﻳﻌﻨﻲ ﻣﻤﻜﻦ اﺳﺖ ﺑﺮﺧﻲ از ﺧﻄﺎﻫﺎي ﻧﺤﻮي را ﻧﺘﻮاﻧﺪ‬
‫ﻛﺸﻒ ﻛﻨﺪ‪.‬‬
‫در ﭘﺎرس ﺗﻘﺪم – ﻋﻤﻠﮕﺮ از ﺳﻪ راﺑﻄﻪ ﺗﻘﺪم ﻣﺎﺑﻴﻦ ﻋﻤﻠﻴﺎت ﺟﻬﺖ ﻫﺪاﻳﺖ ﻋﻤﻞ ﺗﺠﺰﻳﻪ‬
‫اﺳﺘﻔﺎده ﻣﻲ ﮔﺮدد‪.‬در اﻳﻦ روش رواﺑﻂ ﺗﻘﺪم ﺗﻨﻬﺎ ﺑﻴﻦ ﭘﺎﻳﺎﻧﻪ ﻫﺎي ﮔﺮاﻣﺮ و ﺑﺼﻮرت زﻳﺮ‬
‫ﺗﻌﺮﻳﻒ ﻣﻲ ﮔﺮدد ‪:‬‬
‫‪ a < b -1‬ﻳﻌﻨﻲ ﭘﺎﻳﺎﻧﻪ ‪ a‬از ﭘﺎﻳﺎﻧﻪ ‪ b‬ﺗﻘﺪم ﻛﻤﺘﺮي دارد‪.‬ﻣﺎﻧﻨﺪ * < ‪+‬‬

‫‪٥٧‬‬
‫‪ a = b -2‬ﻳﻌﻨﻲ ﭘﺎﻳﺎﻧﻪ ‪ a‬و ﭘﺎﻳﺎﻧﻪ ‪ b‬از ﺗﻘﺪم ﻳﻜﺴﺎﻧﻲ ﺑﺮﺧﻮردارﻧﺪ‪.‬ﻣﺎﻧﻨﺪ ) = (‬
‫‪ a > b -3‬ﻳﻌﻨﻲ ﭘﺎﻳﺎﻧﻪ ‪ a‬از ﭘﺎﻳﺎﻧﻪ ‪ b‬ﺗﻘﺪم ﺑﻴﺸﺘﺮي دارد‪.‬ﻣﺎﻧﻨﺪ * > ‪id‬‬

‫رواﺑﻂ ﺗﻘﺪﻣﻲ ﻛﻪ در اﻳﻨﺠﺎ ﺑﻴﻦ ﭘﺎﻳﺎﻧﻪ ﻫﺎ ﺗﻮﺻﻴﻒ ﻣﻲ ﺷﻮد ﺑﺎ رواﺑﻂ < ‪ > , = ,‬ﻣﻌﻤﻮﻟﻲ ﻛﻪ‬
‫در ﺑﻴﻦ اﻋﺪاد ﻃﺒﻴﻌﻲ ﺑﺮﻗﺮار اﺳﺖ ﺗﻔﺎوت زﻳﺎدي دارﻧﺪ‪.‬ﺑﻪ ﻋﻨﻮان ﻧﻤﻮﻧﻪ در اﻳﻨﺠﺎ ﺑﺎ داﻧﺴﺘﻦ‬
‫اﻳﻨﻜﻪ راﺑﻄﻪ ‪ a < b‬ﺑﺮﻗﺮار اﺳﺖ ﻧﻤﻲ ﺗﻮان ﻧﺘﻴﺠﻪ ﮔﺮﻓﺖ ﻛﻪ ‪. b > a‬از ﻃﺮﻓﻲ ﻣﻤﻜﻦ اﺳﺖ‬
‫در ﭘﺎﻳﺎﻧﻪ ﻫﻴﭽﻴﻚ از اﻳﻦ ﺳﻪ راﺑﻄﻪ ﺗﻘﺪم را ﺑﺎ ﻫﻢ ﻧﺪاﺷﺘﻪ ﺑﺎﺷﻨﺪ و ﻳﺎ اﻳﻨﻜﻪ دو ﭘﺎﻳﺎﻧﻪ دو راﺑﻄﻪ‬
‫ﺗﻘﺪم ﻣﺘﻔﺎوت داﺷﺘﻪ ﺑﺎﺷﻨﺪ‪.‬ﻣﺜﻼ داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻛﻪ * < ‪ -‬و ﻫﻢ * > ‪. -‬‬
‫‪ 1 – 15 – 3‬اﻟﮕﻮرﻳﺘﻢ ﺗﺠﺰﻳﻪ ﺗﻘﺪم – ﻋﻤﻠﮕﺮ‬
‫ﺳﺎﺧﺘﺎر ﭘﺎرﺳﺮ در روش ﺗﻘﺪم – ﻋﻤﻠﮕﺮ ﻛﺎﻣﻼ ﻣﺸﺎﺑﻪ ﺳﺎﺧﺘﺎر ﻳﻚ ﭘﺎرﺳﺮ )‪ LL(1‬اﺳﺖ‪.‬در‬
‫اﻳﻦ ﺳﺎﺧﺘﺎر ﻛﻪ در ﺷﻜﻞ زﻳﺮ ﻧﺸﺎن داده ﺷﺪه اﺳﺖ ﻣﻮﻟﻔﻪ اﺻﻠﻲ ﭘﺎرﺳﺮ ﻳﻚ ﺑﺮﻧﺎﻣﻪ اﺳﺖ ﻛﻪ‬
‫از ﻳﻚ ﻃﺮف ورودي ﺧﻮد را از اﺳﻜﻨﺮ درﻳﺎﻓﺖ ﻣﻲ ﻛﻨﺪ و از ﻳﻚ اﻧﺒﺎره ﺑﺮاي ذﺧﻴﺮه‬
‫اﻃﻼﻋﺎت و از ﻳﻚ ﺟﺪول ﺗﺠﺰﻳﻪ ﺑﺮاي ﻫﺪاﻳﺖ ﻋﻤﻞ ﺗﺠﺰﻳﻪ اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ‪.‬در اﻳﻦ روش‬
‫ﭘﺎرﺳﺮ اﺑﺘﺪا ﻳﻚ ﻋﻼﻣﺖ ‪ $‬ﺑﻪ اﻧﺘﻬﺎي رﺷﺘﻪ ورودي اﺿﺎﻓﻪ ﻣﻲ ﻛﻨﺪ‪.‬اﻧﺒﺎره ﻧﻴﺰ در اﺑﺘﺪاي ﻛﺎر‬
‫ﻓﻘﻂ ﺷﺎﻣﻞ ﻳﻚ ﻋﻼﻣﺖ ‪ $‬اﺳﺖ‪.‬‬
‫ﺟﺪول ﭘﺎرس در اﻳﻦ روش ﻳﻚ ﺟﺪول دو ﺑﻌﺪي ﻣﺮﺑﻊ اﺳﺖ ﻛﻪ ﺑﻪ ﺗﻌﺪاد ﭘﺎﻳﺎﻧﻪ ﻫﺎي ﺑﻌﻼوه‬
‫ﻳﻚ ) ﺑﻪ ﺧﺎﻃﺮ ﻋﻼﻣﺖ ‪ ( $‬ﺳﻄﺮ و ﺳﺘﻮن دارد‪.‬در داﺧﻞ ﺟﺪول ﻧﻴﺰ در ﺑﺮﺧﻲ ﺧﺎﻧﻪ ﻫﺎي‬
‫ﺟﺪول ﻳﻜﻲ از ﻋﻼﻣﺖ ﻫﺎي < ‪ > ,‬و ﻳﺎ = ﻗﺮار دارد و ﻣﺎﺑﻘﻲ ﺧﺎﻧﻪ ﻫﺎي ﺟﺪول ﺧﺎﻟﻲ اﺳﺖ‪.‬‬

‫‪a1‬‬ ‫‪...‬‬ ‫‪ai‬‬ ‫‪b‬‬ ‫‪...‬‬ ‫‪an‬‬ ‫‪$‬‬ ‫ورودﯼ‬

‫‪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‬‬ ‫‪$‬‬

‫‪+‬‬ ‫>‬ ‫<‬ ‫<‬ ‫>‬ ‫<‬ ‫>‬


‫*‬ ‫>‬ ‫>‬ ‫<‬ ‫>‬ ‫<‬ ‫>‬
‫(‬ ‫<‬ ‫<‬ ‫<‬ ‫=‬ ‫<‬
‫)‬ ‫>‬ ‫>‬ ‫>‬ ‫>‬
‫‪id‬‬ ‫>‬ ‫>‬ ‫>‬ ‫>‬
‫‪$‬‬ ‫<‬ ‫<‬ ‫<‬ ‫<‬ ‫=‬

‫اﻧﺒﺎره‬ ‫ورودي‬ ‫ﻋﻤﻞ اﻧﺠﺎم ﺷﺪه‬


‫‪$‬‬ ‫‪id + id * id $‬‬ ‫اﻧﺘﻘﺎل < و ‪id‬‬
‫‪$ < id‬‬ ‫‪+ id * id $‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪E → id‬‬
‫‪$E‬‬ ‫‪+ id * id $‬‬ ‫اﻧﺘﻘﺎل < و ‪+‬‬
‫‪$E<+‬‬ ‫‪id * id $‬‬ ‫اﻧﺘﻘﺎل < و ‪id‬‬
‫‪$ E < + < id‬‬ ‫‪* id $‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪E → id‬‬
‫‪$E<+E‬‬ ‫‪* id $‬‬ ‫اﻧﺘﻘﺎل < و *‬
‫*<‪$E<+E‬‬ ‫‪id $‬‬ ‫اﻧﺘﻘﺎل < و ‪id‬‬
‫‪$ E < + E < * < id‬‬ ‫‪$‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪E → id‬‬
‫‪$E<+E<*E‬‬ ‫‪$‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪E → E*E‬‬
‫‪$E<+E‬‬ ‫‪$‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪E → E+E‬‬
‫‪$E‬‬ ‫‪$‬‬ ‫ﭘﺎﻳﺎن ﭘﺎرس‬

‫‪ 2 – 15 – 3‬ﻧﺤﻮه ﻳﺎﻓﺘﻦ رواﺑﻂ ﺗﻘﺪم‬


‫ﺑﺮاي ﺗﻌﻴﻴﻦ رواﺑﻂ ﺗﻘﺪم از دو ﺗﺎﺑﻊ ﺑﺎ ﺗﻌﺮﻳﻒ زﻳﺮ اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﻴﻢ‪.‬اﻳﻦ دو ﺗﺎﺑﻊ روي‬
‫ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎ ﺗﻌﺮﻳﻒ ﺷﺪه اﻧﺪ و ﺣﺎﺻﻞ آﻧﻬﺎ ﻣﺠﻤﻮﻋﻪ اي از ﭘﺎﻳﺎﻧﻪ ﻫﺎ اﺳﺖ‪.‬‬
‫‪+‬‬ ‫‪+‬‬
‫} ‪Firstterm(A) = { a | A ═› aα or A ═› Baα‬‬
‫‪+‬‬ ‫‪+‬‬
‫} ‪Lastterm(A) = { a | A ═› αa or A ═› αaB‬‬

‫‪٦٠‬‬
‫ﻛﻪ در آن ‪ 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‬‬ ‫‪$‬‬

‫‪+‬‬ ‫>‪.‬‬ ‫‪<.‬‬ ‫‪<.‬‬ ‫>‪.‬‬ ‫‪<.‬‬ ‫>‪.‬‬


‫*‬ ‫>‪.‬‬ ‫>‪.‬‬ ‫‪<.‬‬ ‫>‪.‬‬ ‫‪<.‬‬ ‫>‪.‬‬
‫(‬ ‫‪<.‬‬ ‫‪<.‬‬ ‫‪<.‬‬ ‫=‬ ‫‪<.‬‬
‫)‬ ‫>‪.‬‬ ‫>‪.‬‬ ‫>‪.‬‬ ‫>‪.‬‬
‫‪id‬‬ ‫>‪.‬‬ ‫>‪.‬‬ ‫>‪.‬‬ ‫>‪.‬‬
‫‪$‬‬ ‫‪<.‬‬ ‫‪<.‬‬ ‫‪<.‬‬ ‫‪<.‬‬ ‫=‬

‫‪٦٢‬‬
‫‪ 3 -15 – 3‬اﺻﻼح ﺧﻄﺎ در روش ﺗﻘﺪم – ﻋﻤﻠﮕﺮ‬
‫در اﻳﻦ روش ﻛﻼ ﺑﻪ دو ﺻﻮرت ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي ﺗﺸﺨﻴﺺ داده ﻣﻲ ﺷﻮد‪.‬اول وﻗﺘﻴﻜﻪ ﻫﻴﭻ‬
‫راﺑﻄﻪ اي ﺑﻴﻦ ﭘﺎﻳﺎﻧﻪ ﺑﺎﻻي اﻧﺒﺎره و ورودي ﺟﺎري ﻧﺒﺎﺷﺪ و دوم ﻫﻨﮕﺎﻣﻲ ﻛﻪ دﺳﺘﮕﻴﺮه ﺑﺎﻻي‬
‫اﻧﺒﺎره ﺑﺎ ﺳﻤﺖ راﺳﺖ ﻫﻴﭻ ﻗﺎﻋﺪه اي ﺗﻄﺒﻴﻖ ﻧﻜﻨﺪ‪.‬‬
‫ﺑﺮاي اﺻﻼح ﺧﻄﺎﻫﺎي ﻧﻮع اول در ﺧﺎﻧﻪ ﻫﺎي ﺧﺎﻟﻲ ﺟﺪول ﻧﺸﺎﻧﻪ روﻫﺎﻳﻲ ﺑﻪ زﻳﺮ روال ﻫﺎي‬
‫اﺻﻼح ﺧﻄﺎ ﻣﻲ ﮔﺬارﻳﻢ ﺑﻄﻮرﻳﻜﻪ اﮔﺮ در ﻋﻤﻞ ﺗﺠﺰﻳﻪ ﺑﻪ ﻳﻚ ﺧﺎﻧﻪ ﺧﺎﻟﻲ ﺟﺪول رﺟﻮع‬
‫ﺷﺪه زﻳﺮروال ﻣﺮﺑﻮﻃﻪ ﻓﺮاﺧﻮاﻧﻲ ﺷﺪه و ﺧﻄﺎ ﺑﻪ ﻧﺤﻮ ﻣﻘﺘﻀﻲ اﺻﻼح ﮔﺮدد‪.‬‬
‫‪E→E+T|T‬‬ ‫ﺑﻪ ﻋﻨﻮان ﻧﻤﻮﻧﻪ ﮔﺮاﻣﺮ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ ‪:‬‬
‫‪T → (E) | id‬‬
‫ﺟﺪول ﺗﺠﺰﻳﻪ اﻳﻦ ﮔﺮاﻣﺮ ﺑﺼﻮرت زﻳﺮ اﺳﺖ ‪:‬‬
‫‪id‬‬ ‫(‬ ‫)‬ ‫‪$‬‬ ‫‪+‬‬

‫‪$‬‬ ‫<‬ ‫<‬ ‫‪e1‬‬ ‫=‬ ‫<‬


‫)‬ ‫‪e2‬‬ ‫‪e2‬‬ ‫>‬ ‫>‬ ‫>‬
‫‪id‬‬ ‫‪e2‬‬ ‫‪e2‬‬ ‫>‬ ‫>‬ ‫>‬
‫(‬ ‫<‬ ‫<‬ ‫=‬ ‫‪e3‬‬ ‫<‬
‫‪+‬‬ ‫<‬ ‫<‬ ‫>‬ ‫>‬ ‫>‬

‫روال ﻫﺎي اﺻﻼح ﺧﻄﺎ ﺑﺼﻮرت زﻳﺮ ﺗﻌﺮﻳﻒ ﻣﻲ ﺷﻮﻧﺪ ‪:‬‬


‫• ‪ : e1‬ﺗﻮﻛﻦ " ) " را ﺣﺬف و ﭘﻴﻐﺎم " در ورودي ﻳﻚ ﭘﺮاﻧﺘﺰ ﺑﺴﺘﻪ اﺿﺎﻓﻲ وﺟﻮد‬
‫دارد " را ﭼﺎپ ﻛﻦ‪.‬‬
‫• ‪ : e2‬ﭘﺎﻳﺎﻧﻪ " ‪ " +‬را ﺑﻪ ورودي اﺿﺎﻓﻪ و ﭘﻴﻐﺎم " ﻳﻚ ﻋﻤﻠﮕﺮ در ﺑﺮﻧﺎﻣﻪ ﻛﻢ اﺳﺖ‬
‫" را ﭼﺎپ ﻛﻦ‪.‬‬
‫• ‪ : e3‬ﭘﺎﻳﺎﻧﻪ " ( " را از ﺑﺎﻻي اﻧﺒﺎره ﺣﺬف و ﭘﻴﻐﺎم " ﻳﻚ ﭘﺮاﻧﺘﺰ ﺑﺴﺘﻪ در ورودي‬
‫ﻛﻢ اﺳﺖ " را ﭼﺎپ ﻛﻦ‪.‬‬
‫در ﺻﻮرت ﺗﺸﺨﻴﺺ ﺧﻄﺎي ﻧﻮع دوم ﻳﻌﻨﻲ ﻋﺪم ﺗﻄﺒﻴﻖ دﺳﺘﮕﻴﺮه ﺑﺎ ﺳﻤﺖ راﺳﺖ ﻫﻴﭽﻴﻚ از‬
‫ﻗﻮاﻋﺪ ﮔﺮاﻣﺮ ﭘﺎرﺳﺮ ﺑﻪ دﻧﺒﺎل ﻗﺎﻋﺪه اي ﻛﻪ ﺳﻤﺖ راﺳﺖ آن ﺷﺒﻴﻪ دﺳﺘﮕﻴﺮه ﺑﺎﺷﺪ ) در ﻳﻚ ﻳﺎ‬

‫‪٦٣‬‬
‫دو ﻋﻼﻣﺖ ﺗﻔﺎوت داﺷﺘﻪ ﺑﺎﺷﻨﺪ ( ﺟﺴﺘﺠﻮ ﻣﻲ ﻛﻨﺪ و ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﺧﺘﻼف دﺳﺘﮕﻴﺮه و ﺳﻤﺖ‬
‫راﺳﺖ ﻗﺎﻋﺪه ﭘﻴﺪا ﺷﺪه ﭘﻴﻐﺎم ﻣﻨﺎﺳﺒﻲ ﭼﺎپ ﻣﻲ ﻛﻨﺪ و ﻋﻤﻞ ﻛﺎﻫﺶ را اﻧﺠﺎم ﻣﻲ دﻫﺪ‪.‬‬
‫ﻣﺜﻼ ﻓﺮض ﻛﻨﻴﺪ دﺳﺘﮕﻴﺮه ‪ 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‬‬

‫‪ f(a) < g(b) -1‬ﻫﺮﮔﺎه ‪a < b‬‬


‫‪ f(a) = g(b) -2‬ﻫﺮﮔﺎه ‪a = b‬‬
‫‪ f(a) > g(b) -3‬ﻫﺮﮔﺎه ‪a > b‬‬

‫ﺑﻨﺎ ﺑﺮ اﻳﻦ راﺑﻄﻪ ي اوﻟﻮﻳﺖ ﺑﻴﻦ ‪ a‬و ‪ b‬ﻣﻲ ﺗﻮاﻧﺪ ﺗﻮﺳﻂ ﻣﻘﺎﻳﺴﻪ ي ﻋﺪدي ﺑﻴﻦ )‪ f(a‬و )‪g(b‬‬
‫اﻧﺠﺎم ﮔﻴﺮد‪ .‬ﺑﻪ ﻫﺮ ﺣﺎل ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ ورودي ﻫﺎي ﺧﻄﺎ در ﻣﺎﺗﺮﻳﺲ اوﻟﻮﻳﺖ ‪ ،‬ﻣﻬﻢ‬
‫ﻣﻲ ﺑﺎﺷﻨﺪ‪ .‬زﻳﺮا ﻳﻜﻲ از ﺣﺎﻟﺖ ﻫﺎي )‪ (2) ، (1‬و ﻳﺎ )‪ (3‬ﺑﺪون ﺗﻮﺟﻪ ﺑﻪ اﻳﻦ ﻛﻪ )‪ f(a‬و )‪g(b‬‬
‫داراي ﭼﻪ ﻣﻘﺎدﻳﺮي ﻫﺴﺘﻨﺪ ‪ ،‬ﺑﺮﻗﺮار ﻣﻲ ﺑﺎﺷﺪ‪ .‬ﻓﻘﺪان ﺗﻮاﻧﺎﻳﻲ آﺷﻜﺎر ﺳﺎزي ﺧﻄﺎ ‪ ،‬ﻋﻤﻮﻣﺎ ﺑﻪ‬
‫اﻧﺪازه اي ﻣﻬﻢ ﻧﻴﺴﺖ ﻛﻪ ﻣﺎﻧﻊ اﺳﺘﻔﺎده از ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ در ﻣﻮﻗﻊ ﻟﺰوم ﺷﻮد‪ .‬ﻫﻨﻮز ﻫﻢ اﻣﻜﺎن‬
‫ﮔﺮﻓﺘﻦ ﺧﻄﺎ در زﻣﺎﻧﻲ ﻛﻪ ﻛﺎﻫﺶ درﺧﻮاﺳﺖ ﻣﻲ ﺷﻮد وﻟﻲ دﺳﺘﮕﻴﺮه ﻳﺎﻓﺖ ﻧﻤﻲ ﮔﺮدد ‪،‬‬
‫وﺟﻮد دارد‪.‬‬

‫اﻳﻨﭽﻨﻴﻦ ﻧﻴﺴﺖ ﻛﻪ ﻫﺮ ﺟﺪول رواﺑﻂ اوﻟﻮﻳﺖ ‪ ،‬داراي ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ ﺑﻪ ﻣﻨﻈﻮر ﻛﺪ ﮔﺬاري آن‬
‫ﺑﺎﺷﺪ ‪ ،‬اﻣﺎ در ﻋﻤﻞ ‪ ،‬اﻳﻦ ﺗﻮاﺑﻊ ﻣﻌﻤﻮﻻ وﺟﻮد دارﻧﺪ‪.‬‬

‫‪٦٥‬‬
‫ﻣﺜﺎل (‬
‫ﺟﺪول اوﻟﻮﻳﺖ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ ‪:‬‬

‫‪+‬‬ ‫‪-‬‬ ‫*‬ ‫‪/‬‬ ‫‪id‬‬ ‫(‬ ‫)‬ ‫‪$‬‬

‫‪+‬‬ ‫>‬ ‫< >‬ ‫<‬ ‫<‬ ‫< <‬ ‫> >‬
‫‪-‬‬ ‫>‬ ‫< >‬ ‫<‬ ‫<‬ ‫< <‬ ‫> >‬
‫*‬ ‫>‬ ‫> > >‬ ‫<‬ ‫< <‬ ‫> >‬
‫‪/‬‬ ‫>‬ ‫> > >‬ ‫<‬ ‫< <‬ ‫> >‬
‫>‬ ‫> >‬ ‫>‬ ‫<‬ ‫< <‬ ‫> >‬
‫‪id‬‬ ‫>‬ ‫> >‬ ‫>‬ ‫>‬ ‫> >‬
‫(‬ ‫<‬ ‫< <‬ ‫<‬ ‫<‬ ‫<‬ ‫= <‬
‫)‬ ‫>‬ ‫>‬ ‫>‬ ‫> >‬ ‫> >‬
‫‪$‬‬ ‫<‬ ‫< <‬ ‫<‬ ‫<‬ ‫< <‬

‫ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ ﺟﺪول ﻣﺬﻛﻮر ﺑﻪ ﺷﻜﻞ زﻳﺮ اﺳﺖ ‪:‬‬

‫‪+‬‬ ‫‪-‬‬ ‫*‬ ‫‪/‬‬ ‫(‬ ‫)‬ ‫‪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‬وﺟﻮد ﻧﺪارد‪ .‬ورودي‬
‫ﻫﺎي دﻳﮕﺮ ﺧﻄﺎ در ﺟﺪول اوﻟﻮﻳﺖ ﺑﺎﻻ ﺑﻪ ﻃﻮر ﻣﺸﺎﺑﻪ ﺑﺎ ﻳﻜﻲ از رواﺑﻂ اوﻟﻮﻳﺖ ﺟﺎﻳﮕﺰﻳﻦ ﻣﻲ‬

‫‪٦٦‬‬
‫ﮔﺮدﻧﺪ‪ .‬روﺷﻲ ﺳﺎده ﺑﻪ ﻣﻨﻈﻮر ﻳﺎﻓﺘﻦ ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ ﺑﺮاي ﻳﻚ ﺟﺪول ‪ ،‬اﮔﺮ ﭼﻨﻴﻦ ﺗﻮاﺑﻌﻲ‬
‫وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﻨﺪ ‪ ،‬ﺑﻪ ﺗﺮﺗﻴﺐ زﻳﺮ اﺳﺖ‪.‬‬

‫اﻟﮕﻮرﻳﺘﻢ ﺳﺎﺧﺖ ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ‪:‬‬


‫ورودي ‪ :‬ﻣﺎﺗﺮﻳﺲ ﻋﻤﻠﮕﺮ‪ -‬اوﻟﻮﻳﺖ‬
‫ﺧﺮوﺟﻲ ‪ :‬ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ ﻧﻤﺎﻳﺎﻧﮕﺮ ﻣﺎﺗﺮﻳﺲ ورودي‪ ،‬ﻳﺎ ﻧﺸﺎن دﻫﻨﺪه اﻳﻦ اﺳﺖ ﻛﻪ ﭼﻨﻴﻦ‬
‫ﺟﺪوﻟﻲ وﺟﻮد ﻧﺪارد‪.‬‬
‫روش‪:‬‬
‫‪ -1‬ﻧﻤﺎد ‪ fa‬و‪ ga‬را ﺑﺮاي ﻫﺮ ‪ a‬ﻛﻪ ﻳﻚ ﭘﺎﻳﺎﻧﻪ ﻳﺎ ‪ $‬اﺳﺖ اﻳﺠﺎد ﻧﻤﺎﻳﻴﺪ‪.‬‬
‫‪ -2‬ﻧﻤﺎد ﻫﺎي اﻳﺠﺎد ﺷﺪه را ﺑﻪ ﺗﻌﺪاد ﻣﻤﻜﻦ ﮔﺮوه ﺗﻘﺴﻴﻢ ﺑﻨﺪي ﻛﻨﻴﺪ ﺑﻪ ﺷﻜﻠﻲ ﻛﻪ اﮔﺮ‬
‫‪ a=b‬در اﻳﻦ ﺻﻮرت ‪fa‬و‪ gb‬در ﻳﻚ ﮔﺮوه ﻗﺮار ﻣﻲ ﮔﻴﺮﻧﺪ‪ .‬ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ ﻣﻤﻜﻦ‬
‫اﺳﺖ ﻣﺠﺒﻮر ﺷﻮﻳﺪ ﻧﻤﺎدﻫﺎﻳﻲ را در ﻳﻚ ﮔﺮوه ﻗﺮار دﻫﻴﺪ‪ ،‬ﺣﺘﻲ اﮔﺮ ﺑﺎ راﺑﻄﻪ ي = ﺑﺎ‬
‫ﻳﻜﺪﻳﮕﺮﻣﺮﺗﺒﻂ ﻧﺸﺪه ﺑﺎﺷﻨﺪ‪ .‬ﺑﺮاي ﻣﺜﺎل اﮔﺮ ‪a=b‬و‪ c=b‬اﻧﮕﺎه ‪fa‬و ‪ fc‬ﺑﺎﻳﺪ در ﻳﻚ ﮔﺮوه‬
‫ﻗﺮار ﮔﻴﺮﻧﺪ‪ ،‬زﻳﺮا ﻫﺮدوي آﻧﻬﺎ در ﻫﻤﺎن ﮔﺮوه ‪ gb‬ﻗﺮار دارﻧﺪ‪.‬اﮔﺮ ﻋﻼوه ﺑﺮ آن‪ c=d،‬آﻧﮕﺎه‬
‫‪fa‬و ‪ gd‬در ﻳﻚ ﮔﺮوه ﻗﺮار دارﻧﺪ ﺣﺘﻲ اﮔﺮ ‪ a=d‬وﺟﻮد ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ‪.‬‬
‫‪ -3‬ﮔﺮاف ﺟﻬﺖ داري اﻳﺠﺎد ﻛﻨﻴﺪ ﻛﻪ ﮔﺮه ﻫﺎي آن‪ ،‬ﮔﺮوه ﻫﺎي اﻳﺠﺎد ﺷﺪه در ﻣﺮﺣﻠﻪ ‪2‬‬
‫ﺑﺎﺷﻨﺪ‪ .‬ﺑﺮاي ﻫﺮ ‪a‬و‪ b‬اﮔﺮ ‪،a<b‬آﻧﮕﺎه ﻳﻚ ﻟﺒﻪ از ﮔﺮوه ‪gb‬ﺑﻪ ﮔﺮوه ‪ fa‬رﺳﻢ ﻛﻨﻴﺪ‪.‬اﮔﺮ ‪a>b‬‬
‫اﻧﮕﺎه ﻳﻚ ﻟﺒﻪ از ﮔﺮوه ‪ fa‬ﺑﻪ‪ gb‬رﺳﻢ ﻧﻤﺎﻳﻴﺪ‪ .‬ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ ﻳﻚ ﻟﺒﻪ ﻳﺎ ﻣﺴﻴﺮ از ‪fa‬‬
‫ﺑﻪ ‪ gb‬ﺑﻪ اﻳﻦ ﻣﻌﻨﻲ اﺳﺖ ﻛﻪ ‪ fa‬ﺑﺎﻳﺪ ﺑﻴﺶ از )‪ g(b‬ﺑﺎﺷﺪ؛ﻳﻚ ﻣﺴﻴﺮ از ‪gb‬ﺑﻪ ‪ fa‬ﺑﻪ اﻳﻦ‬
‫ﻣﻌﻨﻲ اﺳﺖ ﻛﻪ )‪ g(b‬ﺑﺎﻳﺪ ﺑﻴﺶ از )‪ f(a‬ﺑﺎﺷﺪ‪.‬‬
‫‪ -4‬اﮔﺮ ﻛﺮاف ﺑﺪﺳﺖ آﻣﺪه در ‪ 3‬داراي دوره ﺑﺎﺷﺪ‪،‬ﺗﻮاﺑﻊ اوﻟﻮﻳﺖ وﺟﻮد ﻧﺪارﻧﺪ‪ .‬اﮔﺮ دوره‬
‫وﺟﻮد ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ ‪،‬ﻣﻘﺪار )‪ ، f(a‬ﻃﻮل ﻃﻮﻻﻧﻲ ﺗﺮﻳﻦ ﻣﺴﻴﺮي اﺳﺖ ﻛﻪ از ﮔﺮوه ‪ fa‬ﺷﺮوع‬
‫ﻣﻲ ﺷﻮد ﻣﻘﺪار ‪ ga‬ﻃﻮل ﻃﻮﻻﻧﻲ ﺗﺮﻳﻦ ﻣﺴﻴﺮ از ﮔﺮوﻫﻲ اﺳﺖ ﻛﻪ ‪ ga‬در آن ﻗﺮار دارد‪.‬‬
‫ﻣﺜﺎل‪:‬‬
‫ﻣﺎﺗﺮﻳﺲ زﻳﺮ را در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ ‪:‬‬

‫‪٦٧‬‬
‫‪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‬‬

‫‪S‬‬ ‫=‬ ‫=‬ ‫<‬ ‫=‬ ‫<‬


‫‪$‬‬ ‫=‬ ‫<‬ ‫<‬
‫(‬ ‫=‬ ‫<‬ ‫<‬
‫)‬ ‫>‬ ‫>‬ ‫>‬ ‫>‬
‫‪c‬‬ ‫>‬ ‫>‬ ‫>‬ ‫>‬

‫‪ 1 – 16 – 3‬اﻟﮕﻮرﻳﺘﻢ ﺗﺠﺰﻳﻪ ﺑﻪ روش ﺗﻘﺪم ﺳﺎده‬


‫در اﻳﻦ روش ﭘﺎرﺳﺮ در ﻫﺮ ﻗﺪم از ﺗﺠﺰﻳﻪ ﺑﺎ اﺳﺘﻔﺎده از ﺗﻮﻛﻦ ﺟﺎري ‪ b‬و ﻋﻨﺼﺮ ﺑﺎﻻي اﻧﺒﺎره‬
‫‪ ) X‬ﻛﻪ ﻣﻲ ﺗﻮاﻧﺪ ﭘﺎﻳﺎﻧﻪ ﻳﺎ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﺎﺷﺪ ( ﺑﻪ ﺟﺪول ﺗﺠﺰﻳﻪ ﻣﺮاﺟﻌﻪ ﻛﺮده و ﺑﺼﻮرت ﻳﻜﻲ‬
‫از ﺣﺎﻻت زﻳﺮ ﻋﻤﻞ ﻣﻲ ﻛﻨﺪ ‪:‬‬
‫‪ .1‬در ﺻﻮرﺗﻲ ﻛﻪ راﺑﻄﻪ ﻋﻼﻣﺖ ﺑﺎﻻي اﻧﺒﺎره و ﺗﻮﻛﻦ ﺟﺎري ﺑﺼﻮرت ‪X < b‬‬
‫ﺑﺎﺷﺪ ﭘﺎرﺳﺮ ﻋﻤﻞ اﻧﺘﻘﺎل را اﻧﺠﺎم ﻣﻲ دﻫﺪ‪.‬در اﻳﻦ ﻣﻮرد اﺑﺘﺪا ﻋﻼﻣﺖ < و‬
‫ﺳﭙﺲ ﺗﻮﻛﻦ ﺟﺎري ‪ b‬را ﺑﻪ ﺑﺎﻻي اﻧﺒﺎره ﻣﻨﺘﻘﻞ ﻣﻲ ﻛﻨﺪ‪.‬‬
‫‪ .2‬در ﺻﻮرﺗﻲ ﻛﻪ راﺑﻄﻪ دو ﻋﻨﺼﺮ ﻣﺰﺑﻮر ﺑﺼﻮرت ‪ X = b‬ﺑﺎﺷﺪ ﭘﺎرﺳﺮ ﻓﻘﻂ‬
‫ﺗﻮﻛﻦ ﺟﺎري را ﺑﻪ ﺑﺎﻻي اﻧﺒﺎره اﻧﺘﻘﺎل ﻣﻲ دﻫﺪ‪.‬‬
‫‪ .3‬در ﺻﻮرﺗﻴﻜﻪ راﺑﻄﻪ ﺑﺼﻮرت ‪ X > b‬ﺑﺎﺷﺪ ﭘﺎرﺳﺮ ﻋﻤﻞ ﻛﺎﻫﺶ را اﻧﺠﺎم ﻣﻲ‬
‫دﻫﺪ‪.‬در اﻳﻦ ﺣﺎﻟﺖ دﺳﺘﮕﻴﺮه رﺷﺘﻪ ﺑﺎﻻي اﻧﺒﺎره ﺗﺎ اوﻟﻴﻦ ﻋﻼﻣﺖ < اﺳﺖ‪.‬ﭘﺎرﺳﺮ‬
‫اﺑﺘﺪا دﺳﺘﮕﻴﺮه را از ﺑﺎﻻي اﻧﺒﺎره ﺣﺬف ﻣﻲ ﻛﻨﺪ‪.‬اﮔﺮ ﻋﻨﺼﺮ ﺑﺎﻻي اﻧﺒﺎره ) ﭘﺲ از‬

‫‪٧٠‬‬
‫ﺣﺬف دﺳﺘﮕﻴﺮه ( را ‪ Top‬ﺑﻨﺎﻣﻴﻢ و ﺳﻤﺖ ﭼﭗ ﻗﺎﻋﺪه اي را ﻛﻪ ﭘﺎرﺳﺮ از آن‬
‫ﺟﻬﺖ ﻛﺎﻫﺶ اﺳﺘﻔﺎده ﻣﻲ ﻛﻨﺪ ‪ Lhs‬ﺑﻨﺎﻣﻴﻢ ﭘﺎرﺳﺮ راﺑﻄﻪ ﺑﻴﻦ ‪ Lhs‬و ‪ Top‬را‬
‫از ﺟﺪول اﺳﺘﺨﺮاج ﻧﻤﻮده و ﻳﻜﻲ از اﻋﻤﺎل زﻳﺮ را اﻧﺠﺎم ﻣﻲ دﻫﺪ ‪:‬‬
‫• اﮔﺮ راﺑﻄﻪ ‪ Top‬و ‪ Lhs‬ﺑﺼﻮرت ‪ Top < Lhs‬ﺑﺎﺷﺪ ﭘﺎرﺳﺮ اﺑﺘﺪا‬
‫ﻋﻼﻣﺖ < وﺳﭙﺲ ‪ Lhs‬را وارد اﻧﺒﺎره ﻣﻲ ﻛﻨﺪ‪.‬‬
‫• اﮔﺮ راﺑﻄﻪ ‪ Top‬و ‪ Lhs‬ﺑﺼﻮرت ‪ Top = Lhs‬ﺑﺎﺷﺪ ﭘﺎرﺳﺮ ﻓﻘﻂ‬
‫‪ Lhs‬را وارد اﻧﺒﺎره ﻣﻲ ﻛﻨﺪ‪.‬‬
‫• اﮔﺮ ‪ Top‬و ‪ Lhs‬راﺑﻄﻪ اي ﻧﺪاﺷﺘﻪ ﺑﺎﺷﻨﺪ ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي رخ داده‬
‫اﺳﺖ و ﺑﺎﻳﺴﺘﻲ روﻳﻪ اﺻﻼح ﺧﻄﺎ ﻓﺮاﺧﻮاﻧﺪه ﺷﻮد‪.‬‬
‫‪ .4‬در ﺻﻮرﺗﻲ ﻛﻪ ﻋﻨﺼﺮ ﺑﺎﻻي اﻧﺒﺎره ‪ X‬و ورودي ﺟﺎري ‪ b‬راﺑﻄﻪ اي ﻧﺪاﺷﺘﻪ‬
‫ﺑﺎﺷﻨﺪ ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي رخ داده اﺳﺖ و ﺑﺎﻳﺴﺘﻲ روﻳﻪ اﺻﻼح ﺧﻄﺎ ﻓﺮاﺧﻮاﻧﺪه‬
‫ﺷﻮد‪.‬‬
‫‪ .5‬در ﺻﻮرﺗﻲ ﻛﻪ ﺗﻮﻛﻦ ﺟﺎري ‪ $‬و در داﺧﻞ اﻧﺒﺎره ‪ S ) $S‬ﻋﻼﻣﺖ ﺷﺮوع‬
‫ﮔﺮاﻣﺮ اﺳﺖ ( ﺑﺎﻗﻲ ﻣﺎﻧﺪه ﺑﺎﺷﺪ ﭘﺎرﺳﺮ ﭘﺎﻳﺎن ﻣﻮﻓﻘﻴﺖ آﻣﻴﺰ ﺗﺠﺰﻳﻪ را اﻋﻼم ﻣﻲ‬
‫ﻛﻨﺪ‪.‬‬

‫‪ 2 – 16 – 3‬ﻣﺸﻜﻼت ﭼﭗ ﮔﺮدي و راﺳﺖ ﮔﺮدي در روش ﺗﻘﺪم ﺳﺎده‬


‫ﻫﺮﮔﺎه در ﻗﻮاﻋﺪ ﮔﺮاﻣﺮ وﺿﻌﻴﺘﻲ ﺑﺼﻮرت زﻳﺮ ﺑﺎﺷﺪ ﻛﻪ در آن ﻗﺎﻋﺪه اول ﻳﻚ ﻗﺎﻋﺪه‬
‫ﭼﭗ ﮔﺮد اﺳﺖ ﺑﻴﻦ ﻋﻼﻣﺖ ‪ X‬و ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ‪ U‬دو راﺑﻄﻪ ﺗﻘﺪم ﺑﺼﻮرت زﻳﺮ وﺟﻮد دارد‪:‬‬
‫…‪U→U‬‬
‫… ‪V → … xU‬‬
‫‪X = U , X < U‬‬

‫ﺑﺮاي ﺣﻞ اﻳﻦ ﻣﺸﻜﻞ ﻗﻮاﻋﺪ ﻓﻮق را ﺑﺼﻮرت زﻳﺮ ﺗﺒﺪﻳﻞ ﻣﻲ ﻛﻨﻴﻢ ﻛﻪ در آن ‪ 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‬‬
‫ﺗﻮﺟﻪ ﻛﻨﻴﺪ ‪:‬‬

‫اﻧﺒﺎره‬ ‫ورودي‬ ‫ﻋﻤﻞ اﻧﺠﺎم ﺷﺪه‬


‫‪$‬‬ ‫‪(c(cc))$‬‬ ‫اﻧﺘﻘﺎل‬
‫(<‪$‬‬ ‫‪c(cc))$‬‬ ‫اﻧﺘﻘﺎل‬
‫‪$<(<c‬‬ ‫‪(cc))$‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪S → c‬‬
‫‪$<(S‬‬ ‫‪(c c ) ) $‬‬ ‫اﻧﺘﻘﺎل‬
‫(<‪$<(S‬‬ ‫‪c))$‬‬ ‫اﻧﺘﻘﺎل‬
‫‪$<(S<(<c‬‬ ‫‪))$‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪S → c‬‬
‫‪$<(S<(S‬‬ ‫‪))$‬‬ ‫اﻧﺘﻘﺎل‬
‫‪$<(S<(S<c‬‬ ‫‪))$‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ ‪S → c‬‬
‫‪$<(S<(SS‬‬ ‫‪))$‬‬ ‫اﻧﺘﻘﺎل‬
‫‪$<(S<(SS‬‬ ‫‪)$‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ )‪S → (SS‬‬
‫‪$<(SS‬‬ ‫‪)$‬‬ ‫اﻧﺘﻘﺎل‬
‫)‪$<(SS‬‬ ‫‪$‬‬ ‫ﻛﺎﻫﺶ ﺑﻮﺳﻴﻠﻪ )‪S → (SS‬‬
‫‪$S‬‬ ‫‪$‬‬ ‫ﭘﺎﻳﺎن‬

‫‪٧٢‬‬
‫روﺷﻬﺎي ﺗﺠﺰﻳﻪ ‪: LR‬‬
‫ﻳﻜﻲ از اﻣﻦ ﺗﺮﻳﻦ روﺷﻬﺎي ﺗﺠﺰﻳﻪ ﭘﺎﺋﻴﻦ ﺑﻪ ﺑﺎﻻ ﻛﻪ ﻣﻲ ﺗﻮاﻧﺪ در ﻣﻮرد اﻛﺜﺮ ﮔﺮاﻣﺮﻫﺎي‬
‫ﻣﺴﺘﻘﻞ از ﻣﺘﻦ اﻋﻤﺎل ﺷﻮد روش ‪ LR‬اﺳﺖ‪.‬ﻣﺰاﻳﺎي روﺷﻬﺎي ﺗﺠﺰﻳﻪ ‪ LR‬ﻋﺒﺎرﺗﻨﺪ از ‪:‬‬
‫‪ -1‬ﺗﻘﺮﻳﺒﺎ ﺗﻤﺎﻣﻲ ﺳﺎﺧﺘﺎرﻫﺎي زﺑﺎن ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ را ﻣﻲ ﺗﻮان ﺗﻮﺳﻂ ﭘﺎرﺳﺮﻫﺎي ‪LR‬‬
‫ﺗﺸﺨﻴﺺ داد‪.‬‬
‫‪ -2‬روش ﺗﺠﺰﻳﻪ ‪ LR‬ﻛﻠﻲ ﺗﺮﻳﻦ روش ﺗﺠﺰﻳﻪ ﻏﻴﺮ ﺑﺎزﮔﺸﺘﻲ ﺑﻪ ﻃﺮﻳﻘﻪ اﻧﺘﻘﺎل – ﻛﺎﻫﺶ‬
‫اﺳﺖ ﻛﻪ ﺗﺎ ﻛﻨﻮن ﺷﻨﺎﺧﺘﻪ ﺷﺪه و ﻣﻲ ﺗﻮان آن را ﺑﻪ ﻛﺎراﻳﻲ ﻫﺮ روش دﻳﮕﺮي ﭘﻴﺎده‬
‫ﺳﺎزي ﻛﺮد‪.‬‬
‫‪ -3‬ﻣﺠﻤﻮﻋﻪ زﺑﺎﻧﻬﺎﻳﻲ ﻛﻪ ﺗﻮﺳﻂ روش ‪ LR‬ﺗﺠﺰﻳﻪ ﻣﻲ ﺷﻮﻧﺪ ﺷﺎﻣﻞ ﻣﺠﻤﻮﻋﻪ زﺑﺎﻧﻬﺎﻳﻲ‬
‫اﺳﺖ ﻛﻪ ﺗﻮﺳﻂ ﭘﺎرﺳﺮﻫﺎي ﭘﻴﺸﮕﻮ ﺗﺠﺰﻳﻪ ﻣﻲ ﺷﻮﻧﺪ‪.‬‬
‫‪ -4‬ﻳﻚ ﭘﺎرﺳﺮ ‪ LR‬ﺧﻄﺎي ﻧﺤﻮي را در ﻛﻤﺘﺮﻳﻦ زﻣﺎن ﺗﻮﺳﻂ ﺑﺮرﺳﻲ ﭼﭗ ﺑﻪ راﺳﺖ‬
‫ورودي ﭘﻴﺪا ﻣﻲ ﻛﻨﺪ‪.‬‬

‫ﺗﺠﺰﻳﻪ ‪ LR‬ﺧﻮد از ﺳﻪ روش زﻳﺮ ﺗﺸﻜﻴﻞ ﻣﻲ ﺷﻮد ‪:‬‬

‫) ‪1. SLR ( Simple LR‬‬


‫) ‪2. LALR ( Look Ahead LR‬‬
‫) ‪3. CLR ( Canonical LR‬‬

‫ﻳﻜﻲ از ﻣﻌﺎﻳﺐ روش ‪ LR‬آن اﺳﺖ ﻛﻪ ﺣﺠﻢ ﻛﺎر ﺑﺴﻴﺎر زﻳﺎدي دارد ﻛﻪ در ﻣﻮرد ﭘﻴﺎده‬
‫ﺳﺎزي دﺳﺘﻲ آن را ﻣﺸﻜﻞ ﻣﻲ ﻧﻤﺎﻳﺪ وﻟﻲ ﺑﻪ ﻫﺮ ﺣﺎل ﻧﺮم اﻓﺰارﻫﺎي ﺧﻮدﻛﺎري وﺟﻮد‬
‫دارﻧﺪ ﻛﻪ ﻣﻲ ﺗﻮاﻧﻨﺪ ﺑﻪ ﭘﺎرﺳﺮﻫﺎي ‪ LR‬ﻛﻤﻚ ﻛﻨﻨﺪ‪.‬ﻣﺎﻧﻨﺪ ﻧﺮم اﻓﺰار ‪Yacc‬‬

‫‪٧٣‬‬
‫ﺳﺎﺧﺘﺎر ﭘﺎرﺳﺮﻫﺎي ‪ LR‬ﺑﻪ ﻓﺮم زﻳﺮ اﺳﺖ ‪:‬‬

‫‪Input‬‬
‫‪a1‬‬ ‫‪...‬‬ ‫‪ai‬‬ ‫‪...‬‬ ‫‪an‬‬ ‫‪$‬‬

‫‪Sm‬‬

‫‪Xm‬‬ ‫‪LR Parsing Program‬‬ ‫‪Output‬‬

‫‪Sm-1‬‬

‫‪Xm-1‬‬ ‫‪action‬‬ ‫‪goto‬‬

‫‪S0‬‬

‫‪Stack‬‬

‫اﻟﮕﻮرﻳﺘﻢ ﺗﺠﺰﻳﻪ ‪: LR‬‬


‫اﮔﺮ در ﺣﺎل ﺣﺎﺿﺮ ﭘﻴﻜﺮﺑﻨﺪي ) ‪( S0X1S1X2S2 … XmSm , aiai+1 ... an$‬را داﺷﺘﻪ‬
‫ﺑﺎﺷﻴﻢ ﻳﻌﻨﻲ اﻳﻨﻜﻪ ﭘﺎرﺳﺮ ‪ LR‬در ﻗﺪﻣﻲ از ﺗﺠﺰﻳﻪ ﻗﺮار ﮔﺮﻓﺘﻪ ﺑﺎﺷﺪ ﻛﻪ ﺷﻤﺎره وﺿﻌﻴﺖ ﺑﺎﻻي‬
‫ﭘﺸﺘﻪ ‪ Sm‬و ﺗﻮﻛﻦ ﺟﺎري ‪ ai‬ﺑﺎﺷﺪ‪.‬ﺑﻨﺎﺑﺮاﻳﻦ در اﻳﻦ ﺣﺎل ﭘﺎرﺳﺮ ﺑﻪ ﺧﺎﻧﻪ ] ‪action [ Sm , ai‬‬
‫رﺟﻮع ﻣﻲ ﻛﻨﺪ اﮔﺮ اﻳﻦ ﺧﺎﻧﻪ ﺧﺎﻟﻲ ﻧﺒﺎﺷﺪ ﻳﻜﻲ از ﺳﻪ ﺣﺎﻟﺖ زﻳﺮ رخ ﻣﻲ دﻫﺪ ‪:‬‬
‫‪1. Shift‬‬ ‫‪2. Reduce A → α‬‬ ‫‪3. Accept‬‬

‫‪ -1‬اﮔﺮ ‪ action [ Sm , ai ] = ShiftS‬ﺑﺎﺷﺪ در اﻳﻨﺼﻮرت ‪ ai‬از رﺷﺘﻪ ورودي ﺣﺬف‬


‫ﺷﺪه و ﺑﻪ ‪ Push , Stack‬ﻣﻲ ﺷﻮد و ﺳﭙﺲ وﺿﻌﻴﺖ ‪ S‬روي آن ﻗﺮار ﻣﻲ ﮔﻴﺮد‪.‬‬
‫‪ -2‬اﮔﺮ ‪ action [ Sm , ai ] = Reduce A → α‬ﺑﺎﺷﺪ در آﻧﺼﻮرت ﭘﺎرﺳﺮ ﻋﻤﻞ‬
‫ﻛﺎﻫﺶ را اﻧﺠﺎم ﻣﻲ دﻫﺪ و از ﭘﻴﻜﺮﺑﻨﺪي‬
‫) ‪( S0X1S1X2S2 … XmSm, aiai+1 ... an $‬‬

‫‪٧٤‬‬
‫ﺑﻪ ﭘﻴﻜﺮﺑﻨﺪي ) ‪ ( 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‬‬

‫ﻧﺤﻮه ﺗﻬﻴﻪ ﺟﺪول )‪: SLR(1‬‬


‫در ﻣﻴﺎن ﺳﻪ روش ‪ LR‬ﺳﺎده ﺗﺮﻳﻦ روش از ﻧﻈﺮ ﭘﻴﺎده ﺳﺎزي روش‪( Simple LR) SLR‬‬
‫اﺳﺖ‪.‬ﻗﺒﻞ از ﺗﻮﺿﻴﺢ راﺟﻊ ﺑﻪ ﻧﺤﻮه ﺑﺪﺳﺖ آوردن ﺟﺪول )‪ SLR(1‬ذﻛﺮ ﭼﻨﺪ ﻣﻔﻬﻮم زﻳﺮ‬
‫ﺿﺮوري اﺳﺖ ‪:‬‬
‫‪ .1‬ﻳﻚ آﻳﺘﻢ )‪ LR(0‬ﻳﻚ ﻗﺎﻋﺪه از ﮔﺮاﻣﺮ اﺳﺖ ﻛﻪ در ﻣﺤﻠﻲ درﺳﻤﺖ‬
‫راﺳﺖ آن ﻳﻚ ﻋﻼﻣﺖ ﺧﺎص ﺻﻔﺮ ﻳﺎ ‪ point‬وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﺪ‪.‬ﻣﺜﻼ‬
‫اﮔﺮ ﻗﺎﻋﺪه اي ﺑﻪ ﺷﻜﻞ زﻳﺮ داﺷﺘﻪ ﺑﺎﺷﻴﻢ ‪ A → xyz‬ﻣﻴﺘﻮان از روي‬
‫آن ‪ 4‬آﻳﺘﻢ زﻳﺮ را ﺑﺪﺳﺖ آورد ‪:‬‬
‫‪A → .xyz‬‬
‫‪A → x.yz‬‬
‫‪A → xy.z‬‬
‫‪A → xyz.‬‬
‫‪ .2‬اﮔﺮ ﻗﺎﻋﺪه ‪ ε‬داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻳﻌﻨﻲ ‪ A → ε‬ﺗﻨﻬﺎ آﻳﺘﻢ ﻣﺎ ‪ A → .‬اﺳﺖ‪.‬‬

‫‪٧٦‬‬
‫ﺗﻌﺮﻳﻒ ) ‪ : Closure( I‬اﮔﺮ ‪ I‬ﻳﻚ ﻣﺠﻤﻮﻋﻪ از آﻳﺘﻢ ﻫﺎي ﻳﻚ ﮔﺮاﻣﺮ ﺑﺎﺷﺪ در‬
‫آﻧﺼﻮرت ) ‪ Closure( I‬را ﺑﺴﺘﺎر ) ‪ ( I‬ﻣﻲ ﻧﺎﻣﻴﻢ ﻛﻪ آن ﻧﻴﺰ ﻳﻚ ﻣﺠﻤﻮﻋﻪ از آﻳﺘﻢ‬
‫ﻫﺎﺳﺖ و ﺑﺮاي ﻣﺤﺎﺳﺒﻪ آن ﺑﺎﻳﺴﺘﻲ دو ﻗﺪم زﻳﺮ را ﭘﻴﻤﺎﻳﺶ ﻛﺮد ‪:‬‬
‫‪ -1‬ﻫﺮ آﻳﺘﻤﻲ ﻛﻪ در ‪ I‬وﺟﻮد دارد را ﺑﻪ ) ‪ Closure ( I‬ﻣﻲ اﻓﺰاﺋﻴﻢ ‪.‬‬
‫‪ -2‬اﮔﺮ ﻗﺎﻋﺪه اي ﺑﻪ ﺷﻜﻞ ‪ A → α.Bβ‬در ﺑﺴﺘﺎر ‪ I‬داﺷﺘﻪ ﺑﺎﺷﻴﻢ و ‪ B → δ‬ﻳﻚ‬
‫ﻗﺎﻋﺪه از ﮔﺮاﻣﺮ ﺑﺎﺷﺪ آﻧﮕﺎه ﻗﺎﻋﺪه ‪ B → .δ‬را ﺑﻪ ﺑﺴﺘﺎر ‪ I‬ﻣﻲ اﻓﺰاﺋﻴﻢ‪.‬‬

‫رﺳﻢ دﻳﺎﮔﺮام اﻧﺘﻘﺎل ‪: SLR‬‬


‫ﻫﻤﺎﻧﻄﻮري ﻛﻪ ﮔﻔﺘﻪ ﺷﺪ ﺑﺮاي ﺗﻬﻴﻪ ﺟﺪول ﺗﺠﺰﻳﻪ روﺷﻬﺎي ‪ LR‬ﺑﺎﻳﺴﺘﻲ ﻳﻚ دﻳﺎﮔﺮام اﻧﺘﻘﺎل‬
‫رﺳﻢ ﻛﺮد‪.‬اﻳﻦ اﻣﺮ در ﻣﻮرد روش )‪ SLR(1‬ﺑﻪ ﺷﻜﻞ زﻳﺮ ﺧﻮاﻫﺪ ﺑﻮد ‪:‬‬

‫‪ .1‬اﺑﺘﺪا ﻗﺎﻋﺪه اي ﺑﻪ ﺷﻜﻞ ‪ S' →.S‬ﻛﻪ در آن ‪ S‬ﻋﻼﻣﺖ ﺷﺮوع ﮔﺮاﻣﺮ اﺳﺖ‬


‫و '‪ S‬ﻳﻚ ‪ nonterminal‬ﺟﺪﻳﺪ اﺳﺖ اﺿﺎﻓﻪ ﻣﻲ ﻛﻨﻴﻢ‪.‬ﺑﻪ اﻳﻦ ﮔﺮاﻣﺮ ﻳﻚ‬
‫ﮔﺮاﻣﺮ اﻓﺰوده ﮔﻮﺋﻴﻢ‪.‬‬
‫‪ .2‬رﺳﻢ دﻳﺎﮔﺮام را ﺑﺎ وﺿﻌﻴﺖ ‪ I0‬و آﻳﺘﻢ ‪ S' →.S‬ﺷﺮوع ﻣﻲ ﻛﻨﻴﻢ و ﺳﭙﺲ‬
‫ﺑﺴﺘﺎر اﻳﻦ آﻳﺘﻢ را ﻣﺤﺎﺳﺒﻪ ﻛﺮده و در ‪ I0‬ﻗﺮار ﻣﻲ دﻫﻴﻢ‪.‬ﺳﭙﺲ درﺻﻮرﺗﻴﻜﻪ‬
‫در ﺣﺎﻟﺖ ‪ I0‬ﻳﺎ ﺑﻄﻮر ﻛﻠﻲ در ﺣﺎﻟﺖ ‪ Ii‬ﺑﺎﺷﻴﻢ آﻳﺘﻢ ﻫﺎﻳﻲ ﻛﻪ ﺑﻪ ﺷﻜﻞ زﻳﺮ‬
‫اﻧﺪ ﻛﻪ در آﻧﻬﺎ ‪ X‬ﻣﻲ ﺗﻮاﻧﺪ ﻳﻚ ﭘﺎﻳﺎﻧﻪ ﻳﺎ ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﺑﺎﺷﺪ وﺟﻮد دارد‪.‬‬
‫‪A1 → α1.Xβ1‬‬
‫‪A2 → α2.Xβ2‬‬
‫‪An → αn.Xβn‬‬

‫ﺑﺎ ﺑﻮﺟﻮد آﻣﺪن وﺿﻌﻴﺖ ﺟﺪﻳﺪ ‪ 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‬‬

‫اﻳﻦ ﮔﺮاﻣﺮ )‪ SLR(1‬ﻧﻴﺴﺖ‪.‬‬

‫دﻳﺎﮔﺮام ﺟﺪول ﺗﺠﺰﻳﻪ ‪ CLR‬و ‪: LALR‬‬


‫ﻳﻚ روش ﺑﺮاي ﺗﻬﻴﻪ دﻳﺎﮔﺮام ‪ LALR‬از ﻃﺮﻳﻖ رﺳﻢ دﻳﺎﮔﺮام ‪ CLR‬اﺳﺖ‪.‬ﺑﺮاي اﻳﻦ‬
‫ﻣﻨﻈﻮر ﻣﻔﻬﻮم آﻳﺘﻢ ﻫﺎي )‪ LR(1‬را ﺗﻌﺮﻳﻒ ﻣﻲ ﻛﻨﻴﻢ‪.‬‬
‫ﻳﻚ آﻳﺘﻢ )‪ LR(1‬ﻋﺒﺎرﺗﺴﺖ از ﻳﻚ زوج ﻣﺮﺗﺐ ﻣﺘﺸﻜﻞ از ﻳﻚ آﻳﺘﻢ )‪ LR(0‬و ﻳﻚ‬
‫ﻣﺠﻤﻮﻋﻪ از ﭘﺎﻳﺎﻧﻪ ﻫﺎ ﺑﻨﺎم ﻣﺠﻤﻮﻋﻪ ﭘﻴﺶ ﺑﻴﻨﻲ و ﻣﻌﻤﻮﻻ اﻳﻦ آﻳﺘﻢ را ﺑﻪ ﺷﻜﻞ ﻛﻠﻲ‬
‫) ‪ ( A → α.Bβ , LA‬ﻧﻤﺎﻳﺶ ﻣﻲ دﻫﻴﻢ‪.‬راﺑﻄﻪ ﺑﻴﻦ ﻣﺠﻤﻮﻋﻪ ﭘﻴﺶ ﺑﻴﻨﻲ ‪ LA‬و ﻣﺠﻤﻮﻋﻪ‬
‫‪Follow‬ي ﻏﻴﺮﭘﺎﻳﺎﻧﻪ ﻫﺎي ‪ A‬ﺑﻪ ﺷﻜﻞ زﻳﺮ اﺳﺖ ‪:‬‬
‫)‪LA € Follow(A‬‬

‫اﻟﮕﻮرﻳﺘﻢ ﻣﺤﺎﺳﺒﻪ ﺗﺎﺑﻊ ﺑﺴﺘﺎر در ﻣﻮرد اﻟﮕﻮرﻳﺘﻢ ﺗﺎﺑﻌﻬﺎي )‪ 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‬ﻫﺎ ﻛﻤﻚ ﻧﻤﻲ ﮔﻴﺮﻳﻢ‪.‬‬

‫‪State‬‬ ‫‪c‬‬ ‫‪d‬‬ ‫‪$‬‬ ‫‪S‬‬ ‫‪C‬‬


‫‪0‬‬ ‫‪S3‬‬ ‫‪S4‬‬ ‫‪1‬‬ ‫‪2‬‬
‫‪1‬‬ ‫‪acc‬‬
‫‪2‬‬ ‫‪S6‬‬ ‫‪S7‬‬ ‫‪5‬‬
‫‪3‬‬ ‫‪S3‬‬ ‫‪S4‬‬ ‫‪8‬‬
‫‪4‬‬ ‫‪r3‬‬ ‫‪r3‬‬
‫‪5‬‬ ‫‪r1‬‬
‫‪6‬‬ ‫‪S6‬‬ ‫‪S7‬‬ ‫‪9‬‬
‫‪7‬‬ ‫‪r3‬‬
‫‪8‬‬ ‫‪r2‬‬ ‫‪r2‬‬
‫‪9‬‬ ‫‪r2‬‬

‫‪٨٤‬‬
‫رﺳﻢ دﻳﺎﮔﺮام و ﺟﺪول ﺗﺠﺰﻳﻪ ‪: LALR‬‬
‫ﺑﺮاي رﺳﻢ دﻳﺎﮔﺮام ‪ LALR‬اﺑﺘﺪا دﻳﺎﮔﺮام ‪ CLR‬را ﺗﺮﺳﻴﻢ ﻛﺮده و ﺳﭙﺲ وﺿﻌﻴﺘﻬﺎي اﻳﻦ‬
‫دﻳﺎﮔﺮام را ﺑﻪ ﺷﻜﻞ زﻳﺮ ادﻏﺎم ﻣﻲ ﻛﻨﻴﻢ‪.‬ﺑﺪﻳﻦ ﺗﺮﺗﻴﺐ ﻛﻪ در وﺿﻌﻴﺘﻬﺎي ﻣﺘﻔﺎوﺗﻲ ﻛﻪ آﻳﺘﻢ‬
‫ﻫﺎي )‪ LR(0‬آﻧﻬﺎ ﻳﻜﺴﺎن اﺳﺖ و ﻋﻼﻣﺖ ﭘﻴﺶ ﺑﻴﻨﻲ آﻧﻬﺎ ﻣﺘﻔﺎوت اﺳﺖ آﻧﻬﺎ را ﻳﻜﻲ ﻓﺮض‬
‫ﻣﻲ ﻧﻤﺎﺋﻴﻢ و ﺑﺨﺶ ﭘﻴﺶ ﺑﻴﻨﻲ آﻧﻬﺎ را ﺑﻪ ﺷﻜﻞ اﺟﺘﻤﺎع ﭘﻴﺶ ﺑﻴﻨﻲ ﻫﺎي دو وﺿﻌﻴﺖ در ﻧﻈﺮ ﻣﻲ‬
‫ﮔﻴﺮﻳﻢ‪.‬‬

‫ﻣﺜﺎل – ﺑﺮاي ﻣﺜﺎل ﻗﺒﻞ وﺿﻌﻴﺘﻬﺎي ‪ 7,4‬و ‪ 8,9‬و ‪ 3,6‬ﻳﻜﻲ ﻣﻲ ﺷﻮﻧﺪ‪.‬‬

‫‪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 → d.,c|d|$‬‬ ‫‪c‬‬ ‫‪d‬‬


‫‪d‬‬
‫‪I7‬‬

‫‪I89‬‬ ‫‪C → d.,$‬‬ ‫‪C‬‬


‫‪I36‬‬
‫‪d‬‬ ‫‪C‬‬
‫‪C → c.C,c|d|$‬‬ ‫‪C → cC.,c|d|$‬‬
‫‪C → .cC,c|d|$‬‬
‫‪C → .d,c|d|$‬‬ ‫‪I9‬‬

‫‪C → cC.,$‬‬

‫‪c‬‬

‫‪٨٥‬‬
‫ﺑﺮاي ﻣﺜﺎل ﻗﺒﻞ ﺗﻐﻴﻴﺮات را اﻋﻤﺎل ﻣﻲ ﻛﻨﻴﻢ‪.‬‬

‫‪c‬‬ ‫‪d‬‬ ‫‪$‬‬ ‫‪S‬‬ ‫‪C‬‬


‫‪0‬‬ ‫‪S36‬‬ ‫‪S47‬‬ ‫‪1‬‬ ‫‪2‬‬
‫‪1‬‬ ‫‪acc‬‬
‫‪2‬‬ ‫‪S36‬‬ ‫‪S47‬‬ ‫‪5‬‬
‫‪36‬‬ ‫‪S36‬‬ ‫‪S47‬‬ ‫‪89‬‬
‫‪47‬‬ ‫‪r3‬‬ ‫‪r3‬‬ ‫‪r3‬‬
‫‪5‬‬ ‫‪r1‬‬
‫‪89‬‬ ‫‪r2‬‬ ‫‪r2‬‬ ‫‪r2‬‬

‫اﮔﺮ ﮔﺮاﻣﺮي ‪ CLR‬ﺑﺎﺷﺪ و ﭘﺲ از ﺑﺪﺳﺖ آوردن ﺟﺪول ﺗﺠﺰﻳﻪ ‪ LALR‬ﺗﺪاﺧﻠﻲ ﺑﻮﺟﻮد‬


‫ﻧﻴﺎﻳﺪ ﻣﻴﺘﻮان ﻧﺘﻴﺠﻪ ﮔﺮﻓﺖ ﻛﻪ ﮔﺮاﻣﺮ ‪ LALR‬اﺳﺖ‪.‬‬
‫اﮔﺮ ﮔﺮاﻣﺮي ‪ CLR‬ﺑﺎﺷﺪ وﻟﻲ ‪ LALR‬ﻧﺒﺎﺷﺪ ﭘﺲ از ادﻏﺎم در ﺟﺪول ﺗﺠﺰﻳﻪ ‪ LALR‬از‬
‫ﮔﺮاﻣﺮ ﻣﻤﻜﻦ اﺳﺖ ﺗﺪاﺧﻞ ﻧﻮع ﻛﺎﻫﺶ – ﻛﺎﻫﺶ رخ دﻫﺪ‪.‬‬
‫ﺗﻮﺟﻪ ‪ :‬در ﺟﺪول ‪ LALR‬ﻫﻴﭻ وﻗﺖ ﺗﺪاﺧﻞ اﻧﺘﻘﺎل – ﻛﺎﻫﺶ رخ ﻧﻤﻲ دﻫﺪ ﻣﮕﺮ آﻧﻜﻪ‬
‫ﮔﺮاﻣﺮ از ﻧﻮع ‪ CLR‬ﻧﺒﺎﺷﺪ‪.‬‬

‫ﺗﻤﺮﻳﻨﻲ راﺟﻊ ﺑﻪ ﻣﺒﺎﺣﺚ ﻗﺒﻠﻲ ‪:‬‬


‫) ‪S → ( SS‬‬ ‫} ‪Head(s) = { ( , c‬‬
‫‪S→c‬‬ ‫} ‪Tail(s) = { ) , c‬‬
‫‪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‬‬ ‫‪$‬‬ ‫(‬ ‫)‬ ‫‪C‬‬

‫‪S‬‬ ‫=‬ ‫=‬ ‫<‬ ‫=‬ ‫<‬


‫‪$‬‬ ‫=‬ ‫<‬ ‫<‬
‫(‬ ‫=‬ ‫<‬ ‫<‬
‫)‬ ‫>‬ ‫>‬ ‫>‬ ‫>‬
‫‪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‬‬

‫در ﺣﺎﻟﺖ ‪ 2‬ﺗﻤﺎﻣﻲ وارده ﻫﺎي ﺧﻄﺎ را ﻣﻲ ﺗﻮان ﺑﺎ ‪ r2‬ﺟﺎﻳﮕﺰﻳﻦ ﻧﻤﻮد‪:‬‬


‫*‬ ‫‪S7‬‬
‫‪any‬‬ ‫‪R2‬‬

‫ﻟﻴﺴﺖ ﺣﺎﻟﺖ ‪ 8‬ﻋﺒﺎرت اﺳﺖ از‪:‬‬


‫‪+‬‬ ‫‪S6‬‬
‫)‬ ‫‪S11‬‬
‫‪any‬‬ ‫‪Error‬‬

‫و ﺑﺮاي ﺣﺎﻟﺖ ‪ 9‬ﺑﻪ ﺻﻮرت‪:‬‬


‫*‬ ‫‪S7‬‬
‫‪any‬‬ ‫‪R1‬‬

‫ﺟﺪول ‪ 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‬‬

‫اﺳﺘﻔﺎده از اوﻟﻮﻳﺖ و ﺷﺮﻛﺖ ﭘﺬﻳﺮي ﺑﻪ ﻣﻨﻈﻮر رﻓﻊ ﺗﻨﺎﻗﺾ ﻫﺎي ﺑﺨﺶ‬


‫‪: action‬‬
‫ﮔﺮاﻣﺮ زﻳﺮ ﺑﺮاي ﻋﺒﺎرت ﻫﺎي ﻣﺤﺎﺳﺒﺎﺗﻲ‪ ،‬ﺑﺎ ﻋﻤﻠﮕﺮ ﻫﺎي ‪ * ، +‬ﻣﺒﻬﻢ اﺳﺖ زﻳﺮا ﺷﺮﻛﺖ‬
‫ﭘﺬﻳﺮي ﻳﺎ اوﻟﻮﻳﺖ *‪ +,‬را ﻣﺸﺨﺺ ﻧﻤﻲ ﻛﻨﺪ ‪:‬‬
‫‪E **** E+E | E*E |(E) | id‬‬
‫ﮔﺮاﻣﺮ ﻏﻴﺮ ﻣﺒﻬﻢ زﻳﺮ ﻫﻤﺎن زﻣﺎن را ﺗﻮﻟﻴﺪ ﻣﻴﻜﻨﺪ اﻣﺎ ﺑﻪ ‪ +‬اوﻟﻮﻳﺖ ﻛﻤﺘﺮي از * ﻣﻲ دﻫﺪ و ﻫﺮ‬
‫دو ﻋﻤﻠﮕﺮ را ﺑﺎ ﺷﺮ ﻛﺖ ﭘﺬﻳﺮي ﭼﭗ ﺗﻌﺮﻳﻒ ﻣﻲ ﻛﻨﺪ‪.‬‬
‫‪E E+T |T‬‬
‫‪T T*F | F‬‬
‫‪F (E) | id‬‬
‫دو دﻟﻴﻞ وﺟﻮد دارد ﻛﻪ از ﮔﺮاﻣﺮ اوﻟﻲ ﺑﺠﺎي دوﻣﻲ اﺳﺘﻔﺎده ﻣﻲ ﺷﻮد‪:‬‬
‫‪ -1‬ﺗﺠﺰﻳﻪ ﻛﻨﻨﺪه ﮔﺮاﻣﺮ دوم زﻣﺎن زﻳﺎدي را ﺻﺮف ﻛﺎﻫﺶ ﺑﺎ ﻣﻮﻟﺪ ﻫﺎي ‪ E T , T F‬ﻣﻲ‬
‫ﻧﻤﺎﻳﺪ‪.‬‬
‫‪ -2‬ﺑﻪ راﺣﺘﻲ ﻣﻲ ﺗﻮان ﺷﺮ ﻛﺖ ﭘﺬﻳﺮي و اوﻟﻮﻳﺖ را ﺑﺮاي ﻋﻤﻠﮕﺮ ﻫﺎي *‪ +,‬ﺑﺪون اﺿﺎﻓﻪ‬
‫ﻛﺮدن ﺗﻌﺪاد ﺣﺎ ﻟﺖ ﻫﺎ در ﮔﺮاﻣﺮ ﺗﻌﺮﻳﻒ ﻛﺮد‪.‬‬
‫ﺷﻜﻞ زﻳﺮ ﻣﺠﻤﻮﻋﻪ ‪Item‬ﻫﺎي )‪ LR(0‬را ﺑﺮاي ﮔﺮاﻣﺮ‬

‫‪٨٩‬‬
‫‪E **** E+E | E*E |(E) | id‬‬
‫ﻛﻪ ‪ E` E‬ﺑﻪ آن اﺿﺎﻓﻪ ﺷﺪه ﻧﺸﺎن ﻣﻲ دﻫﺪ‪.‬‬

‫‪I5:‬‬ ‫‪I0 :‬‬


‫‪E E* .E‬‬ ‫‪E` .E‬‬
‫‪E .E+E‬‬ ‫‪E .E+E‬‬
‫‪E .E *E‬‬ ‫‪E .E*E‬‬
‫)‪E .(E‬‬ ‫)‪E .(E‬‬
‫‪E .id‬‬ ‫‪E .id‬‬

‫‪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‬ﺑﻪ ﺷﻜﻞ زﻳﺮ ﺑﻪ دﺳﺖ ﻣﻲ آﻳﺪ‪:‬‬

‫ﺣﺎﻟﺖ‬ ‫‪id‬‬ ‫‪+‬‬ ‫*‬ ‫(‬ ‫)‬ ‫‪$‬‬ ‫‪E‬‬


‫‪0‬‬ ‫‪S3‬‬ ‫‪S2‬‬ ‫‪1‬‬

‫‪1‬‬ ‫‪S4‬‬ ‫‪S5‬‬ ‫‪acc‬‬

‫‪2‬‬ ‫‪S3‬‬ ‫‪S2‬‬ ‫‪6‬‬

‫‪3‬‬ ‫‪R4‬‬ ‫‪R4‬‬ ‫‪R4‬‬ ‫‪R4‬‬

‫‪4‬‬ ‫‪S3‬‬ ‫‪S2‬‬ ‫‪8‬‬

‫‪5‬‬ ‫‪S3‬‬ ‫‪S2‬‬ ‫‪8‬‬

‫‪6‬‬ ‫‪S4‬‬ ‫‪S5‬‬ ‫‪S9‬‬

‫‪7‬‬ ‫‪R1‬‬ ‫‪S5‬‬ ‫‪R1‬‬ ‫‪R1‬‬

‫‪8‬‬ ‫‪R2‬‬ ‫‪R2‬‬ ‫‪R2‬‬ ‫‪R2‬‬

‫‪9‬‬ ‫‪R3‬‬ ‫‪R3‬‬ ‫‪R3‬‬ ‫‪R3‬‬

‫‪٩٢‬‬
‫ﺟﺪول ﻫﺎي ﻧﻤﺎد‬

‫ﻛﺎﻣﭙﺎﻳﻠﺮ از ﺟﺪول ﻧﻤﺎد ﺑﺮاي ﻧﮕﻬﺪاري اﻃﻼﻋﺎت و ﻣﺤﺪوده ﺗﻌﺮﻳﻒ ﻧﺎم ﻫﺎي ﺑﺮﻧﺎﻣﻪ اﺳﺘﻔﺎده‬

‫ﻣﻲ ﻧﻤﺎﻳﺪ ‪ .‬ﻫﺮ زﻣﺎن ﻧﺎﻣﻲ در ﻣﺘﻦ ﺑﺮﻧﺎﻣﻪ ﻳﺎﻓﺖ ﻣﻲ ﺷﻮد ‪ ،‬ﺟﺪول ﻧﻤﺎد ﺟﺴﺘﺠﻮ ﻣﻲ ﮔﺮدد ‪.‬‬

‫ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﻧﺎم ﺟﺪﻳﺪي ﻳﺎ اﻃﻼﻋﺎت ﺟﺪﻳﺪي در راﺑﻄﻪ ﺑﺎ ﻧﺎم ﻫﺎي ﻣﻮﺟﻮد در ﺟﺪول ﻳﺎﻓﺖ‬

‫ﺷﻮد ‪ ،‬ﺟﺪول ﺗﻐﻴﻴﺮ ﻣﻲ ﻧﻤﺎﻳﺪ ‪.‬‬

‫ﻣﻜﺎﻧﺰﻳﻢ ﺟﺪول ﻧﻤﺎد ﺑﺎﻳﺪ اﻳﻦ اﻣﻜﺎن را ﻓﺮاﻫﻢ ﺳﺎزد ﻛﻪ اﺿﺎﻓﻪ ﻧﻤﻮدن وارده ﻫﺎي ﺟﺪﻳﺪ و‬

‫ﺟﺴﺘﺠﻮي وارده ﻫﺎي ﻣﻮﺟﻮد ﺑﻪ ﺻﻮرت ﻛﺎرآﻣﺪ اﻧﺠﺎم ﮔﻴﺮد ‪ .‬دو ﻣﻜﺎﻧﻴﺰم ﺟﺪول ﻧﻤﺎد ﻛﻪ‬

‫در اﻳﻦ ﺑﺨﺶ اراﺋﻪ ﺷﺪه اﺳﺖ ‪ ،‬ﻟﻴﺴﺖ ﻫﺎي ﺧﻄﻲ و ﺟﺪول در ﻫﻢ ﻣﻲ ﺑﺎﺷﻨﺪ ‪ .‬ﻫﺮ ﻳﻚ از اﻳﻦ‬

‫ﻃﺮح ﻫﺎ ﺑﺮ ﻣﺒﻨﺎي زﻣﺎن ﻻزم ﺟﻬﺖ اﻓﺰودن ‪ n‬وارده و اﻧﺠﺎم ‪ e‬در ﺧﻮاﺳﺖ ﺑﺮرﺳﻲ ﻣﻲ‬

‫ﮔﺮدﻧﺪ ‪.‬ﻟﻴﺴﺖ ﺧﻄﻲ آﺳﺎﻧﺘﺮﻳﻦ آن ﻫﺎ ﺑﺮاي ﺳﺎﺧﺖ ﻣﻲ ﺑﺎﺷﺪ ‪ .‬اﻣﺎ زﻣﺎﻧﻲ ﻛﻪ ‪ n‬و‪ e‬ﺑﻪ ﺳﻤﺖ‬

‫ﻣﻘﺎدﻳﺮ ﺑﺰرگ ﻣﻴﻞ ﻛﻨﻨﺪ ﻛﺎراﻳﻲ ﻛﺎﻫﺶ ﻣﻲ ﻳﺎﺑﺪ ‪ .‬ﻃﺮح ﻫﺎي درﻫﻢ ﻛﺎراﻳﻲ ﺑﻬﺘﺮي را ﺑﺎ‬

‫ﻓﻌﺎﻟﻴﺖ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺴﻲ و ﺻﺮف ﻓﻀﺎي ﺑﻴﺸﺘﺮ اراﺋﻪ ﻣﻴﺪﻫﻨﺪ ‪ .‬ﻫﺮ دو روش ﻣﻲ ﺗﻮاﻧﻨﺪ ﺑﻪ ﮔﻮﻧﻪ‬

‫اي ﺳﺎزﮔﺎر ﺷﻮﻧﺪ ﻛﻪ ﺑﺘﻮاﻧﻨﺪ اﻛﺜﺮ ﻗﻮاﻧﻴﻦ ﻣﺤﺪوده ﺑﺮﻧﺎﻣﻪ را اداره ﻧﻤﺎﻳﻨﺪ ‪.‬‬

‫ﺑﺮاي ﻛﺎﻣﭙﺎﻳﻠﺮﻣﻔﻴﺪ اﺳﺖ ﻛﻪ ﺑﺘﻮاﻧﺪ در ﺻﻮرت ﻟﺰوم در زﻣﺎن ﻛﺎﻣﭙﺎﻳﻞ اﻧﺪازه ﺟﺪول ﻧﻤﺎد را‬

‫ﺑﻪ ﺻﻮرت ﭘﻮﻳﺎ اﻓﺰاﻳﺶ دﻫﺪ ‪ .‬اﮔﺮ در زﻣﺎن ﻧﻮﺷﺘﻦ ﻛﺎﻣﭙﺎﻳﻠﺮ ‪ ،‬اﻧﺪازه ﺟﺪول ﻧﻤﺎد ﺛﺎﺑﺖ ﺑﺎﺷﺪ ‪،‬‬

‫اﻧﺪازه آن ﺑﺎﻳﺪ ﺑﻪ اﻧﺪازه ﻛﺎﻓﻲ ﺑﺰرگ اﻧﺘﺨﺎب ﺷﻮد ﺗﺎ ﺑﺘﻮاﻧﺪ ﻫﺮ ﺑﺮﻧﺎﻣﻪ ﻣﺒﺪا ﻣﻤﻜﻦ را اداره‬

‫ﻧﻤﺎﻳﺪ ‪ .‬اﻳﻦ ﭼﻨﻴﻦ اﻧﺪازه ﺛﺎﺑﺘﻲ ﺑﺮاي اﻛﺜﺮ ﺑﺮﻧﺎﻣﻪ ﻫﺎ ﺑﺴﻴﺎر ﺑﺰرگ و ﺑﺮاي ﺑﻌﻀﻲ ﻧﺎﻣﻨﺎﺳﺐ اﺳﺖ‪.‬‬

‫‪٩٣‬‬
‫وارده ﻫﺎي ﺟﺪول ﻧﻤﺎد‬

‫ﻫﺮ وارده در ﺟﺪول ﻧﻤﺎد ‪ ،‬ﺑﺮاي اﻋﻼن ﻧﺎم اﺳﺖ ‪ .‬ﻗﺎﻟﺐ ﻳﺎ ﺷﻜﻞ وارده ﻫﺎ ﻟﺰوﻣﻲ ﻧﺪارد ﻛﻪ‬

‫ﻳﻜﻨﻮاﺧﺖ ﺑﺎﺷﺪ ‪ ،‬زﻳﺮا اﻃﻼﻋﺎت ذﺧﻴﺮه ﺷﺪه ﺑﺮاي ﻧﺎم ‪ ،‬ﺑﻪ اﺳﺘﻔﺎده ازآن ﻧﺎم ﺑﺴﺘﮕﻲ دارد ‪ .‬ﻫﺮ‬

‫وارده ﻣﻲ ﺗﻮاﻧﺪ ﺑﻪ ﻋﻨﻮان رﻛﻮردي ﻛﻪ ﺷﺎﻣﻞ دﻧﺒﺎﻟﻪ اي از ﻛﻠﻤﺎت ﻣﺘﻮاﻟﻲ ﺣﺎﻓﻈﻪ اﺳﺖ ‪ ،‬ﭘﻴﺎده‬

‫ﺳﺎزي ﺷﻮد ‪ .‬ﺑﻪ ﻣﻨﻈﻮر ﺣﻔﻆ ﻳﻜﻨﻮاﺧﺘﻲ رﻛﻮردﻫﺎي ﺟﺪول ﻧﻤﺎد ‪ ،‬ﺑﻌﻀﻲ از اﻃﻼﻋﺎت ﻣﺮﺑﻮط‬

‫ﺑﻪ ﻧﺎم ﻣﻨﺎﺳﺐ ﺗﺮ اﺳﺖ ﻛﻪ درﺧﺎرج ازاﻳﻦ وارده ﺟﺪول ﻧﮕﻬﺪاري ﺷﻮﻧﺪ و ﺗﻨﻬﺎ ﻳﻚ اﺷﺎره ﮔﺮ‬

‫ﺑﻪ اﻃﻼﻋﺎت ذﺧﻴﺮه ﺷﺪه در اﻳﻦ رﻛﻮرد ‪ ،‬ﻗﺮار داده ﺷﻮد ‪.‬‬

‫اﻃﻼﻋﺎت در زﻣﺎن ﻫﺎي ﻣﺘﻔﺎوﺗﻲ ﺑﻪ ﺟﺪول ﻧﻤﺎد وارد ﻣﻲ ﺷﻮﻧﺪ ‪ .‬ﻛﻠﻤﺎت ﻛﻠﻴﺪي در اﺑﺘﺪا ﺑﻪ‬

‫ﺟﺪول وارد ﻣﻲ ﺷﻮﻧﺪ ‪ .‬ﺗﺤﻠﻴﻞ ﮔﺮ ﻟﻐﻮي ‪ ،‬دﻧﺒﺎﻟﻪ اي از ﺣﺮوف و ارﻗﺎم را درﺟﺪول ﻧﻤﺎد‬

‫ﺟﺴﺘﺠﻮ ﻣﻲ ﻧﻤﺎﻳﺪ ﺗﺎ ﻣﺸﺨﺺ ﺷﻮد ﻛﻪ ﻳﻚ ﻛﻠﻤﻪ ﻛﻠﻴﺪي و ﻳﺎ ﻧﺎم ﺗﺸﺨﻴﺺ داده ﺷﺪه اﺳﺖ ‪.‬‬

‫دراﻳﻦ روش ‪ ،‬ﻛﻠﻤﺎت ﻛﻠﻴﺪي ﻗﺒﻞ از اﻳﻨﻜﻪ ﺗﺤﻠﻴﻞ ﮔﺮ ﻟﻐﻮي ﻛﺎر ﺧﻮد را آﻏﺎز ﻧﻤﺎﻳﺪ ‪ ،‬ﺑﺎﻳﺪ‬

‫در ﺟﺪول وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﻨﺪ ‪ .‬در ﻣﻘﺎﺑﻞ ‪ ،‬اﮔﺮ ﺗﺤﻠﻴﻞ ﮔﺮ ﻟﻐﻮي در ﺿﻤﻦ ﻛﺎر ﻛﻠﻤﺎت‬

‫ﻛﻠﻴﺪي رزرو ﺷﺪه را ﺗﺸﺨﻴﺺ ﻣﻲ دﻫﺪ ‪ ،‬ﻧﻴﺎزي ﺑﻪ ﺣﻀﻮرآﻧﻬﺎ در ﺟﺪول ﻧﻤﺎد ﻧﻤﻲ ﺑﺎﺷﺪ ‪.‬‬

‫اﮔﺮ زﺑﺎن ﻛﻠﻤﺎت ﻛﻠﻴﺪي را رزرو ﻧﻜﻨﺪ‪ ،‬ﻻزم اﺳﺖ ﻛﻪ ﻛﻠﻤﺎت ﻛﻠﻴﺪي ﺑﻪ ﺟﺪول وارد ﺷﻮﻧﺪ‬

‫ﺑﺎ اﻳﻦ ﻫﺸﺪار ﻛﻪ اﻣﻜﺎن اﺳﺘﻔﺎده آﻧﻬﺎ ﺑﻪ ﻋﻨﻮان ﻛﻠﻤﻪ ﻛﻠﻴﺪي وﺟﻮد دارد ‪.‬‬

‫ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﻧﻘﺶ ﻳﻚ ﻧﺎم روﺷﻦ ﺷﻮد ‪ ،‬وارده اي از ﺟﺪول ﻧﻤﺎد ﻣﻲ ﺗﻮاﻧﺪ ﺑﻪ آن اﺧﺘﺼﺎص‬

‫داده ﺷﻮد و ﺑﻪ ﻣﺤﺾ اﻳﻨﻜﻪ اﻃﻼﻋﺎت ﻣﺮﺑﻮط ﺑﻪ آن در دﺳﺘﺮس ﻗﺮار ﮔﺮﻓﺖ ﻣﻘﺎدﻳﺮ ﺻﻔﺖ‬

‫‪٩٤‬‬
‫آن وارده در ﺟﺪول ﺗﻜﻤﻴﻞ ﻣﻲ ﮔﺮدد ‪.‬در ﺑﺮﺧﻲ از ﻣﻮارد ‪ ،‬ﺑﻪ ﻣﺤﺾ اﻳﻨﻜﻪ ﺗﺤﻠﻴﻞ ﮔﺮ ﻟﻐﻮي‬

‫ﻧﺎم را در ورودي دﻳﺪ وارده اي ﺑﺮاي آن در ﺟﺪول ﻧﻤﺎد اﺧﺘﺼﺎص ﻣﻲ دﻫﺪ اﻏﻠﺐ اوﻗﺎت ‪،‬‬

‫ﻳﻚ ﻧﺎم ﻣﻲ ﺗﻮاﻧﺪ ﻣﺸﺨﺺ ﻛﻨﻨﺪه ﭼﻨﺪﻳﻦ ﺷﻲء ﺑﺎﺷﺪ ﺣﺘﻲ در ﻳﻚ ﺑﻠﻮك ﻳﺎروﻳﻪ ‪ .‬ﺑﺮاي‬

‫ﻣﺜﺎل اﻋﻼن ﻫﺎي ‪ C‬ﺑﻪ ﺻﻮرت ‪:‬‬

‫)‪ -1‬اﻟﻒ(‬

‫;‪Int x‬‬
‫}; ‪Struct x { float y , z‬‬
‫‪ x‬را ﻫﻢ ﺑﻪ ﻋﻨﻮان ﻣﺘﻐﻴﺮ وﻫﻢ ﺑﻪ ﻋﻨﻮان ﻳﻚ ﺳﺎﺧﺘﺎر ﺑﺎ دو ﻓﻴﻠﺪ ﻣﻌﺮﻓﻲ ﻣﻲ ﻧﻤﺎﻳﺪ ‪ .‬درﭼﻨﻴﻦ‬

‫ﻣﻮاردي ‪ ،‬ﺗﺤﻠﻴﻞ ﮔﺮ ﻟﻐﻮي ﻣﻲ ﺗﻮاﻧﺪ ﺑﺠﺎي اﺷﺎره ﮔﺮي ﺑﻪ وارده ﺟﺪول ﻧﻤﺎد ) ﻳﺎ ﻳﻚ اﺷﺎره‬

‫ﮔﺮ ﺑﻪ ﻟﻐﺖ ﺗﺸﻜﻴﻞ دﻫﻨﺪه آن ﻧﺎم ( ﻓﻘﻂ ﻧﺎم را ﺑﻪ ﺗﺠﺰﻳﻪ ﻛﻨﻨﺪه ﺑﺮﮔﺮداﻧﺪ ‪ .‬رﻛﻮرد ﺟﺪول‬

‫ﻧﻤﺎد ‪ ،‬ﻫﻨﮕﺎﻣﻲ ﻛﻪ اﻳﺠﺎد ﻣﻲ ﺷﻮد ﻛﻪ ﻧﻘﺶ ﻧﺤﻮي اﻳﻦ ﻧﺎم روﺷﻦ ﮔﺮدد ‪ .‬ﺑﺮاي اﻋﻼن ﻫﺎي‬

‫)‪ -1‬اﻟﻒ ( دو وارده در ﺟﺪول ﻧﻤﺎد ﺑﺮاي ‪ x‬اﻳﺠﺎد ﺧﻮاﻫﺪ ﺷﺪ ‪ .‬ﻳﻜﻲ ﺑﺎ ‪ x‬ﺑﻪ ﻋﻨﻮان ﻳﻚ ﻋﺪد‬

‫ﺻﺤﻴﺢ و دﻳﮕﺮي ﺑﻪ ﻋﻨﻮان ﻳﻚ ﺳﺎﺧﺘﺎر ‪.‬‬

‫ﺻﻔﺎت در ﭘﺎﺳﺦ ﺑﻪ اﻋﻼن ﻫﺎ ﻛﻪ ﻣﻤﻜﻦ اﺳﺖ ﺿﻤﻨﻲ ﺑﺎﺷﻨﺪ ‪ ،‬ﺑﻪ ﺟﺪول وارد ﻣﻲ ﺷﻮﻧﺪ ‪.‬‬

‫ﺑﺮﭼﺴﺐ ﻫﺎ ‪ ،‬اﻏﻠﺐ ﺷﻨﺎﺳﻪ ﻫﺎﻳﻲ ﻫﺴﺘﻨﺪ ﻛﻪ ﺑﻪ دﻧﺒﺎل آﻧﻬﺎ دو ﻧﻘﻄﻪ )‪ (:‬ﻗﺮار ﻣﻲ ﮔﻴﺮد ‪ ،‬ﺑﻨﺎﺑﺮاﻳﻦ‬

‫ﭘﺲ از ﺗﺸﺨﻴﺺ ﭼﻨﻴﻦ ﺷﻨﺎﺳﻪ اي ﺑﺎﻳﺪ اﻃﻼع ﻣﺮﺑﻮط ﺑﻪ ﺑﺮ ﭼﺴﺐ ﺑﻮدن آن را در وارده‬

‫ﻣﺮﺑﻮط درﺟﺪول ﻧﻤﺎد وارد ﻛﺮد ‪ .‬ﺑﻄﻮر ﻣﺸﺎﺑﻪ ‪ ،‬ﺳﺎﺧﺘﺎر ﻧﺤﻮي اﻋﻼن روﻳﻪ ﻫﺎ ﻣﺸﺨﺺ ﻣﻲ‬

‫ﻧﻤﺎﻳﺪ ﻛﻪ ﺑﻌﻀﻲ ﺷﻨﺎﺳﻪ ﻫﺎ از ﻧﻮع ﭘﺎراﻣﺘﺮﻫﺎي رﺳﻤﻲ ﻫﺴﺘﻨﺪ ‪.‬‬

‫‪٩٥‬‬
‫ﻛﺎراﻛﺘﺮﻫﺎي ﻣﻮﺟﻮد در ﻳﻚ ﻧﺎم‬

‫ﺑﻴﻦ ﻧﺸﺎﻧﻪ ‪ id‬ﺑﺮاي ﻳﻚ ﺷﻨﺎﺳﻪ ﻳﺎ ﻧﺎم ‪ ،‬ﻟﻐﺖ ﺷﺎﻣﻞ رﺷﺘﻪ ﻛﺎراﻛﺘﺮﻫﺎي ﺗﺸﻜﻴﻞ دﻫﻨﺪه ﻧﺎم و‬

‫ﺻﻔﺎت اﻳﻦ ﻧﺎم ﺗﻔﺎوت وﺟﻮد دارد ‪ .‬ﺑﻄﻮر ﮔﺴﺘﺮده اي ﻣﻤﻜﻦ اﺳﺖ ﺑﺎ رﺷﺘﻪ ﻫﺎﻳﻲ از‬

‫ﻛﺎراﻛﺘﺮﻫﺎ ﻛﺎر ﻧﺸﻮد ‪ ،‬ﺑﻨﺎﺑﺮاﻳﻦ ﻛﺎﻣﭙﺎﻳﻠﺮ ﻧﻤﺎﻳﺸﻲ ﺑﺎ ﻃﻮل ﺛﺎﺑﺖ از آن ﻧﺎم را ﺑﺠﺎي ﻟﻐﺖ آن‬

‫اﺳﺘﻔﺎده ﻣﻲ ﻧﻤﺎﻳﺪ ‪ .‬اﻳﻦ ﻟﻐﺖ زﻣﺎﻧﻲ ﻣﻮرد ﻧﻴﺎزاﺳﺖ ﻛﻪ وارده ﺟﺪول ﻧﻤﺎد ﺑﺮاي اوﻟﻴﻦ ﺑﺎر وارد‬

‫ﻣﻲ ﺷﻮد ‪ .‬ﻫﻤﭽﻨﻴﻦ ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﺑﺮاي ﻟﻐﺖ ﻳﺎﻓﺖ ﺷﺪه در ورودي ﺑﻪ ﺟﺪول ﻣﺮاﺟﻌﻪ ﻣﻲ ﺷﻮد‬

‫ﺗﺎ ﻣﺸﺨﺺ ﺷﻮد آﻳﺎ اﻳﻦ ﻧﺎم ﻗﺒﻼ وﺟﻮد داﺷﺘﻪ اﺳﺖ ﻳﺎ ﺧﻴﺮ‪ .‬ﻧﻤﺎﻳﺸﻲ ﻣﺮﺳﻮم از ﻧﺎم ‪ ،‬اﺷﺎره‬

‫ﮔﺮي اﺳﺖ ﺑﻪ وارد ه ﺟﺪول ﻧﻤﺎد ﻣﺮﺑﻮط ﺑﻪ آن ‪ .‬اﮔﺮ ﺣﺪ ﺑﺎﻻي ﻣﺘﻮﺳﻄﻲ ﺑﺮاي ﻃﻮل ﻧﺎم‬

‫وﺟﻮد داﺷﺘﻪ ﺑﺎﺷﺪ ‪ ،‬ﻛﺎراﻛﺘﺮﻫﺎي ﻣﻮﺟﻮد در ﻧﺎم ﻣﻲ ﺗﻮاﻧﻨﺪ در وارده ﺟﺪول ﻧﻤﺎد ﻣﺸﺎﺑﻪ زﻳﺮ‬

‫ذﺧﻴﺮه ﺷﻮﻧﺪ ‪:‬‬

‫ﺻﻔﺎت‬
‫ﻧﺎم‬

‫‪s‬‬ ‫‪o‬‬ ‫‪r‬‬ ‫‪t‬‬

‫‪a‬‬

‫‪r‬‬ ‫‪e‬‬ ‫‪a‬‬ ‫‪d‬‬ ‫‪a‬‬ ‫‪r‬‬ ‫‪r‬‬ ‫‪a‬‬ ‫‪y‬‬

‫‪i‬‬

‫‪٩٦‬‬
‫اﮔﺮ ﻣﺤﺪودﻳﺘﻲ ﺑﺮاي ﻃﻮل ﻧﺎم وﺟﻮد ﻧﺪاﺷﺘﻪ ﺑﺎﺷﺪ ﻳﺎ اﮔﺮ اﻳﻦ ﺣﺪ ﺑﻨﺪرت ﻗﺎﺑﻞ ﺣﺼﻮل ﺑﺎﺷﺪ‬

‫ﻃﺮح ﻏﻴﺮ ﻣﺴﺘﻘﻴﻢ ﺷﻜﻞ زﻳﺮ ﻣﻲ ﺗﻮاﻧﺪ اﺳﺘﻔﺎده ﺷﻮد ‪:‬‬


‫ﻧﺎم‬ ‫ﺻﻔﺎت‬

‫‪s o r t eos a eos r e a d a r r a y eos i eos‬‬

‫ﺑﺠﺎي ﺗﺨﺼﻴﺺ ﺣﺪاﻛﺜﺮ ﻓﻀﺎي ﻣﻤﻜﻦ ﺑﺮاي ﻧﮕﻬﺪاري ﻳﻚ ﻟﻐﺖ در ﻫﺮ وارده ﺟﺪول ﻧﻤﺎد ‪،‬‬

‫اﮔﺮ ﻓﻘﻂ ﻓﻀﺎ ﺑﺮاي اﺷﺎره ﮔﺮي در وارد ه ﺟﺪول ﻧﻤﺎد اﺧﺘﺼﺎص داده ﺷﻮد ‪ ،‬ﻣﻲ ﺗﻮاﻧﺪ از‬

‫ﻓﻀﺎ ﺑﻄﻮر ﻛﺎرآﻣﺪي اﺳﺘﻔﺎده ﻧﻤﺎﻳﺪ ‪ .‬در رﻛﻮد ﻳﻚ ﻧﺎم ‪ ،‬اﺷﺎره ﮔﺮي ﺑﻪ آراﻳﻪ اي از‬

‫ﻛﺎراﻛﺘﺮﻫﺎ )ﺟﺪول رﺷﺘﻪ ( ﻗﺮار داده ﻣﻲ ﺷﻮد ﻛﻪ ﻣﻮﻗﻌﻴﺖ اوﻟﻴﻦ ﻛﺎراﻛﺘﺮ آن ﻟﻐﺖ را‬

‫ﻣﺸﺨﺺ ﻣﻲ ﻧﻤﺎﻳﺪ ‪ .‬ﻃﺮح ﻏﻴﺮﻣﺴﺘﻘﻴﻢ اﺟﺎزه ﻣﻲ دﻫﺪ اﻧﺪازه ﻓﻴﻠﺪ ﻧﺎم وارده ﺟﺪول ﻧﻤﺎد ﺛﺎﺑﺖ‬

‫ﺑﺎﻗﻲ ﺑﻤﺎﻧﺪ ‪.‬‬

‫‪٩٧‬‬
‫ﺑﺮاي ﻫﺮ ﻧﺎم ‪ ،‬ﻟﻐﺖ ﻛﺎﻣﻞ ﺗﺸﻜﻴﻞ دﻫﻨﺪه آن ﺑﺎﻳﺪ ذﺧﻴﺮه ﺷﻮد ﺗﺎ اﻃﻤﻴﻨﺎن ﺣﺎﺻﻞ ﮔﺮدد ﻛﻪ‬

‫ﺗﻤﺎم ﻛﺎرﺑﺮدﻫﺎي آن ﻧﺎم ﺑﻪ ﻳﻚ رﻛﻮرد از ﺟﺪول ﻧﻤﺎد ﻣﺮﺗﺒﻂ ﻣﻲ ﺷﻮﻧﺪ ‪ .‬ﺑﻪ ﻫﺮ ﺣﺎل‬

‫رﺧﺪادﻫﺎي ﻟﻐﺖ ﻣﺸﺎﺑﻪ ﻛﻪ در ﻣﺤﺪوده ﻫﺎي اﻋﻼن ﻫﺎي ﻣﺘﻔﺎوت ﻗﺮار دارﻧﺪ ﺑﺎﻳﺪ ﻗﺎﺑﻞ‬

‫ﺗﻔﻜﻴﻚ ﺑﺎﺷﻨﺪ ‪.‬‬

‫اﻃﻼﻋﺎت ﺗﺨﺼﻴﺺ ﺣﺎﻓﻈﻪ‬

‫اﻃﻼﻋﺎت ﻣﺮﺑﻮط ﺑﻪ ﻣﻜﺎن ﻫﺎي ﺣﺎﻓﻈﻪ ﻛﻪ در زﻣﺎن اﺟﺮا ﺑﻪ ﻧﺎم ﻫﺎ اﺧﺘﺼﺎص ﺧﻮاﻫﺪ ﻳﺎﻓﺖ ‪،‬‬

‫در ﺟﺪول ﻧﻤﺎد ﻧﮕﻬﺪاري ﻣﻲ ﺷﻮد ‪ .‬اﺑﺘﺪا ﻧﺎم ﻫﺎﻳﻲ را ﺑﺎ ﺣﺎﻓﻈﻪ اﻳﺴﺘﺎ در ﻧﻈﺮ ﺑﮕﻴﺮﻳﺪ ‪ .‬اﮔﺮ ﻛﺪ‬

‫ﻫﺪف زﺑﺎن اﺳﻤﺒﻠﻲ ﺑﺎﺷﺪ ‪ ،‬اﺳﻤﺒﻠﺮ اﻳﻦ اﺟﺎزه را ﺧﻮاﻫﺪ داﺷﺖ ﻛﻪ در ﻣﻮرد ﻣﻜﺎن ﻫﺎي‬

‫ﺣﺎﻓﻈﻪ ﺑﺮاي ﻧﺎم ﻫﺎي ﮔﻮﻧﺎﮔﻮن ‪ ،‬ﻣﺮ اﻗﺒﺖ ﻻزم را اﻧﺠﺎم دﻫﺪ ‪ .‬ﺗﻤﺎم آن ﭼﻴﺰي ﻛﻪ ﺑﺎﻳﺪ ﭘﺲ‬

‫از ﺗﻮﻟﻴﺪ ﻛﺪ اﺳﻤﺒﻠﻲ ﺑﺮاي ﺑﺮﻧﺎﻣﻪ اﻧﺠﺎم ﮔﻴﺮد ‪ ،‬ﭘﻮﻳﺶ ﺟﺪول ﻧﻤﺎد و ﺗﻮﻟﻴﺪ ﺗﻌﺎرﻳﻒ داده ﻫﺎي‬

‫زﺑﺎن اﺳﻤﺒﻠﻲ ﻧﺎﻣﻲ ﻣﻲ ﺑﺎﺷﺪ ﻛﻪ ﺑﺎﻳﺪ ﺑﻪ ﺑﺮﻧﺎﻣﻪ اﺿﺎﻓﻪ ﺷﻮد ‪.‬‬

‫اﮔﺮ ﻛﺪ ﻣﺎﺷﻴﻦ را ﻛﺎﻣﭙﺎﻳﻠﺮ ﺑﺎﻳﺪ ﺗﻮﻟﻴﺪ ﻛﻨﺪ ﻣﻮﻗﻌﻴﺖ ﻫﺮ داده ﻣﻘﺼﻮد ﻧﺴﺒﺖ ﺑﻪ ﻳﻚ ﻣﺒﺪا ﺛﺎﺑﺖ‬

‫ﻣﺎﻧﻨﺪ اﺑﺘﺪاي ﻳﻚ رﻛﻮرد ﻓﻌﺎﻟﻴﺖ ‪ ،‬ﺑﺎﻳﺪ ﻣﺸﺨﺺ ﺷﻮد ‪ .‬ﺗﻮﺿﻴﺤﺎت ﻣﺸﺎﺑﻬﻲ در ﻣﻮرد ﻳﻚ‬

‫ﺑﻠﻮك ازداده ﻫﺎ ﻛﻪ ﺑﻪ ﻋﻨﻮان ﻳﻚ ﭘﻴﻤﺎﻧﻪ ﻣﺠﺰا از ﺑﺮﻧﺎﻣﻪ ﺑﺎر ﻣﻲ ﺷﻮد ‪ ،‬ﻣﻲ ﺗﻮاﻧﺪ ﺑﻜﺎر رود‪.‬‬

‫ﺑﺮاي ﻣﺜﺎل ﺑﻠﻮك ﻫﺎي ‪ COMMON‬در ﻓﺮﺗﻦ ﺑﻪ ﺻﻮرت ﻣﺠﺰا ﺑﺎر ﻣﻲ ﺷﻮﻧﺪ و ﻣﻮﻗﻌﻴﺖ ﻧﺎم‬

‫ﻫﺎ ﻧﺴﺒﺖ ﺑﻪ اﺑﺘﺪاي ﺑﻠﻮك‪ COMMON‬در ﻓﺮﺗﺮن ﺑﻪ ﺻﻮرت ﻣﺠﺰا ﺑﺎر ﻣﻲ ﺷﻮﻧﺪ و ﻣﻮﻗﻌﻴﺖ‬

‫ﻧﺎم ﻫﺎ ﻧﺴﺒﺖ ﺑﻪ اﺑﺘﺪاي ﺑﻠﻮك ‪COMMON‬ﻛﻪ درآن ﻗﺮار دارﻧﺪ ‪ ،‬ﺑﺎﻳﺪ ﻣﺸﺨﺺ ﺷﻮد ‪.‬‬

‫‪٩٨‬‬
‫در راﺑﻄﻪ ﺑﺎ ﻧﺎم ﻫﺎﻳﻲ ﻛﻪ ﺣﺎﻓﻈﻪ آﻧﻬﺎ در ﭘﺸﺘﻪ ﻳﺎ ﻛﭙﻪ اﺧﺘﺼﺎص ﻳﺎﻓﺘﻪ ‪ ،‬ﺑﻪ ﻫﻴﭻ وﺟﻪ ﻛﺎﻣﭙﺎﻳﻠﺮ‬

‫ﺣﺎﻓﻈﻪ را اﺧﺘﺼﺎص ﻧﻤﻲ دﻫﺪ ‪.‬‬

‫ﺳﺎﺧﺘﻤﺎن داده ﻟﻴﺴﺖ ﺑﺮاي ﺟﺪول ﻫﺎي ﻧﻤﺎد‬

‫ﺳﺎده ﺗﺮﻳﻦ ﺳﺎﺧﺘﻤﺎن داده ﺑﺮاي ﺳﺎﺧﺖ ﺟﺪول ﻧﻤﺎد ‪ ،‬ﻟﻴﺴﺖ ﺧﻄﻲ از رﻛﻮرد ﻫﺎ ﻣﻲ ﺑﺎﺷﺪ ‪،‬‬

‫ﻛﻪ در ﺷﻜﻞ زﻳﺮ ﻧﺸﺎن داده ﺷﺪه اﺳﺖ ‪:‬‬


‫‪id1‬‬

‫‪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‬اﻓﺰاﻳﺶ ﻣﻲ ﻳﺎﺑﺪ ‪ ،‬ﺑﻨﺎﺑﺮاﻳﻦ ﺗﻌﺎدﻟﻲ ﺑﻴﻦ‬

‫زﻣﺎن وﻓﻀﺎ اﻳﺠﺎد ﺷﺪه اﺳﺖ ‪ .‬ﻃﺮح اﺻﻠﻲ ﺟﺴﺘﺠﻮي در ﻫﻢ در ﺷﻜﻞ زﻳﺮ ﻧﻤﺎﻳﺶ داده ﺷﺪه‬

‫اﺳﺖ ‪:‬‬

‫آراﻳﻪ ﻟﻴﺴﺖ ﻋﻨﺎوﻳﻦ ﻛﻪ ﺑﺎ ﻣﻘﺪار درﻫﻢ‬


‫ﺳﺎزﻣﺎن دﻫﻲ ﺷﺪه اﺳﺖ‬

‫ﻟﻴﺴﺖ ﻋﻨﺎوﻳﻦ اﻳﺠﺎد ﻧﺸﺪه ﺑﺮاي‬


‫‪0‬‬
‫‪....‬‬
‫ﻧﺎم ﻫﺎي ﻧﺸﺎن داده ﺷﺪه‬
‫‪cp‬‬ ‫‪n‬‬
‫‪9‬‬

‫‪....‬‬
‫‪match‬‬
‫‪20‬‬

‫‪....‬‬
‫‪last‬‬ ‫‪action‬‬ ‫‪ws‬‬

‫‪32‬‬
‫‪....‬‬

‫‪210‬‬
‫اﻳﻦ ﺳﺎﺧﺘﻤﺎن داده داراي دو ﺑﺨﺶ اﺳﺖ ‪:‬‬

‫‪-1‬ﺟﺪول در ﻫﻢ ﺷﺎﻣﻞ آراﻳﻪ ﺛﺎﺑﺖ ﺑﺎ ‪ m‬اﺷﺎره ﮔﺮ ﺑﻪ وارده ﻫﺎي ﺟﺪول ‪.‬‬

‫‪ -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‬اﻧﺠﺎم ﻣﻲ ﺷﻮد ‪.‬‬

‫ﺗﻮاﺑﻊ در ﻫﻢ ﻛﻪ ﺑﻪ ﺗﻤﺎم ﻛﺎراﻛﺘﺮﻫﺎي رﺷﺘﻪ ﺗﻮﺟﻪ دارﻧﺪ ‪ ،‬ﻛﻤﺘﺮ از ﺗﻮاﺑﻌﻲ ﻛﻪ ﻓﻘﻂ ﺑﻪ ﺗﻌﺪادي‬

‫از ﻛﺎراﻛﺘﺮﻫﺎي اﻧﺘﻬﺎ ﻳﺎ وﺳﻂ رﺷﺘﻪ ﺗﻮﺟﻪ دارﻧﺪ دﭼﺎر ﺧﻄﺎ ﻣﻲ ﺷﻮﻧﺪ ‪.‬ﺑﺨﺎﻃﺮ آورﻳﺪ ورودي‬

‫ﺑﻪ ﻛﺎﻣﭙﺎﻳﻠﺮ ﻣﻤﻜﻦ اﺳﺖ ﺗﻮﺳﻂ ﻳﻚ ﺑﺮﻧﺎﻣﻪ اﻳﺠﺎد ﺷﻮد و ﺑﻨﺎﺑﺮاﻳﻦ ﺑﻪ ﻣﻨﻈﻮر ﺟﻠﻮﮔﻴﺮي از‬

‫ﺗﻨﺎﻗﺾ ﺑﺎ ﻧﺎم ﻫﺎي اﺳﺘﻔﺎده ﺷﺪه ﺗﻮﺳﻂ اﺷﺨﺎص ﻳﺎ ﺑﺮﻧﺎﻣﻪ دﻳﮕﺮ ﻣﻤﻜﻦ اﺳﺖ داراي ﺷﻜﻞ‬

‫ﺧﺎص ﺑﺎﺷﻨﺪ اﺷﺨﺎص ﺗﻤﺎﻳﻞ ﺑﻪ دﺳﺘﻪ ﺑﻨﺪي ﻧﺎم ﻫﺎ دارﻧﺪ ﻣﺎﻧﻨﺪ اﻧﺘﺨﺎب ﻫﺎي ‪:‬‬

‫‪ baz1,new baz,baz‬و ﻣﺎﻧﻨﺪ آن ‪.‬‬

‫ﻳﻚ روش ﺳﺎده ﺑﺮاي ﻣﺤﺎﺳﺒﻪ ‪ ،h‬ﺟﻤﻊ ﻧﻤﻮدن ﻣﻘﺎدﻳﺮ ﺻﺤﻴﺢ ﻛﺎراﻛﺘﺮﻫﺎي رﺷﺘﻪ اﺳﺖ ‪ .‬اﻳﺪه‬

‫ﺑﻬﺘﺮ ‪ ،‬ﺿﺮب ﻣﻘﺪار ﻗﺒﻠﻲ ‪ h‬ﺑﺎ ﺛﺎﺑﺖ ‪ a‬ﻗﺒﻞ از ﺟﻤﻊ ﺑﺎ ﻛﺎراﻛﺘﺮ ﺑﻌﺪي اﺳﺖ ‪ .‬ﻳﻌﻨﻲ‬

‫‪ hi=ahi-l+ci , h0=0‬ﺑﺮاي ‪ h = hk ,1 ≤ i ≤ k‬ﻛﻪ ‪ k‬ﻃﻮل رﺷﺘﻪ اﺳﺖ ‪) .‬ﺑﺨﺎﻃﺮ آورﻳﺪ ‪ ،‬ﻣﻘﺪار‬

‫درﻫﻢ ﻛﻪ ﺷﻤﺎره ﻟﻴﺴﺖ را ﻣﺸﺨﺺ ﻣﻲ ﻧﻤﺎﻳﺪ ‪ h mod m‬اﺳﺖ ( ﺗﻨﻬﺎ ﺟﻤﻊ ﻛﺎراﻛﺘﺮﻫﺎ ﺑﺎ‬

‫ﻳﻜﺪﻳﮕﺮ ‪ ،‬ﺣﺎﻟﺘﻲ اﺳﺖ ﻛﻪ ‪ a=1‬ﻣﻲ ﺑﺎﺷﺪ ‪ .‬ﻳﻚ اﺳﺘﺮاﺗﮋي ﻣﺸﺎﺑﻪ اﻳﻦ اﺳﺖ ﻛﻪ ‪ ci‬ﻫﺎ ﺑﺠﺎي‬

‫ﺟﻤﻊ ﺷﺪن ﺑﺎ ﻣﻘﺪار ‪ exclusive-or ، a hi-1‬ﺷﻮﻧﺪ‪.‬‬

‫ﺑﺮاي اﻋﺪاد ﺻﺤﻴﺢ ‪ 32‬ﺑﻴﺘﻲ ‪ ،‬اﮔﺮ ‪ a = 65599‬ﺑﺎﺷﺪ ‪،‬ﻛﻪ ﻳﻚ ﻋﺪد اول ﻧﺰدﻳﻚ ‪ 216‬اﺳﺖ‬

‫در اﻳﻦ ﺻﻮرت ﺑﺰودي در ﻣﺤﺎﺳﺒﻪ ‪ a hi-l‬ﺳﺮ رﻳﺰ رخ ﺧﻮاﻫﺪ داد ‪ .‬ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﻳﻨﻜﻪ ‪ a‬ﻋﺪد‬

‫اول اﺳﺖ ‪ ،‬ﺻﺮﻓﻨﻈﺮ ﻛﺮدن از ﺳﺮرﻳﺰﻫﺎ و ﺣﻔﻆ ‪ 32‬ﺑﻴﺖ ﻣﺮﺗﺒﻪ ﭘﺎﻳﻴﻦ ﺑﻪ ﻧﻈﺮ ﻣﻨﺎﺳﺐ اﺳﺖ ‪.‬‬

‫‪١٠٥‬‬
‫در ﻳﻚ ﺳﺮي آزﻣﺎﻳﺶ ‪ ،‬ﺗﺎﺑﻊ درﻫﻢ ‪ hashpjw‬در ﺷﻜﻞ زﻳﺮ ﺑﺮاي ﻛﺎﻣﭙﺎﻳﻠﺮ ‪ C‬از‬

‫‪ P.J.Weinberger‬ﺑﺎ ﺗﻤﺎم اﻧﺪازه ﻫﺎ ﺟﺪول اﻣﺘﺤﺎن ﺷﺪه ﺑﻪ ﺷﻜﻞ ﻣﻨﺎﺳﺒﻲ ﻋﻤﻞ ﻣﻲ ﻧﻤﺎﻳﺪ‪.‬‬

‫‪(1) #define PRIME 211‬‬


‫‘ ‪(2) #define Eos ’ \0‬‬

‫)‪(3) int hashpjw (s‬‬


‫;‪(4) char *.s‬‬
‫{ )‪(5‬‬
‫)‪(6‬‬ ‫;‪char *p‬‬
‫)‪(7‬‬ ‫;‪unsigned h = 0 , g‬‬
‫)‪(8‬‬ ‫{ ) ‪for ( p = s; *p != Eos; p =p+1‬‬
‫)‪(9‬‬ ‫;)‪h = ( h << 4 ) + (*p‬‬
‫)‪(10‬‬ ‫{ ) ‪if ( g= h & 0xf0000000‬‬
‫)‪(11‬‬ ‫;) ‪h = h ^ ( g >> 24‬‬
‫)‪(12‬‬ ‫;‪h = h ^ g‬‬
‫)‪(13‬‬ ‫}‬
‫)‪(14‬‬ ‫}‬
‫)‪(15‬‬ ‫; ‪return h % PRIME‬‬
‫} )‪(16‬‬
‫اﻳﻦ اﻧﺪازه ﻫﺎ ﺷﺎﻣﻞ اوﻟﻴﻦ اﻋﺪاد اول ﺑﺰرﮔﺘﺮ از ‪100‬و‪200‬و‪.....‬و‪ 1500‬ﻣﻲ ﺑﺎﺷﻨﺪ ‪ .‬دوﻣﻴﻦ‬

‫ﺗﺎﺑﻊ ﻧﺰدﻳﻚ ‪ ،‬ﺗﺎﺑﻌﻲ ﺑﻮد ﻛﻪ ‪ h‬را ﺑﺎ ﺿﺮب ﻣﻘﺪار ﻗﺒﻠﻲ آن در ‪ 65599‬و ﺻﺮف ﻧﻈﺮ از ﺳﺮ‬

‫رﻳﺰ و ﺟﻤﻊ آن ﺑﺎ ﻛﺎراﻛﺘﺮ ﺑﻌﺪي ﻣﺤﺎﺳﺒﻪ ﻧﻤﻮده ﺗﺎﺑﻊ ‪ hashpjw‬ﺑﺎ ﺷﺮوع از ‪ h=0‬ﻣﺤﺎﺳﺒﻪ ﻣﻲ‬

‫ﺷﻮد‪ .‬ﺑﺮاي ﻫﺮ ﻛﺎراﻛﺘﺮ ‪ c‬ﺑﻴﺖ ﻫﺎي ‪ h‬ﺑﻪ اﻧﺪازه ‪ 4‬ﻣﻜﺎن ﺑﻪ ﭼﭗ اﻧﺘﻘﺎل ﻣﻲ ﻳﺎﺑﻨﺪ و ﺑﺎ ‪ c‬ﺟﻤﻊ‬

‫ﻣﻲ ﺷﻮﻧﺪ ‪ .‬اﮔﺮ ﻫﺮ ﻳﻚ از ‪ 4‬ﺑﻴﺖ ﻣﺮﺗﺒﻪ ﺑﺎﻻي ‪ (1) h‬ﺑﺎﺷﻨﺪ اﻳﻦ ‪ 4‬ﺑﻴﺖ ‪ 24 ،‬ﻣﻜﺎن ﺑﻪ راﺳﺖ‬

‫‪١٠٦‬‬
‫اﻧﺘﻘﺎل داده ﻣﻲ ﺷﻮد و ﺑﺎ ‪ exclusive-or, h‬ﻣﻲ ﮔﺮدﻧﺪ و ﻫﺮ ﻳﻚ از ﭼﻬﺎر ﺑﻴﺖ ﻣﺮ ﺗﺒﻪ ﺑﺎﻻ‬

‫ﻛﻪ ‪ 1‬ﺑﻮده ‪ ،‬ﺑﻪ ﺻﻔﺮ ﺗﺒﺪﻳﻞ ﻣﻲ ﺷﻮد‪.‬‬

‫ﻣﺜﺎل ‪ -1‬اﻟﻒ ﺑﻪ ﻣﻨﻈﻮر رﺳﻴﺪن ﺑﻪ ﺑﻬﺘﺮﻳﻦ ﻧﺘﺎﻳﺞ ‪ ،‬اﻧﺪازه ﺟﺪول درﻫﻢ و ورودي ﻣﻮرد‬

‫اﻧﺘﻈﺎر در زﻣﺎن ﻃﺮاﺣﻲ ﺗﺎﺑﻊ در ﻫﻢ ﺑﺎﻳﺪ ﻣﻮرد ﺗﻮﺟﻪ ﻗﺮار ﮔﻴﺮد ‪ .‬ﺑﺮاي ﻣﺜﺎل ﻣﻄﻠﻮب اﺳﺖ ﻛﻪ‬

‫ﻣﻘﺎدﻳﺮ در ﻫﻢ ﺑﺮاي ﻧﺎم ﻫﺎﻳﻲ ﻛﻪ ﺑﻄﻮر ﻣﻜﺮر در زﺑﺎن رخ ﻣﻲ دﻫﻨﺪ ﻣﺘﻔﺎوت ﺑﺎﺷﻨﺪ اﮔﺮ‬

‫ﻛﻠﻤﺎت ﻛﻠﻴﺪي ﻧﻴﺰ ﺑﻪ ﺟﺪول ﻧﻤﺎد وارد ﺷﻮﻧﺪ ‪ ،‬ﻛﻠﻤﺎت ﻛﻠﻴﺪي ﻧﻴﺰ در ﮔﺮوه ﻧﺎم ﻫﺎﻳﻲ ﻫﺴﺘﻨﺪ‬

‫ﻛﻪ ﺑﻄﻮر ﻣﻜﺮر اﺳﺘﻔﺎده ﻣﻲ ﺷﻮﻧﺪ ‪ .‬اﮔﺮ ﭼﻪ در ﻳﻚ ﻧﻤﻮﻧﻪ از ﺑﺮﻧﺎﻣﻪ ﻫﺎي ‪ c‬ﻧﺎم ‪ i‬ﺗﺎ ﺑﻴﺶ از ﺳﻪ‬

‫ﺑﺎر ‪ ،‬ﻣﺸﺎﺑﻪ ‪ while‬اﺳﺘﻔﺎده ﺷﺪه اﺳﺖ ‪.‬‬

‫ﻳﻚ راه اﻣﺘﺤﺎن ﻧﻤﻮدن ﺗﺎﺑﻊ درﻫﻢ ﺗﻌﻴﻴﻦ ﺗﻌﺪاد رﺷﺘﻪ ﻫﺎﻳﻲ اﺳﺖ ﻛﻪ در ﻟﻴﺴﺖ ﻣﺸﺎﺑﻪ ﻗﺮار ﻣﻲ‬

‫ﮔﻴﺮﻧﺪ ‪ .‬ﺑﺎ داﺷﺘﻦ ﻳﻚ ﻓﺎﻳﻞ ﺑﻪ ﻧﺎم ‪ F‬ﺷﺎﻣﻞ ‪ n‬رﺷﺘﻪ ‪ ،‬ﻓﺮض ﻛﻨﻴﺪ ﺗﻌﺪاد ‪ bj‬رﺷﺘﻪ در ﻟﻴﺴﺖ ‪j‬‬

‫ﻳﻚ اﻧﺪازه ﮔﻴﺮي از ﻣﻴﺰان ﻳﻜﻨﻮاﺧﺘﻲ رﺷﺘﻪ ﻫﺎﻳﻲ ﻛﻪ در‬ ‫‪0 ≤ j ≤ m −1‬‬ ‫ﻗﺮار ﮔﻴﺮﻧﺪ ‪،‬ﺑﺮاي‬

‫‪m-1‬‬ ‫ﻟﻴﺴﺖ ﻫﺎ ﺗﻮزﻳﻊ ﺷﺪه اﻧﺪ ﺑﺎ ﻣﺤﺎﺳﺒﻪ ‪:‬‬


‫‪bj(bj +1)/2‬‬
‫‪j=0‬‬

‫ﺑﺪﺳﺖ ﻣﻲ آﻳﺪ ‪ .‬ﻳﻚ ﺑﺎزﺑﻴﻨﻲ اﺳﺘﻘﺮاﻳﻲ ﺑﺮاي اﻳﻦ ﻋﺒﺎرت اﻳﻦ اﺳﺖ ﻛﻪ ﺑﺮاي ﻳﺎﻓﺘﻦ اوﻟﻴﻦ‬

‫وارده در ﻟﻴﺴﺖ ‪ j‬ﻧﻴﺎز ﺑﺮرﺳﻲ ﻳﻚ ﻋﻨﺼﺮ ازﻟﻴﺴﺖ ﻣﻲ ﺑﺎﺷﺪ ‪ ،‬ﺑﺮاي ﻳﺎﻓﺘﻦ دوﻣﻴﻦ ﻋﻨﺼﺮ ‪ ،‬دو‬

‫ﻋﻀﻮ ﺑﺎﻳﺪ ﺑﺮرﺳﻲ ﺷﻮد ‪ ،‬و ﺑﻪ ﻫﻤﻴﻦ ﺗﺮﺗﻴﺐ ﺗﺎ ﺑﺮاي آﺧﺮﻳﻦ ﻋﻨﺼﺮ ﺑﻪ ‪ bj‬ﺑﺮرﺳﻲ ﻧﻴﺎز اﺳﺖ ‪.‬‬

‫ﻣﺠﻤﻮع ‪1‬و‪2‬و‪ bj....‬ﺑﺮاﺑﺮ ﺑﺎ ‪ bj(bj+1)/2‬ﻣﻲ ﺑﺎﺷﺪ ‪.‬‬

‫‪١٠٧‬‬
‫ﻧﻤﺎﻳﺶ اﻃﻼﻋﺎت ﻣﺤﺪوده‬

‫وارده ﻫﺎي ﺟﺪول ﻧﻤﺎد ‪ ،‬ﺑﺮاي اﻋﻼن ﻧﺎم ﻫﺎ ﻣﻲ ﺑﺎﺷﻨﺪ ‪ .‬ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﺑﺮاي رﺧﺪادي از ﻧﺎم ‪،‬‬

‫در ﺟﺪول ﻧﻤﺎد ﺟﺴﺘﺠﻮ ﻣﻲ ﺷﻮد ‪ ،‬وارده ﻣﺮﺑﻮط ﺑﻪ اﻋﻼن ﻣﻨﺎﺳﺐ آن ﻧﺎم ﺑﺎﻳﺪ ﺑﺮ ﮔﺮداﻧﺪه‬

‫ﺷﻮد ‪ .‬ﻗﻮاﻧﻴﻦ ﻣﺤﺪوده در زﺑﺎن ﻣﺒﺪا ﻣﺸﺨﺺ ﻣﻲ ﻧﻤﺎﻳﻨﺪ ﻛﻪ ﻛﺪام اﻋﻼن ﻣﻨﺎﺳﺐ اﺳﺖ ‪.‬‬

‫ﻳﻚ روش ﺳﺎده اﺳﺘﻔﺎده از ﺟﺪول ﻧﻤﺎد ﻣﺠﺰا ﺑﺮاي ﻫﺮ ﻣﺤﺪوده اﺳﺖ ‪ .‬در ﻧﺘﻴﺠﻪ ﺟﺪول ﻧﻤﺎد‬

‫ﺑﺮاي ﻳﻚ روﻳﻪ ﻳﺎ ﻣﺤﺪوده ﻣﻌﺎدل رﻛﻮرد ﻓﻌﺎﻟﻴﺖ در زﻣﺎن ﻛﺎﻣﭙﺎﻳﻞ اﺳﺖ ‪ .‬اﻃﻼﻋﺎت ﻏﻴﺮ‬

‫ﻣﺤﻠﻲ ﻳﻚ روﻳﻪ ﺑﺎ اﺳﺘﻔﺎده از ﭘﻮﻳﺶ ﺟﺪول ﻫﺎي ﻧﻤﺎد ﻣﺮﺑﻮط ﺑﻪ روﻳﻪ ﻫﺎي ﻣﻮﺟﻮد در ﺣﻴﻄﻪ‬

‫ﻗﻮاﻧﻴﻦ ﻣﺤﺪوده زﺑﺎن ﻳﺎﻓﺖ ﻣﻲ ﺷﻮد ‪ .‬ﺑﻄﻮر ﻣﺸﺎﺑﻪ اﻃﻼﻋﺎت ﻣﺤﻠﻲ ﻳﻚ روﻳﻪ ﻣﻲ ﺗﻮاﻧﺪ ﺑﻪ‬

‫ﮔﺮوه ﻣﺮﺑﻮط ﺑﻪ آن روﻳﻪ در درﺧﺖ ﻧﺤﻮ ﻣﺮﺑﻮط ﺑﻪ آن ﺑﺮﻧﺎﻣﻪ اﻟﺤﺎق ﮔﺮدد ‪ .‬ﺑﺎ اﻳﻦ ﮔﺮاﻳﺶ ‪،‬‬

‫ﺟﺪول ﻧﻤﺎد در ﻧﻤﺎﻳﺶ ﻣﻴﺎﻧﻲ ورودي ﻣﺠﺘﻤﻊ ﻣﻲ ﺷﻮد ‪.‬‬

‫اﻛﺜﺮ ﻗﻮاﻧﻴﻦ ﻣﺤﺪوده ﻛﻪ ﺑﻪ ﺷﻜﻞ ﻧﺰدﻳﻜﻲ ﻣﺘﺪاﺧﻞ ﻫﺴﺘﻨﺪ ‪ ،‬ﻣﻲ ﺗﻮاﻧﻨﺪ ﺑﺎ اﻗﺘﺒﺎس از ﺳﺎﺧﺘﻤﺎن‬

‫داده ﻫﺎي اراﺋﻪ ﺷﺪه در اﻳﻦ ﺑﺨﺶ ﭘﻴﺎده ﺳﺎزي ﮔﺮدﻧﺪ ‪ .‬ﻧﺎم ﻫﺎي ﻣﺤﻠﻲ ﻫﺮ روﻳﻪ ﺑﺎ اﺧﺘﺼﺎص‬

‫ﻋﺪد ﻣﻨﺤﺼﺮ ﺑﻪ ﻓﺮدي ﺑﻪ ﻫﺮ روﻳﻪ ﻗﺎﺑﻞ ﭘﻴﮕﻴﺮي اﺳﺖ ‪ .‬اﮔﺮ زﺑﺎن داراي ﺳﺎﺧﺘﺎر ﺑﻠﻮﻛﻲ اﺳﺖ‬

‫ﺑﻠﻮك ﻫﺎ ﻧﻴﺰ ﺑﺎﻳﺪ ﺷﻤﺎره ﮔﺬاري ﺷﻮﻧﺪ ‪ .‬ﺷﻤﺎره ﻫﺮ روﻳﻪ ﻣﻲ ﺗﻮاﻧﺪ ﺑﻪ ﺻﻮرت ﻧﺤﻮﮔﺮا ﺑﺎ‬

‫اﺳﺘﻔﺎده از ﻗﻮاﻧﻴﻦ ﻣﻌﻨﺎﻳﻲ ﻛﻪ اﺑﺘﺪا و اﻧﺘﻬﺎي ﻫﺮ روﻳﻪ را ﻣﺸﺨﺺ ﻣﻲ ﻧﻤﺎﻳﻨﺪ ‪ ،‬ﻣﺤﺎﺳﺒﻪ ﮔﺮدد‪.‬‬

‫ﺷﻤﺎره روﻳﻪ ﺑﻪ ﻋﻨﻮان ﺑﺨﺸﻲ از ﺗﻤﺎم اﻃﻼﻋﺎت ﻣﺤﻠﻲ آن روﻳﻪ ﻗﺮار ﻣﻲ ﮔﻴﺮد ‪ ،‬ﻧﻤﺎﻳﺶ ﻧﺎم‬

‫ﻣﺤﻠﻲ در ﺟﺪول ﻧﻤﺎد زوﺟﻲ اﺳﺖ ﺷﺎﻣﻞ ﻧﺎم و ﺷﻤﺎره روﻳﻪ‪) .‬در ﺑﻌﻀﻲ از ﻣﻮارد ﻣﺎﻧﻨﺪ آن‬

‫‪١٠٨‬‬
‫ﻫﺎﻳﻲ ﻛﻪ در زﻳﺮ ﺗﻮﺻﻴﻒ ﺷﺪه اﻧﺪ ﻇﺎﻫﺮ ﺷﺪن ﺷﻤﺎره روﻳﻪ ﺑﻪ ﻃﻮر واﻗﻌﻲ ﻟﺰوﻣﻲ ﻧﺪارد ‪ ،‬زﻳﺮا‬

‫ﻣﻲ ﺗﻮاﻧﺪ ازﻣﻮﻗﻌﻴﺖ رﻛﻮرد در ﺟﺪول ﻧﻤﺎد ﻣﺸﺨﺺ ﺷﻮد (‪.‬‬

‫ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﺑﺮاي ﻧﺎم ﺗﺎزه ﭘﻮﻳﺶ ﺷﺪه ﺑﻪ ﺟﺪول ﻧﻤﺎد ﻣﺮاﺟﻌﻪ ﻣﻲ ﺷﻮد ‪ ،‬ﺗﻨﻬﺎ ﻫﻨﮕﺎﻣﻲ ﻛﻪ‬

‫اﻧﻄﺒﺎق رخ ﻣﻲ دﻫﺪ ﻛﻪ ﻛﺎراﻛﺘﺮﻫﺎي ﻧﺎم ﺑﻪ ﺻﻮرت ﻛﺎراﻛﺘﺮ ﺑﻪ ﻛﺎراﻛﺘﺮ ﺑﺎ وارده ﻣﻨﻄﺒﻖ‬

‫ﮔﺮدد و ﺷﻤﺎره ﻫﻤﺮاه آن در وارده ﺟﺪول ﻧﻤﺎد ﺑﺎ ﺷﻤﺎره روﻳﻪ اي ﻛﻪ در ﺣﺎل ﭘﺮدازش اﺳﺖ‬

‫ﺑﺮاﺑﺮ ﺑﺎﺷﺪ‪ .‬اﻛﺜﺮ ﻗﻮاﻧﻴﻦ ﻣﺤﺪوده اي ﻛﻪ ﺑﻄﻮر ﻧﺰدﻳﻜﻲ ﺑﺎ ﻫﻢ ﻣﺘﺪاﺧﻠﻨﺪ ﻣﻲ ﺗﻮاﻧﻨﺪ در ﻗﺎﻟﺐ‬

‫ﻋﺒﺎرت ﻫﺎﻳﻲ از اﻋﻤﺎل زﻳﺮ ﺑﺮ روي ﻳﻚ ﻧﺎم ﭘﻴﺎده ﺳﺎزي ﺷﻮﻧﺪ ‪:‬‬

‫‪ : Lookup‬ﻳﺎﻓﺘﻦ ﺟﺪﻳﺪﺗﺮﻳﻦ وارده اﻳﺠﺎد ﺷﺪه‬

‫‪ :Insert‬اﻳﺠﺎد ﻳﻚ وارده ﺟﺪﻳﺪ‬

‫‪ :Delete‬ﺣﺬف ﺟﺪﻳﺘﺮﻳﻦ وارده اﻳﺠﺎد ﺷﺪه ‪.‬‬

‫وارده ﻫﺎي ﺣﺬف ﺷﺪه ﺑﺎﻳﺪ ﻧﮕﻬﺪاري ﺷﻮﻧﺪ ‪ ،‬آﻧﻬﺎ ﻓﻘﻂ از ﺟﺪول ﻧﻤﺎد ﻓﻌﺎل ﺣﺬف ﻣﻲ‬

‫ﺷﻮﻧﺪ در ﻛﺎﻣﭙﺎﻳﻠﺮ ﺗﻚ ﮔﺬره ‪ ،‬اﻃﻼﻋﺎت ﺟﺪول ﻧﻤﺎد در ﻣﻮرد ﻳﻚ ﻣﺤﺪوده ﺷﺎﻣﻞ ﺑﺪﻧﻪ‬

‫روﻳﻪ ‪ ،‬ﭘﺲ از ﭘﺎﻳﺎن ﭘﺮدازش ﺑﺪﻧﻪ روﻳﻪ ﻣﻮرد ﻧﻴﺎز ﻧﻤﻲ ﺑﺎﺷﺪ ‪ .‬ﺑﻪ ﻫﺮ ﺣﺎل ﻣﻤﻜﻦ اﺳﺖ در‬

‫زﻣﺎن اﺟﺮا ﻣﻮرد ﻧﻴﺎز ﺑﺎﺷﺪ ‪ ،‬ﻣﺨﺼﻮﺻﺎً اﮔﺮ ﻳﻚ ﺳﻴﺴﺘﻢ ﺗﺸﺨﻴﺼﻲ زﻣﺎن اﺟﺮا ﭘﻴﺎده ﺳﺎزي‬

‫ﺷﻮد ‪ .‬در اﻳﻦ ﺣﺎﻟﺖ ‪ ،‬اﻃﻼﻋﺎت ﺟﺪول ﻧﻤﺎد ‪ ،‬ﺑﺎﻳﺪ ﺑﻪ ﻛﺪ ﺗﻮﻟﻴﺪ ﺷﺪه ﺑﺮاي اﺳﺘﻔﺎده اﻟﺤﺎق ﮔﺮ‬

‫ﻳﺎﺳﻴﺴﺘﻢ ﺗﺸﺨﻴﺼﻲ زﻣﺎن اﺟﺮا اﺿﺎﻓﻪ ﮔﺮدد ‪.‬‬

‫‪١٠٩‬‬
‫ﻫﺮ ﻳﻚ از ﺳﺎﺧﺘﻤﺎن داده ﻫﺎي ﺑﺤﺚ ﺷﺪه دراﻳﻦ ﺑﺨﺶ ‪ ،‬ﻟﻴﺴﺖ ﻫﺎ و ﺟﺪاول در ﻫﻢ ﻣﻲ‬

‫ﺗﻮاﻧﻨﺪ ﺑﻪ ﺷﻜﻠﻲ ﺑﻪ ﻛﺎر ﮔﺮﻓﺘﻪ ﺷﻮﻧﺪ ﻛﻪ اﻋﻤﺎل ﻓﻮق را ﺣﻤﺎﻳﺖ ﻧﻤﺎﻳﻨﺪ ‪.‬‬

‫ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﻟﻴﺴﺖ ﺧﻄﻲ ﺷﺎﻣﻞ آراﻳﻪ اي از رﻛﻮرد ﻫﺎ در ﻗﺴﻤﺖ ﻫﺎي ﻗﺒﻠﻲ اﻳﻦ ﺑﺨﺶ‬

‫ﺗﻮﺻﻴﻒ ﺷﺪ ‪ ،‬ﺑﻪ اﻳﻦ ﻧﻜﺘﻪ اﺷﺎره ﺷﺪه ﻛﻪ ﭼﮕﻮﻧﻪ ‪ Lookup‬ﻣﻲ ﺗﻮاﻧﺪ ﺑﺎ درج ﻛﺮدن وارده ﻫﺎ‬

‫در ﻳﻚ ﻃﺮف ﺑﻪ ﺷﻜﻠﻲ ﻛﻪ ﺗﺮﺗﻴﺐ وارده ﻫﺎ درآراﻳﻪ ﻣﺸﺎﺑﻪ ﺗﺮﺗﻴﺐ درج ﻛﺮدن وارده ﻫﺎ‬

‫ﺑﺎﺷﺪ ‪ ،‬ﭘﻴﺎده ﺳﺎزي ﺷﻮد ‪ .‬ﻳﻚ ﭘﻮﻳﺶ ﺑﺎ ﺷﺮوع از اﻧﺘﻬﺎ و اداﻣﻪ ﺑﻪ ﺳﻤﺖ اﺑﺘﺪاي آراﻳﻪ ‪،‬‬

‫ﺟﺪﻳﺘﺮﻳﻦ وارده را ﺑﺮاي آن ﻧﺎم ﻣﻲ ﻳﺎﺑﺪ‪ .‬اﻳﻦ وﺿﻌﻴﺖ ﺷﺒﻴﻪ ﻟﻴﺴﺖ ﭘﻴﻮﻧﺪي ﺷﻜﻞ زﻳﺮ‬

‫ﻣﻲ ﺑﺎﺷﺪ ‪:‬‬


‫‪front‬‬

‫‪a2‬‬ ‫‪a0‬‬
‫…‬ ‫…‬ ‫…‬

‫اﺷﺎره ﮔﺮ ‪ front‬ﺑﻪ ﺟﺪﻳﺘﺮﻳﻦ وارده اﺿﺎﻓﻪ ﺷﺪه ﺑﻪ ﻟﻴﺴﺖ اﺷﺎره ﻣﻲ ﻧﻤﺎﻳﻨﺪ ‪ .‬ﭘﻴﺎده ﺳﺎزي‬

‫‪ insert‬زﻣﺎن ﺛﺎﺑﺘﻲ ﻧﻴﺎز دارد زﻳﺮا وارده ﺟﺪﻳﺪ در ﺟﻠﻮي ﻟﻴﺴﺖ ﻗﺮار ﻣﻲ ﮔﻴﺮد ‪ .‬ﭘﻴﺎده ﺳﺎزي‬

‫‪ Lookup‬ﺑﺎ ﭘﻮﻳﺶ ﻟﻴﺴﺖ و ﺷﺮوع از وارده اﺷﺎره ﺷﺪه ﺗﻮﺳﻂ ‪ front‬و دﻧﺒﺎل ﻧﻤﻮدن اﺗﺼﺎل‬

‫ﻫﺎ ﺗﺎ زﻣﺎن ﻳﺎﻓﺘﻦ وارد ه ﻣﻮرد ﻧﻈﺮ ﻳﺎ ﻓﺮارﺳﻴﺪن اﻧﺘﻬﺎي ﻟﻴﺴﺖ ‪ ،‬اﻧﺠﺎم ﻣﻲ ﮔﻴﺮد ‪ .‬در ﺷﻜﻞ‬

‫ﻓﻮق وارده ﺑﺮاي ‪ a‬ﻛﻪ درﺑﻠﻮك ‪ B2‬اﻋﻼن ﺷﺪه اﺳﺖ ‪ ،‬ﻛﻪ ﺧﻮد در داﺧﻞ ﺑﻠﻮك ‪ B0‬ﻗﺮار‬

‫دارد ‪ ،‬از وارده ‪ a‬ﻛﻪ در ‪ B0‬اﻋﻼن ﺷﺪه ‪ ،‬ﺑﻪ اﺑﺘﺪاي ﻟﻴﺴﺖ ﻧﺰدﻳﻚ ﺗﺮ اﺳﺖ ‪.‬‬

‫‪١١٠‬‬
‫ﺑﺮاي ﻋﻤﻞ ‪ delete‬ﺗﻮﺟﻪ داﺷﺘﻪ ﺑﺎﺷﻴﺪ ﻛﻪ وارده ﻫﺎي ﻣﺮﺑﻮط ﺑﻪ اﻋﻼن ﻫﺎي روﻳﻪ اي ﻛﻪ در‬

‫ﻋﻤﻴﻖ ﺗﺮﻳﻦ روﻳﻪ داﺧﻠﻲ ﻗﺮار دارﻧﺪ ‪ ،‬در ﻧﺰدﻳﻚ ﺗﺮﻳﻦ ﻗﺴﻤﺖ ﺑﻪ اﺑﺘﺪاي ﻟﻴﺴﺖ ﻗﺮار ﻣﻲ‬

‫ﮔﻴﺮﻧﺪ‪ .‬ﺑﻨﺎﺑﺮاﻳﻦ ‪ ،‬ﻧﻴﺎزي ﺑﻪ ﻧﮕﻬﺪاري ﺷﻤﺎره روﻳﻪ ﺑﺎ ﻫﺮ وارده ﻧﻤﻲ ﺑﺎﺷﺪ ‪ .‬اﮔﺮ اوﻟﻴﻦ وارده‬

‫ﺑﺮاي ﻫﺮ روﻳﻪ ﻣﺸﺨﺺ ﺷﻮد ﺗﻤﺎم وارده ﻫﺎ ﺗﺎ اوﻟﻴﻦ وارده ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﭘﺮدازش ﻣﺤﺪوده اﻳﻦ‬

‫روﻳﻪ ﺧﺎﺗﻤﻪ ﻣﻲ ﻳﺎﺑﺪ ‪ ،‬ﻣﻲ ﺗﻮاﻧﺪ از ﺟﺪول ﻧﻤﺎد ﻓﻌﺎل ﺣﺬف ﮔﺮدد ‪ .‬ﺟﺪول در ﻫﻢ ﺷﺎﻣﻞ ‪m‬‬

‫ﻟﻴﺴﺖ ‪ ،‬ﺑﺎ اﺳﺘﻔﺎده از آراﻳﻪ دﺳﺘﻴﺎﺑﻲ ﻣﻲ ﺷﻮد ‪ .‬ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ اﻳﻨﻜﻪ ﻳﻚ ﻧﺎم ﻫﻤﻴﺸﻪ ﺑﺎ اﺳﺘﻔﺎده از‬

‫ﺗﺎﺑﻊ درﻫﻢ ﺑﻪ ﻟﻴﺴﺖ ﻣﻨﺘﻘﻞ ﻣﻲ ﺷﻮد ﻫﺮ ﻳﻚ از ﻓﻬﺮﺳﺖ ﻫﺎ ﻣﺸﺎﺑﻪ ﺷﻜﻞ ﻓﻮق ﻧﮕﻬﺪاري ﻣﻲ‬

‫ﺷﻮد ‪ .‬ﺑﻪ ﻫﺮ ﺣﺎل ﺑﺮاي ﭘﻴﺎده ﺳﺎزي ﻋﻤﻞ ‪ delete‬ﻧﻴﺎزي ﺑﻪ ﭘﻮﻳﺶ ﺗﻤﺎم ﺟﺪول در ﻫﻢ ﺑﺮاي‬

‫ﻳﺎﻓﺘﻦ ﻟﻴﺴﺘﻲ ﺷﺎﻣﻞ وارده ﻫﺎﻳﻲ ﻛﻪ ﺑﺎﻳﺪ ﺣﺬف ﺷﻮد ﻧﻤﻲ ﺑﺎﺷﺪ ‪ .‬روش زﻳﺮ ﻣﻲ ﺗﻮاﻧﺪ ﻣﻮرد‬

‫اﺳﺘﻔﺎده ﻗﺮار ﮔﻴﺮد ‪.‬ﻓﺮض ﻛﻨﻴﺪ ﻫﺮ وارده داراي دو اﺗﺼﺎل اﺳﺖ ‪:‬‬

‫‪ -1‬ﻳﻚ اﺗﺼﺎل در ﻫﻢ ﻛﻪ اﻳﻦ وارده را ﺑﻪ وارده ﻫﺎي دﻳﮕﺮي ﻛﻪ ﻧﺎم آﻧﻬﺎ ﺗﻮﺳﻂ ﺗﺎﺑﻊ در ﻫﻢ‬

‫ﺑﻪ ﻣﻘﺪار ﻣﺸﺎﺑﻪ ﺗﺒﺪﻳﻞ ﻣﻲ ﺷﻮد ﺑﻪ زﻧﺠﻴﺮ ﻣﺘﺼﻞ ﻣﻲ ﻧﻤﺎﻳﺪ ‪.‬‬

‫‪ -2‬ﻳﻚ اﺗﺼﺎل ﻣﺤﺪوده ﻛﻪ ﺗﻤﺎم وارده ﻫﺎﻳﻲ را ﻛﻪ درﻣﺤﺪوده ﻣﺸﺎﺑﻪ ﻗﺮار دارﻧﺪ ﺑﻪ ﺻﻮرت‬

‫زﻧﺠﻴﺮ ﺑﻪ ﻳﻜﺪﻳﮕﺮ ﻣﺘﺼﻞ ﻣﻲ ﻧﻤﺎﻳﺪ ‪.‬‬

‫اﮔﺮ اﺗﺼﺎل ﻣﺤﺪوده در زﻣﺎن ﺣﺬف ﻳﻚ وارده از ﺟﺪول در ﻫﻢ دﺳﺖ ﻧﺨﻮرده ﺑﺎﻗﻲ ﺑﻤﺎﻧﺪ ‪،‬‬

‫زﻧﺠﻴﺮي ﻛﻪ ﺑﺎ اﺗﺼﺎﻻت ﻣﺤﺪوده ﺗﺸﻜﻴﻞ ﺷﺪه اﺳﺖ ‪ ،‬ﺟﺪول ﻧﻤﺎد ﻣﺠﺰاﻳﻲ را ﺑﺮاي ﻣﺤﺪوده‬

‫ﻣﻮرد ﺑﺤﺚ )ﻏﻴﺮ ﻓﻌﺎل ( ﺗﺸﻜﻴﻞ ﻣﻲ دﻫﺪ ‪.‬‬

‫‪١١١‬‬
‫ﺣﺬف وارده از ﺟﺪول در ﻫﻢ ﺑﺎﻳﺪ ﺑﺎ دﻗﺖ اﻧﺠﺎم ﮔﻴﺮد ‪ ،‬زﻳﺮا ﺣﺬف ﻳﻚ وارده ﺑﺮ ﻣﻘﺪار‬

‫ﻗﺒﻠﻲ آن در ﻓﻬﺮﺳﺖ ﺗﺎﺛﻴﺮ دارد ‪ .‬ﺑﺨﺎﻃﺮ آورﻳﺪ ﻛﻪ ﺣﺬف وارده ﺷﻤﺎره ‪ i‬ﺑﺎ ﺑﺮﻗﺮاري ارﺗﺒﺎط‬

‫وارده ﺷﻤﺎره ‪ i-1‬ﺑﺎ وارده ﺷﻤﺎره ‪ i+1‬اﻧﺠﺎم ﻣﻲ ﮔﻴﺮد ﺑﻨﺎﺑﺮاﻳﻦ ﺗﻨﻬﺎ اﺳﺘﻔﺎده از اﺗﺼﺎﻻت‬

‫ﻣﺤﺪوده ﺑﺮاي ﻳﺎﻓﺘﻦ وارد ﺷﻤﺎره ‪ i‬ﻛﺎﻓﻲ ﻧﻴﺴﺖ ‪ .‬اﮔﺮ اﺗﺼﺎﻻت در ﻫﻢ ﻳﻚ ﻟﻴﺴﺖ ﭘﻴﻮﻧﺪي‬

‫ﺣﻠﻘﻮي را ﺗﺸﻴﻜﻞ دﻫﻨﺪ ﻛﻪ درآن آﺧﺮﻳﻦ وارده ﺑﻪ اوﻟﻴﻦ وارده اﺷﺎره ﻧﻤﺎﻳﺪ ‪ ،‬وارده ‪ i-1‬ام‬

‫ﻧﻴﺰ ﻣﻲ ﺗﻮاﻧﺪ ﻳﺎﻓﺖ ﺷﻮد ‪ .‬در ﻣﻘﺎﺑﻞ ﻣﻲ ﺗﻮان از ﭘﺸﺘﻪ ﺑﻪ ﻣﻨﻈﻮر ﭘﻲ ﮔﻴﺮي ﺗﻌﻴﻴﻦ ﻟﻴﺴﺖ ﻫﺎﻳﻲ‬

‫ﺷﺎﻣﻞ وارده ﻫﺎﻳﻲ ﻛﻪ ﺑﺎﻳﺪ ﺣﺬف ﺷﻮﻧﺪ اﺳﺘﻔﺎده ﻧﻤﻮد ‪ .‬ﻫﻨﮕﺎﻣﻲ ﻛﻪ روﻳﻪ ﺟﺪﻳﺪي ﭘﻮﻳﺶ ﻣﻲ‬

‫ﺷﻮد ‪ ،‬ﻳﻚ ﻋﻼﻣﺖ در ﭘﺸﺘﻪ ﻗﺮار ﻣﻲ ﮔﻴﺮد ‪ .‬در ﺑﺎﻻي ﻋﻼﻣﺖ ‪ ،‬ﺷﻤﺎره ﻟﻴﺴﺖ ﻫﺎﻳﻲ ﺷﺎﻣﻞ‬

‫وارده ﻫﺎﻳﻲ ﺑﺮاي ﻧﺎم ﻫﺎي اﻋﻼن ﺷﺪه در اﻳﻦ روﻳﻪ ﻗﺮار دارﻧﺪ ‪ .‬ﻫﻨﮕﺎﻣﻲ ﻛﻪ ﭘﺮدازش اﻳﻦ‬

‫روﻳﻪ ﺧﺎﺗﻤﻪ ﻣﻲ ﻳﺎﺑﺪ ‪ ،‬اﻋﺪاد ﻓﻬﺮﺳﺖ ﻣﻲ ﺗﻮاﻧﻨﺪ از ﭘﺸﺘﻪ ﺧﺎرج ﺷﻮﻧﺪ ﺗﺎ زﻣﺎﻧﻲ ﻛﻪ ﻋﻼﻣﺖ آن‬

‫روﻳﻪ ﻓﺮاﺑﺮﺳﺪ‪.‬‬

‫‪١١٢‬‬

You might also like