我正在決議所有具有以下結構的檔案:
A=B #A==test
這意味著變數 A 將被設定為 B,只有當 A 等于 'test' 條件也可以變得更復雜
C=D #C==test1,D==test2
這意味著如果 C 等于“test1”且 D 等于“test2”,則 C 將設定為 D
條件也可以有 OR 陳述句:
E=F #E==test3,(F==test4|F==test5),username!=abc
這意味著如果 E 等于“test3”并且(F 等于“test4”或 F 等于“test5”)并且用戶名不等于“abc”,則 E 將設定為 F
我一直在評估最后一個條件。評估這種運算式的好方法是什么?
目前我將每個條件決議為 3 個陣列,分別是“左手”、“運算子”、“右手”。條件 #C==test1,D==test2 被決議為:
$lefthands=(C, D)
$righthands=(test1,test2)
$operands=(==, ==)
之后,我遍歷陣列并根據當前使用的運算元進行評估。
if $operands[$i] -eq "==" ( return ($lefthands[$i] -eq $righthands[$i] }
只要其中一個條件回傳 false,我就會停止評估完整條件。如果只有 AND 陳述句,這可以正常作業,但是如果存在 OR 陳述句,這將不再有效。
uj5u.com熱心網友回復:
看起來你有一個相當簡單的中綴運算式語法,如下所示:
variable = [ a-z | A-Z ]
string = [ a-z | A-Z | 0-9 ]
atom = variable [ "==" | "!=" ] string
expr = [ atom | "(" expr ")" | expr [ "," | "|" ] expr ]
假設您可以將運算式嵌套到任意深度并且您希望正確處理運算子優先級(例如,w,x|y,z表示((w,x)|y),z)或(w,x)|(y,z)什至w,(x|y),z,所有這些都會給出不同的結果),您將很難使用基本的字串操作來評估運算式,您可能需要使用更復雜的方法:
- 將字串 Lex 轉換為標記
- 更改標記的順序(中綴 -> 后綴)以更容易計算結果
- 評估給定變數的一些值的運算式
樂行
這基本上將運算式字串分解為邏輯塊串列 - 例如A==test可能變為identifier "A", operator "==", identifier "test".
它被稱為“中綴”表示法,因為運算子位于運算元之間 - 例如A==test.
function ConvertTo-InfixTokens
{
param( [string] $Source )
$chars = $Source.ToCharArray();
$index = 0;
while( $index -lt $chars.Length )
{
$token = [pscustomobject] [ordered] @{
"Type" = $null
"Value" = [string]::Empty
};
$char = $chars[$index];
switch -regex ( $char )
{
# var or str
"[a-zA-z0-9]" {
$token.Type = "identifier";
$token.Value = $char;
$index = 1;
while( ($index -lt $chars.Length) -and ($chars[$index] -match "[a-zA-z0-9]") )
{
$token.Value = $chars[$index];
$index = 1;
}
}
# eq
"=" {
if( $chars[$index 1] -eq "=" )
{
$token.Type = "eq";
$token.Value = $chars[$index] $chars[$index 1];
$index = 2;
}
else
{
throw "LEX01 - unhandled char '$($chars[$index 1])'";
}
}
# ne
"!" {
if( $chars[$index 1] -eq "=" )
{
$token.Type = "ne";
$token.Value = $chars[$index] $chars[$index 1];
$index = 2;
}
else
{
throw "LEX02 - unhandled char '$($chars[$index 1])'";
}
}
"," {
$token.Type = "and";
$token.Value = $char;
$index = 1;
}
"\|" {
$token.Type = "or";
$token.Value = $char;
$index = 1;
}
"\(" {
$token.Type = "lb";
$token.Value = $char;
$index = 1;
}
"\)" {
$token.Type = "rb";
$token.Value = $char;
$index = 1;
}
default {
throw "LEX03 - unhandled char '$char'";
}
}
write-output $token;
}
}
例子:
PS> ConvertTo-InfixTokens -Source "A==test"
Type Value
---- -----
identifier A
eq ==
identifier test
PS> ConvertTo-InfixTokens -Source "E==test3,(F==test4|F==test5),username!=abc"
Type Value
---- -----
identifier E
eq ==
identifier test3
and ,
lb (
identifier F
eq ==
identifier test4
or |
identifier F
eq ==
identifier test5
rb )
and ,
identifier username
ne !=
identifier abc
轉換為后綴
中綴表示法看起來更自然,因為它類似于普通數學,但它依賴于您在評估運算式時處理操作的優先規則 - 例如1 1 * 2 2等于(((1 1) * 2) 2) => 6或1 (1 * 2) 2 => 5或(1 1) * (2 2) => 8?- 這意味著您可能需要提前查看其他運營商即將到來的情況,以防他們需要首先進行處理。
如果我們將運算式轉換為后綴表示法(或Reverse Polish Notation),評估運算式會更容易 - 請參閱此答案以獲得一些額外的優勢。這基本上將運算元放在第一位,然后是運算子 - 例如1 1 * 2 2become 1 1 2 * 2 ,它將在邏輯上處理為:((1 (1 2 *) ) 2 )=> ((1 2 ) 2 )=> (3 2 )=>5
以下代碼將應用此轉換:
# based on https://www.tutorialspoint.com/Convert-Infix-to-Postfix-Expression#:~:text=To convert infix expression to,maintaining the precedence of them.
function Convert-InfixToPostfix
{
param( [pscustomobject[]] $Tokens )
$precedence = @{
"or" = 1
"and" = 2
"eq" = 9
"ne" = 9
};
$stack = new-object System.Collections.Generic.Stack[pscustomobject];
for( $i = 0; $i -lt $Tokens.Length; $i )
{
$token = $Tokens[$i];
switch( $token.Type )
{
"identifier" {
write-output $token;
}
"lb" {
$stack.Push($token);
}
"rb" {
while( $stack.Peek().Type -ne "lb" )
{
write-output $stack.Pop();
}
$null = $stack.Pop();
}
default {
# must be a known operator
if( -not $precedence.ContainsKey($token.Type) )
{
throw "PF01 - unknown operator '$($token | ConvertTo-Json -Depth 99 -Compress)' at index $i";
}
while( ($stack.Count -gt 0) -and ($precedence[$token.Type] -le $precedence[$stack.Peek().Type]) )
{
write-output $stack.Pop();
}
$stack.Push($token);
}
}
}
while( $stack.Count -gt 0 )
{
write-output $stack.Pop();
}
}
例子:
PS> $infix = ConvertTo-InfixTokens -Source "A==test"
PS> $postfix = Convert-InfixToPostfix -Tokens $infix
PS> $postfix
Type Value
---- -----
identifier A
identifier test
eq ==
PS> $infix = ConvertTo-InfixTokens -Source "E==test3,(F==test4|F==test5),username!=abc"
PS> $postfix = Convert-InfixToPostfix -Tokens $infix
PS> $postfix
Type Value
---- -----
identifier E
identifier test3
eq ==
identifier F
identifier test4
eq ==
identifier F
identifier test5
eq ==
or |
and ,
identifier username
identifier abc
ne !=
and ,
評估
最后一步是獲取后綴標記并評估它們。“所有”我們需要做的就是從左到右讀取標記——當我們得到一個運算元時,我們把它放在堆疊上,當我們得到一個運算子時,我們從堆疊中讀取一些運算元,應用運算子并將結果推回到堆疊上...
function Invoke-PostfixEval
{
param( [pscustomobject[]] $Tokens, [hashtable] $Variables )
$stack = new-object System.Collections.Generic.Stack[pscustomobject];
for( $i = 0; $i -lt $Tokens.Length; $i )
{
$token = $Tokens[$i];
if( $token.Type -eq "identifier" )
{
$stack.Push($token);
}
elseif( $token.Type -in @( "eq", "ne" ) )
{
$str = $stack.Pop().Value;
$var = $stack.Pop();
if( -not $Variables.ContainsKey($var.Value) )
{
throw "undefined variable '$($var.Value)' in token '$($token | ConvertTo-Json -Depth 99 -Compress)' at index $i";
}
$val = $Variables[$var.Value];
$result = switch( $token.Type ) {
"eq" { $val -eq $str }
"ne" { $val -ne $str }
default { throw "unhandled operator '$($token.Type)' in token '$($token | ConvertTo-Json -Depth 99 -Compress)' at index $i"; }
}
#write-host "'$val' $($token.Type) '$str' => $result";
$stack.Push([pscustomobject] [ordered] @{ "Type" = "bool"; Value = $result });
}
elseif( $token.Type -in @( "and", "or" ) )
{
$left = $stack.Pop().Value;
$right = $stack.Pop().Value;
$result = switch( $token.Type ) {
"and" { $left -and $right }
"or" { $left -or $right }
default { throw "unhandled operator '$($token.Type)' in token '$($token | ConvertTo-Json -Depth 99 -Compress)' at index $i"; }
}
#write-host "'$left' $($token.Type) '$right' => $result";
$stack.Push([pscustomobject] [ordered] @{ "Type" = "bool"; Value = $result });
}
else
{
throw "EVAL01 - unhandled token '$($token | ConvertTo-Json -Depth 99 -Compress)' at index $i";
}
}
return $stack.Peek().Value;
}
例子:
PS> $variables = [ordered] @{
"A" = "test";
"C" = "test1";
"D" = "test2";
"E" = "test3";
"F" = "test4";
"username" = "abc"
}
PS> $infix = ConvertTo-InfixTokens -Source "A==test"
PS> $postfix = Convert-InfixToPostfix -Tokens $infix
PS> $value = Invoke-PostfixEval -Tokens $postfix -Variables $variables
PS> $value
True
PS> $infix = ConvertTo-InfixTokens -Source "E==test3,(F==test4|F==test5),username!=abc"
PS> $postfix = Convert-InfixToPostfix -Tokens $infix
PS> $value = Invoke-PostfixEval -Tokens $postfix -Variables $variables
PS> $value
False # fails because "username!=abc" is false
PS> $infix = ConvertTo-InfixTokens -Source "E==test3,F==test4|F==test5,username!=abc"
PS> $postfix = Convert-InfixToPostfix -Tokens $infix
PS> $value = Invoke-PostfixEval -Tokens $postfix -Variables $variables
PS> $value
True # operator precedence means it's evaluated as "(E==test3,F==test4)|(F==test5,username!=abc)" and "(E==test3,F==test4)" is true
您可能希望為生產代碼添加更多錯誤處理,并撰寫一些 Pester 測驗以確保它適用于更多種類的輸入,但它適用于我在這里所做的有限測驗。如果您發現任何不起作用的示例,請隨時在評論中發布它們......
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/525821.html
標籤:电源外壳解析表达
