Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: PHP: Laskutoimitusten suorittaminen merkkijonosta

kayttaja-2791 [20.06.2005 19:33:00]

#

Laskee yhtälön arvon merkkijonosta.

Alunperin tein tämän toista projektia varten, mutta kun homma paisui niin päätin erottaa tämän kokonaan omaksi systeemikseen.
Toki tämänkin voisi regexpien sijaan hoitaa evalilla (kunhan ensin tarkastaa syötteen kunnolla),
mutta päädyin regexpeihin kun niitä pitäisi opetella jokatapauksessa :|

* Tukee kerto-, jako-, plus- ja potenssimerkkejä (^). Lisäksi desimaalitluvut pitää merkitä pisteinä (.), pilkkua (,) ei tueta.
* Antaa debugtietoja halutessa, omaan käyttöön halusin paitsi html:nä myös ihan tekstimuodossa, joten sekin on valittavissa
* Tekee vähän tyhmästi kaikki toiminnot (esim. suorittaa kertolaskuosan vaikkei kertomisia olekaan), vähän tyhmä mutta ehkä versiossa 2 paremmin :|

Edelleen koodia saa käyttää kuten tahtoo, kunhan et vain väitä tätä alkuperäistä koodia omaksesi.

Versio 2.0, 22.07.2005:
*Korjattu bugi jossa muut muuttujat kuin x eivät toimineet
*Korjattu bugi jossa tuli virhe kutsuttaessa funktiota useaan kertaan

<?php
/*
Laskee yhtälön arvon stringistä.

Alunperin tein tämän toista projektia varten, mutta kun homma paisui niin päätin erottaa tämän kokonaan omaksi systeemikseen.
Toki tämänkin voisi regexpien sijaan hoitaa evalilla (kunhan ensin tarkastaa syötteen kunnolla),
mutta päädyin regexpeihin kun niitä pitäisi opetella jokatapauksessa :|

* Tukee kerto-, jako-, plus- ja potenssimerkkejä (^). Lisäksi desimaalitluvut pitää merkitä pisteinä (.), pilkkua (,) ei tueta.
* Antaa debugtietoja halutessa, omaan käyttöön halusin paitsi html myös ihan tekstimuodossa, joten sekin on valittavissa
* Tekee vähän tyhmästi kaikki toiminnot (esim. suorittaa kertolaskuosan vaikkei kertomisia olekaan), vähän tyhmä mutta ehkä versiossa 2 paremmin :|

Edelleen koodia saa käyttää kuten tahtoo, kunhan et vain väitä tätä alkuperäistä koodia omaksesi.

Versio 2.0, 22.07.2005:
*Korjattu bugi jossa muut muuttujat kuin x eivät toimineet
*Korjattu bugi jossa tuli virhe kutsuttaessa funktiota useaan kertaan
*/

function laskestring($laskutoimitus, $luku, $muuttuja = "x", $debug = false, $debughtml = true) {
    //Poistetaan turhat välilyönnit alusta ja lopusta, samoin muutetaan useiden välilyöntien sarjat vain yhdeksi välilyönniksi
    $laskutoimitus = preg_replace("/[ ]+/", " ", trim($laskutoimitus));

    //Hyväksytään vain laskutoimitusmerkit sekä numerot ja välilyönnit
    $laskutoimitus = preg_replace("/[^\\+\\-\\/\\*\\^\\(\\)\\.{$muuttuja}0-9 ]/", "", $laskutoimitus);

    //Tarkastetaan että sulkeiden aloitusmerkkejä on yhtä paljon kuin sulkemismerkkejä, mutten palautetaan error
    if (substr_count($laskutoimitus, "(") != substr_count($laskutoimitus, ")")) {
        if ($debug)
            echo "Sulkujen määrä ei täsmää, tarkasta että olet sulkenut kaikki sulkeet\n";
        if ($debughtml)
            echo "<br />";
        return false;
    }

    //Tämä suorittaa itse laskun, tälläinen järjestely sulkujen takia,
    //aloitusviestillä voi antaa ensimmäisen viestin jossa näytetään laskettava lauseke
    if (!function_exists("suoritalasku")) {
    function suoritalasku($laskutoimitus, $luku, $muuttuja = "x", $debug = false, $debughtml = false, $aloitusviesti = "Laskutoimitus") {
          //Aloitustilanne debugtulostusta varten
          $debugtulostus[$aloitusviesti] = $laskutoimitus;

          //Sallitut laskumerkit
          $laskumerkit = "[\\+\\-\\/\\*\\^]";

          //Tarkistetaan hieman syntaksia
          //Aloitus
          if (preg_match("/^([\\+\\/\\*\\^])/", $laskutoimitus, $virhe)) {
              $debugtulostus['Virheellinen aloitus'] = $virhe[0];
              $virheitä = true;
          }

          //Lopetus
          if (preg_match("/($laskumerkit)$/", $laskutoimitus, $virhe)) {
              $debugtulostus['Virheellinen lopetus'] = $virhe[0];
              $virheitä = true;
          }

          //Laskutoimitukset
          if (preg_match("/($laskumerkit)[ ]?($laskumerkit)/", $laskutoimitus, $virhe)) {
              $debugtulostus['Virheellinen laskutoimitus'] = $virhe[0];
              $virheitä = true;
          }

          //Muuten ei virhettä, tehdään laskutoimitukset
          if (!$virheitä) {
              //Muunnetaan merkinnät 2x muotoon 2 * x
              $debugtulostus['Muunnos'] = $laskutoimitus = preg_replace("/([0-9.]+)[ ]?$muuttuja/", "\\1 * $muuttuja", $laskutoimitus);

              //Sijoitetaan muuttuja
              $debugtulostus['Muunnos'] = $laskutoimitus = preg_replace("/$muuttuja/", $luku, $laskutoimitus);

              //Lasketaan potenssit
              $debugtulostus['Potenssi'] = $laskutoimitus = preg_replace("/([0-9.]+)[ ]?\^[ ]?([0-9.]+)/e", "pow(\\1, \\2)", $laskutoimitus); //Korottaa potenssiin

              //Lasketaan kerto- ja jakolaskut
              while (!$exit) {
                  $edellinen = $laskutoimitus;
                  $debugtulostus['Kertominen'] = $laskutoimitus = preg_replace("/([0-9.]+)[ ]?\\*[ ]?([0-9.]+)/e", "\\1 * \\2", $laskutoimitus); //Kertominen
                  $debugtulostus['Jakaminen'] = $laskutoimitus = preg_replace("/([0-9.]+)[ ]?\\/[ ]?([0-9.]+)/e", "\\1 / \\2", $laskutoimitus); //Jakaminen
                  //Mikäli ei ole enään jako- tai kertomerkkejä, tai loop on jäänyt junnaamaan (edellinen arvo sama kuin nykyinen) poistutaan while-silmukasta
                  if (strpos($laskutoimitus, "/") == false && strpos($laskutoimitus, "*") == false or $edellinen == $laskutoimitus)
                      $exit = true;
              }
            }
            //Exitiä käytetään vielä toisessa loopissa
            unset($exit);

            //Lasketaan plus- ja miinuslaskut
            while (!$exit) {
                $edellinen = $laskutoimitus;
                if (strpos($laskutoimitus, "-") === 0)
                    $debugtulostus['Ynnäys'] = $laskutoimitus = preg_replace("/\\-[ ]?([0-9.]+)[ ]?(\\+|\\-)[ ]?([0-9.]+)/e", "-\\1 \\2 \\3", $laskutoimitus);
                else
                    $debugtulostus['Ynnäys'] = $laskutoimitus = preg_replace("/^[ ]?([0-9.]+)[ ]?(\\+|\\-)[ ]?([0-9.]+)/e", "\\1 \\2 \\3", $laskutoimitus);
                if (!strpos($laskutoimitus, "+") && !strpos(substr($laskutoimitus, 1), "-") or $edellinen == $laskutoimitus)
                    $exit = true;
            }

          //Tulostetaan debugit jos käyttäjä niin haluaa
          if ($debug) {
              if ($debughtml)
                  echo "<pre>";

              foreach ($debugtulostus as $toiminto => $laskutoimitus) {
                  if ($debughtml)
                      echo "$toiminto: \t $laskutoimitus\n";
                  else
                      echo "$toiminto: $laskutoimitus \n";
              }

              if ($debughtml)
                  echo "</pre>";
          }

          //Palautetaan vastaus mikäli laskutoimitukset meni ilman virheitä
          if (!$virheitä)
              return $laskutoimitus;
          else {
              return "false"; //Purkkaa ehkäpä,
                              //palautetaan string false ja tarkastetaan sen sijainti itse koodissa alla, tämä siksi että
                              //palautus tulee regexpin evaliin josta en osaa sen totuusarvoa tarkastaa :|
          }
      } //suoritakoodi()
    }
    //Annetaan se kaikkein alkuperäisin jos debugataan
    if ($debug) {
        if ($debughtml)
            echo "<b>Alkuperäinen muoto:</b> <code>$laskutoimitus</code> \n";
        else
            echo "Alkuperäinen muoto: $laskutoimitus \n";
    }

    //Sulkujen sisältä lasketaan ensin laskut
    while (!$exit) {
        $edellinen = $laskutoimitus;
        $laskutoimitus = preg_replace("/\\([ ]?([^\\(]*?)[ ]?\\)/e", "suoritalasku('\\1', $luku, $muuttuja, $debug, $debughtml, 'Suluista')",$laskutoimitus);

        if (strpos($laskutoimitus, "false") != false)
            return false;

        //Ja taas tätä loputonta debuggauskoodia
        if ($debug) {
            if ($debughtml)
                echo "<br />\n<b>Sulut poistettu, uusi muoto:</b> <code>$laskutoimitus</code>"."<br />\n";
            else
                echo "\nSulut poistettu, uusi muoto: $laskutoimitus"."\n";
        }

        //Mikäli on sulkuja vielä, tai looppi jää jostain syystä jauhamaan tyhjää keskeytetään se
        if (strpos($laskutoimitus, "(") == false && strpos($laskutoimitus, ")") == false or $edellinen == $laskutoimitus)
            $exit = true;
    }

    //Sitten kun sulut on poistettu niin lasketaan loputkin
    $laskutoimitus = suoritalasku($laskutoimitus, $luku, $muuttuja, $debug, $debughtml);

    //Palautetaan vastaus mikäli laskutoimitukset meni ilman virheitä
    if (!$virheitä && is_numeric($laskutoimitus))
        return $laskutoimitus;
    else
        return false;
}

//Esimerkki
if (!$_GET['laskutoimitus'])
    $laskutoimitus = "((((-5 * 5) + (7x)) * 3.5) - 2) ^ 2";
else
    $laskutoimitus = $_GET['laskutoimitus'];
if (!$_GET['luku'])
    $luku = 5;
else
    $luku = $_GET['luku'];

echo "\nVastaus: ".laskestring($laskutoimitus, $luku, "x", 1, 1);
?>

T.M. [30.06.2005 01:51:08]

#

En testannut, mutta kyllä tuosta on hyötyä tehdä se muullakin kuin evalilla, koska epä-scriptikielissä ei ole eval funktiota lainkaan :)

Vielä kun tekisi ilman regexpejä niin voisi soveltaa helposti kaikkiin kieliin.

feenix [05.07.2006 09:55:59]

#

Onhan tuo hienoa viritellä regexpeillä mutta homma menisi hieman nätimmin (ja laajennettavammin) ihan vain tekemällä pinon ja se siitä...

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta