2013-03-31 11 views
9

mi aggiravo se qualcuno bene in PHP potrebbe consigliare su come convalidare le staffe in un pungiglione espressione come questa:Come convalidare le staffe in stringa un'equazione in PHP

(5 * 3 [ 6) - 6] 

che è l'espressione sbagliata. Ho bisogno di una funzione per farlo. Ecco cosa ho provato fino ad ora:

<?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! 

?> 

Le parole mi aiutano a risolvere la convalida della cosa [6]! Non so come farlo :(

+2

Consenti parentesi nidificate? per esempio. '((1 + 3)/(2-5)) + 5' ... se è così, probabilmente meglio utilizzare una corretta lexer –

+0

Credo che la vostra condizione di controllo non è giusto. Dovrebbe essere "if ($ counter1! = 0 || $ counter2! = 0) {return false; } '. Cominci con entrambi i contatori '0' ed entrambi dovrebbero essere' 0' quando hai finito di convalidare se le parentesi sono valide. Questo è in aggiunta al commento di @MarkBaker :-) risposta – Havelock

+0

di ircmaxell per http://stackoverflow.com/questions/12692727/how-to-make-a-calculator-in-php è un buon punto di partenza per una matematica formula lexer (e un parser, se avete bisogno che pure) –

risposta

8

La prima idea che mi viene in mente è quello di utilizzare una pila. PHP fornisce due funzioni per trattare un array come una pila: array_push e array_pop. possiamo utilizzare per creare una pila di 0 (siamo all'interno un'apertura () e 1 (siamo dentro un [) e controllare ogni volta una staffa di chiusura corrisponde con l'ultimo valore abbiamo inserito:

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)); 
} 

noti che è possibile estendere questo a qualsiasi altra coppia di personaggi, tra cui { e }:

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

+1 smart e minimalista. Anche una macchina stack è esattamente ciò che viene utilizzato per verificare se le stringhe appartengono alla lingua, definita da una grammatica context-free. – Havelock

+0

Grazie mille! Questo sembra fare il trucco! – user1838334

+2

buon approccio.Avrei spinto il '(/ [' stesso in pila e ho controllato ') /]' mentre scoppiavo. – Ejaz

1

Il modo usuale di convalidare tali regole sintattiche è usare un Context-free grammar - vedere lo examples e troverai esattamente quello che stai cercando di fare come esempio :-) E tu può applicare le regole di una tale grammatica usando un lexer come indicato da Mark Baker.
In voi il codice si sta solo facendo in modo che il numero di staffe di apertura corrisponde al numero di parentesi di chiusura e che è il difetto. Inoltre, come ho sottolineato nel mio commento l'ultima condizione dovrebbe essere

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

Nel tuo caso attuale true verrà restituito quando uno qualsiasi dei contatori è >=0. Prova con un semplice caso hasMatchedParenthesis('[5] * 3 - (4 - 7 * [3-6');, che restituirà true anche se è sbagliato.

+0

Ciao, hai qualche link per vedere come posso applicare questa ** al contesto senza grammatica ** in php? per fare la stessa validazione di questa domanda –

+1

@EmilioGort è possibile [trovare alcune risorse] (https://www.google.com/search?q=php%20context%20free%20grammar%20parser) in rete – Havelock

0

ho scritto successiva realizzazione.

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); 
} 

Utilizza anche stack, ma richiede meno codice e ha parametri aggiuntivi per le proprie parentesi impostate.

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