2013-03-31 7 views
9

Chodziłam jeśli ktoś dobry w PHP mógłby doradzić, w jaki sposób sprawdzić poprawność wsporniki w użądleniu ekspresyjnym tak:Jak zweryfikować wsporniki w równaniu ciąg w PHP

(5 * 3 [ 6) - 6] 

który jest niewłaściwy wyraz. Potrzebuję do tego funkcji. Oto, co próbowałem do tej pory:

<?php 
function hasMatchedParenthesis($string) { 

$counter1 = 0; 
$counter2 = 0; 

$length = strlen($string); 

for ($i = 0;$i < $length; $i++) { 
    $char = $string[$i]; 
    if($char == '(') { 
     $counter1 ++; 
    } elseif($char == ')') { 
     $counter1 --; 
    } 

     for($j =0;$j < $length; $j++) { 
      $char = $string[$j]; 
      if($char == '[') { 
       $counter2 ++; 
     } elseif($char == ']') { 
       $counter2 --; 
     } 

     } 


    if($counter1 < 0 || $counter2 < 0) { 
     return false; 
    } 

} 

echo 'ok';; 

} 


hasMatchedParenthesis('[5] * 3 - (4 - 7 * [3-6])'); // this is ok! 

hasMatchedParenthesis('(5 * 3 [ 6) - 6]'); // this returns as TRUE, but it is not! 

?> 

Zarzuty pomogły mi rozwiązać problem "[6]"! Nie wiem jak to zrobić :(

+2

Czy zezwalasz na zagnieżdżone nawiasy? na przykład '((1 + 3)/(2 - 5)) + 5' ... jeśli tak, prawdopodobnie najlepiej użyć odpowiedniego lexera. –

+0

Myślę, że twój warunek sprawdzania nie jest właściwy. Powinno to być 'if ($ counter1! = 0 || $ counter2! 0) {return false; } '. Zaczynasz od obu liczników "0", a oba powinny mieć wartość "0" po sprawdzeniu, czy nawiasy są prawidłowe. Jest to dodatek do komentarza @MarkBaker :-) – Havelock

+0

Odpowiedź ircmaxell na http://stackoverflow.com/questions/12692727/how-to-make-a-calculator-inphph jest dobrym punktem wyjścia do matematycznego formuła lexer (i parser, jeśli potrzebujesz tego również) –

Odpowiedz

8

Pierwszy pomysł, który przychodzi mi do głowy to użycie stosu. PHP udostępnia dwie funkcje do traktowania tablicy jako stosu: array_push i array_pop. Możemy wykorzystać je do stworzenia stos 0 (jesteśmy wewnątrz otworu () i 1 (jesteśmy wewnątrz [) i sprawdzić, kiedy wspornik zamykający mecze z ostatniej wartości możemy brzmieniu:

function hasMatchedParenthesis($string) { 
    $len = strlen($string); 
    $stack = array; 
    for ($i = 0; $i < $len; $i++) { 
     switch ($string[$i]) { 
      case '(': array_push($stack, 0); break; 
      case ')': 
       if (array_pop($stack) !== 0) 
        return false; 
      break; 
      case '[': array_push($stack, 1); break; 
      case ']': 
       if (array_pop($stack) !== 1) 
        return false; 
      break; 
      default: break; 
     } 
    } 
    return (empty($stack)); 
} 

Zauważmy, że można przedłużyć ten jakiejkolwiek innej pary znaków ze { i }:

case '{': array_push($stack, 2); break; 
case '}': 
    if (array_pop($stack) !== 2) 
     return false; 
break; 
+1

+1 smart i minimalistyczny. Również maszyna stosu jest dokładnie tym, czego używa się do sprawdzenia, czy ciągi znaków należą do języka, co definiuje gramatyka bezkontekstowa. – Havelock

+0

Dziękuję bardzo! Wydaje się, że to wystarczy! – user1838334

+2

dobre podejście.Pchnąłbym '(/ [' sam do stosu i sprawdził na ') /]' podczas poppingu. – Ejaz

1

Zwykłym sposobem sprawdzania poprawności takich reguł składniowych jest użycie Context-free grammar - zobacz examples, a znajdziesz dokładnie to, co próbujesz zrobić jako przykład :-) A ty może zastosować zasady takiej gramatyki za pomocą lexera, jak zauważył Mark Baker.
W swoim kodzie upewnisz się tylko, że liczba nawiasów otwierających odpowiada liczbie nawiasów zamykających i jest to usterka. Również jak już wskazano w moim komentarzu twój ostatni warunek powinien być

if ($counter1 != 0 || $counter2 != 0){ 
    return false; 
} 

W obecnym przypadku true zostanie zwrócone, gdy każdy z liczników jest >=0. Wypróbuj prosty przypadek hasMatchedParenthesis('[5] * 3 - (4 - 7 * [3-6');, który zwróci true, mimo że jest nieprawidłowy.

+0

Witam, czy masz jakiś link, aby zobaczyć, jak mogę to zastosować ** Gramatyka bez kontekstu ** w php? aby dokonać tej samej weryfikacji tego pytania: –

+1

@EmilioGort można [znaleźć pewne zasoby] (https://www.google.com/search?q=php%20context%20free%20grammar%20parser) w sieci – Havelock

0

pisałem następujące realizację.

function check_brackets_balance($string, $bracket_map = false) { 
    $bracket_map = $bracket_map ?: [ '[' => ']', '{' => '}', '(' => ')' ]; 
    $bracket_map_flipped = array_flip($bracket_map); 
    $length = mb_strlen($string); 
    $brackets_stack = []; 
    for ($i = 0; $i < $length; $i++) { 
     $current_char = $string[$i]; 
     if (isset($bracket_map[$current_char])) { 
      $brackets_stack[] = $bracket_map[$current_char]; 
     } else if (isset($bracket_map_flipped[$current_char])) { 
      $expected = array_pop($brackets_stack); 
      if (($expected === NULL) || ($current_char != $expected)) { 
       return false; 
      } 
     } 
    } 
    return empty($brackets_stack); 
} 

Używa również stosu, ale pobiera mniej kodu i ma dodatkowy parametr dla własnego zestawu nawiasów.

check_brackets_balance('[5] * 3 - (4 - 7 * [3-6])'); // true 
check_brackets_balance('(5 * 3 [ 6) - 6]')); // false