我在这里的第一篇文章:)
我看到有很多关于调车场算法的问题,但我希望仍然有论坛成员有兴趣帮助我解决关于这个算法的另一个问题。
我确实搜索了其他帖子,看看我的答案是否已经得到了回答,我在其他论坛和互联网上做了一些研究:
http://stackoverflow.com/questions/16877546/modifying-the-shunting-yard-algorithm-c
http://www.computerhope.com/forum/index.php?topic=146535.0
http://en.wikipedia.org/wiki/Shunting-yard_algorithm
http://www.autoitscript.com/forum/topic/164627-shunting-yard-with-functions/
我的代码是用vb脚本编写的,因为我喜欢它的简单性,而且我不知道java或类c语言。。
我的问题:
目前,算法允许错误使用"("one_answers")"示例:函数((10,20,)30)是允许的,但这显然不是调用函数的正确方法。。
我也不确定我的代码写得是否正确,维基百科上的伪代码是我的参考,但它不是很清楚:(
我还计划用if-else语句和嵌套循环之类的东西来扩展它,因为主要目标是用类似vb的语言编写某种解释器作为学习项目:)
我的代码[编辑]:
SET PRECEDENCE = CREATEOBJECT("SCRIPTING.DICTIONARY")
WITH PRECEDENCE
.ADD "^",3
.ADD "*",2
.ADD "/",2
.ADD "%",2
.ADD "+",1
.ADD "-",1
.ADD "FUNCTION",0
.ADD "(",0
.ADD ",",0
.ADD ")",0
END WITH
'#############################################################################
tokenArray = split("FUNCTION ( ( A , B ) , C )")
msgbox SHUNTINGYARD(tokenArray)
'#############################################################################
FUNCTION SHUNTINGYARD(INPUT)
TOKEN_QUEUE = ARRAY()
TOKEN_STACK = ARRAY()
FOR TOKEN_NUMBER = 0 TO UBOUND(INPUT)
SELECT CASE INPUT(TOKEN_NUMBER)
CASE "("
CALL PUSH(INPUT(TOKEN_NUMBER), TOKEN_STACK)
CASE ")"
DO WHILE NOT( PRECEDENCE( PEEK(TOKEN_STACK) ) = 0 )
CALL PUSH(POP(TOKEN_STACK), TOKEN_QUEUE)
IF STACKISEMPTY(TOKEN_STACK) THEN CALL ERRORS("Can't find a matching ""("".", TRUE)
LOOP
IF PEEK(TOKEN_STACK) = "FUNCTION" THEN
DISCARD = POP(TOKEN_STACK)
CALL PUSH("@", TOKEN_QUEUE)
ELSE
DISCARD = POP(TOKEN_STACK)
END IF
CASE ","
DO WHILE NOT( PRECEDENCE( PEEK(TOKEN_STACK) ) = 0 )
CALL PUSH(POP(TOKEN_STACK), TOKEN_QUEUE)
IF STACKISEMPTY(TOKEN_STACK) THEN CALL ERRORS("Can't find a matching function ""("".", TRUE)
LOOP
CASE "+","-","*","/","^","%"
TOKEN_A = INPUT(TOKEN_NUMBER)
DO WHILE ISOPERATOR(PEEK(TOKEN_STACK))
TOKEN_B = PEEK(TOKEN_STACK)
IF (ASSOCIATIVITY(TOKEN_B) = "left" AND PRECEDENCE(TOKEN_A) = PRECEDENCE(TOKEN_B)) OR (PRECEDENCE(TOKEN_A) < PRECEDENCE(TOKEN_B)) THEN
CALL PUSH(POP(TOKEN_STACK), TOKEN_QUEUE)
ELSE
EXIT DO
END IF
LOOP
CALL PUSH(TOKEN_A, TOKEN_STACK)
CASE ELSE
CALL PUSH(INPUT(TOKEN_NUMBER), TOKEN_QUEUE)
END SELECT
NEXT
FOR ITEMCOUNT = 0 TO UBOUND(TOKEN_STACK)
IF PEEK(TOKEN_STACK) = "(" THEN CALL ERRORS("Can't find a matching "")"".", TRUE)'(
CALL PUSH(POP(TOKEN_STACK), TOKEN_QUEUE)
NEXT
SHUNTINGYARD = JOIN(TOKEN_QUEUE,"|")
END FUNCTION
'#############################################################################
FUNCTION ASSOCIATIVITY(ASSOC)
SELECT CASE LCASE(ASSOC)
CASE "^",""
ASSOCIATIVITY = "right"
CASE ELSE
ASSOCIATIVITY = "left"
END SELECT
END FUNCTION
FUNCTION ISOPERATOR(ITEM)
ISOPERATOR = LEN(ITEM) = 1 AND INSTR("+-*/%^",ITEM)
END FUNCTION
SUB PUSH(ITEM,BYREF STACK)
IF UBOUND(STACK) > -1 THEN
REDIM PRESERVE STACK(UBOUND(STACK) + 1)
STACK(UBOUND(STACK)) = ITEM
ELSE
STACK = ARRAY(ITEM)
END IF
END SUB
FUNCTION POP(BYREF STACK)
IF UBOUND(STACK) > -1 THEN
POP = STACK(UBOUND(STACK))
REDIM PRESERVE STACK(UBOUND(STACK) - 1)
END IF
END FUNCTION
FUNCTION STACKISEMPTY(STACK)
IF UBOUND(STACK) > -1 THEN
STACKISEMPTY = FALSE
ELSE
STACKISEMPTY = TRUE
END IF
END FUNCTION
FUNCTION PEEK(STACK)
IF UBOUND(STACK) > -1 THEN
PEEK = STACK(UBOUND(STACK))
END IF
END FUNCTION
您可以像对待其他运算符一样对待"FUN"、"("、")"、",并且可以推送到TOKEN_STACK上。(为了简洁起见,我把FUNCTION简称为FUN)。"FUN"、"("、")"、"的优先级低于"+",因此优先级表看起来像
^ 4
* / % 3
+ - 2
( ) , FUN 1
考虑分析1+FUN(2*3)时会发生什么
Remaining Input Stack Output
1+FUN(2*3)
+FUN(2*3) 1
FUN(2*3) + 1
(2*3) + FUN 1
2*3) + FUN ( 1
*3) + FUN ( 1 2
3) + FUN ( * 1 2
) + FUN ( * 1 2 3
+ FUN ( * ) 1 2 3 Note 1
+ FUN ( ) 1 2 3 * Note 2
+ 1 2 3 * FUN()
1 2 3 * FUN() +
输出标记"FUN()"代表函数求值。
注意1:当我们试图将")"推到堆栈上时,所有优先级较高的运算符都会从堆栈中取出并移到输出。
注2:当堆栈末尾的项是匹配的"("时,它将从堆栈中删除。有两种情况需要处理,括号的简单匹配如1+(2*3)或者如果堆栈上的"("之前有一个函数令牌。在这种情况下,从堆栈中弹出FUN,并在输出中添加一个函数评估令牌。
","将以类似于")"的方式处理,导致弹出优先级更高的运算符。但是它不会导致函数评估被输出。您需要一些方法来记录一个函数有多少个参数。
在我的代码中,我使用了一种基于递归的算法。解析算法可以递归调用,并提供一个停止标记。当遇到停止标记时,它退出递归。当遇到函数时,它会调用递归,等待","或")"。
我设法使用cscript命令行程序运行iut。我还用Wscript.Echo.添加了一些调试代码
查看TOKEN_STACK,您从未将FUNCTION令牌添加到堆栈中,因此在搜索时它不在那里。
正在添加CASE"函数"调用推送(输入(TOKEN_NUMBER),TOKEN_STACK)
似乎给出了正确的东西。虽然我不确定当你匹配第一个括号时会发生什么。它的输入也出现了严重的错误,比如"E+FUNCTION((A,B),C)+F"。
SET PRECEDENCE = CREATEOBJECT("SCRIPTING.DICTIONARY")
WITH PRECEDENCE
.ADD "^",3
.ADD "*",2
.ADD "/",2
.ADD "%",2
.ADD "+",1
.ADD "-",1
.ADD "FUNCTION",0
.ADD "(",0
.ADD ",",0
.ADD ")",0
END WITH
Wscript.Echo "Start"
'#############################################################################
tokenArray = split("FUNCTION ( ( A , B ) , C )")
'#tokenArray = split("A + B * C")
Wscript.Echo "Result " + SHUNTINGYARD(tokenArray)
'#############################################################################
FUNCTION SHUNTINGYARD(INPUT)
TOKEN_QUEUE = ARRAY()
TOKEN_STACK = ARRAY()
FOR TOKEN_NUMBER = 0 TO UBOUND(INPUT)
Wscript.Echo "Token " + INPUT(TOKEN_NUMBER)
Wscript.Echo "Stack " + JOIN(TOKEN_STACK,"|")
SELECT CASE INPUT(TOKEN_NUMBER)
CASE "("
CALL PUSH(INPUT(TOKEN_NUMBER), TOKEN_STACK)
CASE ")"
DO WHILE NOT( PRECEDENCE( PEEK(TOKEN_STACK) ) = 0 )
CALL PUSH(POP(TOKEN_STACK), TOKEN_QUEUE)
IF STACKISEMPTY(TOKEN_STACK) THEN CALL ERRORS("Can't find a matching ""("".", TRUE)
LOOP
IF PEEK(TOKEN_STACK) = "FUNCTION" THEN
DISCARD = POP(TOKEN_STACK)
CALL PUSH("@", TOKEN_QUEUE)
ELSE
DISCARD = POP(TOKEN_STACK)
END IF
CASE ","
DO WHILE NOT( PRECEDENCE( PEEK(TOKEN_STACK) ) = 0 )
CALL PUSH(POP(TOKEN_STACK), TOKEN_QUEUE)
IF STACKISEMPTY(TOKEN_STACK) THEN CALL ERRORS("Can't find a matching function ""("".", TRUE)
LOOP
CASE "+","-","*","/","^","%"
TOKEN_A = INPUT(TOKEN_NUMBER)
DO WHILE ISOPERATOR(PEEK(TOKEN_STACK))
TOKEN_B = PEEK(TOKEN_STACK)
IF (ASSOCIATIVITY(TOKEN_B) = "left" AND PRECEDENCE(TOKEN_A) = PRECEDENCE(TOKEN_B)) OR (PRECEDENCE(TOKEN_A) < PRECEDENCE(TOKEN_B)) THEN
CALL PUSH(POP(TOKEN_STACK), TOKEN_QUEUE)
ELSE
EXIT DO
END IF
LOOP
CALL PUSH(TOKEN_A, TOKEN_STACK)
CASE "FUNCTION"
CALL PUSH(INPUT(TOKEN_NUMBER), TOKEN_STACK)
CASE ELSE
CALL PUSH(INPUT(TOKEN_NUMBER), TOKEN_QUEUE)
END SELECT
NEXT
FOR ITEMCOUNT = 0 TO UBOUND(TOKEN_STACK)
IF PEEK(TOKEN_STACK) = "(" THEN CALL ERRORS("Can't find a matching "")"".", TRUE)'(
CALL PUSH(POP(TOKEN_STACK), TOKEN_QUEUE)
NEXT
SHUNTINGYARD = JOIN(TOKEN_QUEUE,"|")
END FUNCTION
'#############################################################################
FUNCTION ASSOCIATIVITY(ASSOC)
SELECT CASE LCASE(ASSOC)
CASE "^",""
ASSOCIATIVITY = "right"
CASE ELSE
ASSOCIATIVITY = "left"
END SELECT
END FUNCTION
FUNCTION ISOPERATOR(ITEM)
ISOPERATOR = LEN(ITEM) = 1 AND INSTR("+-*/%^",ITEM)
END FUNCTION
SUB PUSH(ITEM,BYREF STACK)
IF UBOUND(STACK) > -1 THEN
REDIM PRESERVE STACK(UBOUND(STACK) + 1)
STACK(UBOUND(STACK)) = ITEM
ELSE
STACK = ARRAY(ITEM)
END IF
END SUB
FUNCTION POP(BYREF STACK)
IF UBOUND(STACK) > -1 THEN
POP = STACK(UBOUND(STACK))
REDIM PRESERVE STACK(UBOUND(STACK) - 1)
END IF
END FUNCTION
FUNCTION STACKISEMPTY(STACK)
IF UBOUND(STACK) > -1 THEN
STACKISEMPTY = FALSE
ELSE
STACKISEMPTY = TRUE
END IF
END FUNCTION
FUNCTION PEEK(STACK)
IF UBOUND(STACK) > -1 THEN
PEEK = STACK(UBOUND(STACK))
END IF
END FUNCTION