調度場算法

調度場算法（Shunting Yard Algorithm）是一個用於將中綴表達式轉換為後綴表達式的經典算法，由艾茲格·迪傑斯特拉引入，因其操作類似於火車編組場而得名。

**簡例**
算法示意圖，使用了3個空間。輸入用符號代替，如果輸入是一個數字則直接進輸出隊列，即圖中 b),d),f),h)。如果輸入是運算符，則壓入操作符堆棧，即圖中 c),e)，但是，如果輸入運算符的優先級低於或等於運算符棧頂的操作符優先級，則棧內元素進入輸出隊列，輸入操作符壓入運算符堆棧，即圖中 g)。 最後，運算符堆棧內元素入輸出隊列，算法結束。
輸入：3+4
#將3入輸出隊列（每當輸入一個數字時，直接進入輸出隊列）
#將+號壓入運算堆棧
#將4入輸出隊列
#輸入結束，將操作符堆棧中剩餘操作符入輸出隊列
#在本情況下只有+號
#輸出 3+4
通過這個例子可以看出兩條規則：- 當讀入一個數字時直接入輸出隊列- 當輸入結束後，運算符隊列中所有操作符入輸出隊列

**詳細的算法**
 當還有記號可以讀取時：- 讀取一個記號。- 如果這個記號表示一個數字，那麼將其添加到輸出隊列中。- 如果這個記號表示一個函數，那麼將其壓入棧當中。- 如果這個記號表示一個函數參數的分隔符（例如，一個半角逗號 , ）：
:* 從棧當中不斷地彈出操作符並且放入輸出隊列中去，直到棧頂部的元素為一個左括號為止。如果一直沒有遇到左括號，那麼要麼是分隔符放錯了位置，要麼是括號不匹配。- 如果這個記號表示一個操作符，記做o1，那麼：
:* 只要存在另一個記為o2的操作符位於棧的頂端，並且
:::: 如果o1是左結合性的並且它的運算符優先級要小於或者等於o2的優先級，或者
:::: 如果o1是右結合性的並且它的運算符優先級比o2的要低，那麼
::: 將o2從棧的頂端彈出並且放入輸出隊列中（循環直至以上條件不滿足為止）；
:* 然後，將o1壓入棧的頂端。- 如果這個記號是一個左括號，那麼就將其壓入棧當中。- 如果這個記號是一個右括號，那麼：
:* 從棧當中不斷地彈出操作符並且放入輸出隊列中，直到棧頂部的元素為左括號為止。
:* 將左括號從棧的頂端彈出，但並不放入輸出隊列中去。
:* 如果此時位於棧頂端的記號表示一個函數，那麼將其彈出並放入輸出隊列中去。
:* 如果在找到一個左括號之前棧就已經彈出了所有元素，那麼就表示在表達式中存在不匹配的括號。- 當再沒有記號可以讀取時：- 如果此時在棧當中還有操作符：
:* 如果此時位於棧頂端的操作符是一個括號，那麼就表示在表達式中存在不匹配的括號。
:* 將操作符逐個彈出並放入輸出隊列中。- 退出算法。

**更詳細的例子**
中綴表示法 及 結果：3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3
*:逆波蘭表示法：3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3

**C++程序實現**
#include 
#include 
#define op_left_assoc(c) (c == '+' 
c == '-' 
c == '/' 
c == '*' 
c == '%')
#define is_operator(c)   (c == '+' 
c == '-' 
c == '/' 
c == '*' 
c == '!' 
c == '%' 
c == '=')
#define is_function(c)   (c >= 'A' && c = '0' && c = 'a' && c  0)
{
sc = stacksl - 1;
//掃描到左括號
//跳出輸出循環，此時左括號作為函數邊界判定，所以不出棧
if (sc == '(')
{
pe = true;
break;
}
else {
// 棧頂元素不是左括號
// 將棧頂元素依次出棧並放入輸出隊列
*outpos = sc;
++outpos;
sl--;
}
}
// 如果沒有遇到左括號，則有可能是符號放錯或者不匹配
if (!pe)
{
printf("Error: separator or parentheses mismatched\n");
return false;
}
}
// 如果輸入符號為運算符，然後:
else if (is_operator(c))
{
while (sl > 0)
{
sc = stacksl - 1;
// sc為其棧頂元素
// 如果c是左結合性的且它的優先級小於等於棧頂運算符sc的優先級
// 或者c是右結合性且它的優先級小於棧頂運算符sc的優先級
// 將棧頂元素sc出棧，否則sc進棧
if (is_operator(sc) && ((op_left_assoc(c) && (op_preced(c)  0)
{
sc = stacksl - 1;
if (sc == '(')
{
pe = true;
break;
}
else
{
*outpos = sc;
++outpos;
sl--;
}
}
// 如果沒有掃描到左括號，則有可能是符號放錯或者不匹配
if (!pe)
{
printf("Error: parentheses mismatched\n");
return false;
}
// 左括號出棧且不放入輸出隊列
sl--;
// 掃描完左括號後 
// 如果棧頂元素是函數運算符
// 則將其出棧並放入輸出隊列
if (sl > 0)
{
sc = stacksl - 1;
if (is_function(sc))
{
*outpos = sc;
++outpos;
sl--;
}
}
}
//未知運算符c
else printf("Unknown token %c\n", c);
}
++strpos;
}
// 當所有元素已經讀完
// 棧中還有剩餘運算符
while (sl > 0)
{
sc = stacksl - 1;
//如果剩餘括號，則符號放錯或者不匹配
if (sc == '(' 
sc == ')') 
{
printf("Error: parentheses mismatched\n");
return false;
}
//出棧並放入輸出隊列
*outpos = sc;
++outpos;
--sl;
}
*outpos = 0;//指針置零
return true;
}
bool execution_order(const char* input) 
{
printf("order: (arguments in reverse order)\n");
const char* strpos = input, * strend = input + strlen(input);
char c, res4;
unsigned int sl = 0, sc, stack32, rn = 0;
// While there are input tokens left
while (strpos  0) 
{
sc = stacksl - 1;
sl--;
if (nargs > 1) printf("%s, ", &sc);
else printf("%s)\n", &sc);
--nargs;
}
}
else 
{
if (nargs == 1) 
{
sc = stacksl - 1;
sl--;
printf("%c %s;\n", c, &sc);
}
else
{
sc = stacksl - 1;
sl--;
printf("%s %c ", &sc, c);
sc = stacksl - 1;
sl--;
printf("%s;\n", &sc);
}
}
// Push the returned results, if any, back onto the stack.
stacksl = *(unsigned int*)res;
++sl;
}
++strpos;
}
// If there is only one value in the stack
// That value is the result of the calculation.
if (sl == 1) 
{
sc = stacksl - 1;
sl--;
printf("%s is a result\n", &sc);
return true;
}
// If there are more values in the stack
// (Error) The user input has too many values.
return false;
}
int main()
{
// functions: A() B(a) C(a, b), D(a, b, c) ...
// identifiers: 0 1 2 3 ... and a b c d e ...
// operators: = - + / * % !
const char* input = "a = D(f - b * c + d, !e, g)";
char output128;
printf("input: %s\n", input);
if (shunting_yard(input, output)) 
{
printf("output: %s\n", output);
if (!execution_order(output))
printf("\nInvalid input\n");
}
return 0;
}

**參見**
 中綴表達式- 後綴表達式