transformer une chaine en opérateur mathématique

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : transformer une chaine en opérateur mathématique

par Sékiltoyai » 02 avr. 2009, 15:32

Alors, je présentais la piste de l'orienté objet comme alternative à la représentation sous forme de chaine. Justement, le parsage d'une expression est vraiment très complexe, c'est pour cela qu'on évite au maximum d'y avoir recours.
Si tu es intéressé par ces problématiques, tu peux essayer le cours de Théorie des Langages sur developpez. Là encore je ne l'ai pas lu, mais en principe avec cela tu auras toutes les armes en main pour attaquer des expressions de ce type…

par SpintroniK » 02 avr. 2009, 08:43

Y'a pas grand chose sur developpez.com.
Par contre, ton exemple nécessite quand même un parsage avant de pouvoir construire l'arbre, il faut que a+b devienne (dans l'arbre)

Code : Tout sélectionner

+ / \ a b
donc en gros il faut parser avant et transformer a+b en +(a, b)...
Ce qui n'est pas forcément évident quand, c'est mon cas par exemple, on veut parser du LaTeX étant donné que le langage ne s'occupe que de l'"affichage" des maths et non du sens des formules.
Je pense donc qu'il faut d'abord explorer la piste du parseur... (ce que je vais faire)

par Sékiltoyai » 02 avr. 2009, 02:05

Je ne vais pas m'appesantir sur ce point, mais le problème, c'est que PHP n'est pas assez strict et il me semble qu'il cache beaucoup d'aspects intéressants de la POO.

Voilà, si tu as des questions, n'hésite pas. :)

par Nours312 » 02 avr. 2009, 01:56

Tu commences à comprendre la POO.
Ben je croyais avoir compris, mais je suis un pur autodidacte, jamais aucuns cours, seulement du temps passé à chercher comment faire comprendre à mes prog ce que j'ai en tête ! ...

C'est sur, je ne m'étais jamais posé ce genre de question... de simple boucles au seins de mes objet, me permettaient de résoudre mes idées ... mais là, des classes paraissant indépendantes dans leurs structures rassemblées en une "interface", dictant les fonctions résultantes communes ça m'ouvre de belles perspectives de manipulations d'objets ... enfin, si j'ai bien suivi ...

J'ai trouvé quelques articles sympa sur codes-sources et autres ... qui m'ont permit de mieux assimiler l'ensemble des perspectives, même si je suppose ne pas avoir tout imaginé !!

je viens juste (il y a 2 semaines) de comprendre l'étendu des possibilité en POO Javascript (dont le raisonnement m'a toujours aru tordu :s), tout en ayant toujours pensé "maitriser" le PHP, ... Mais c'est bien de ce rendre compte qu'on a toujours à apprendre sur ce merveilleux langage ...

Merci pour tout ! ;) @Bientôt !

[Note : ce message a été posté de manière anonyme avant d'être réattribué à son auteur]

par Sékiltoyai » 02 avr. 2009, 01:18

Hyper intéressant, as-tu des liens vers des cours expliquant ces principes ?
Euh, je dois t'avouer que non :-/
Peut être ces cours de developpez.com mais à part ça… Concernant cet exemple, c'est une version simplifiée de ce que l'on a pu voir dans nos cours de POO.
ssssssssSSSSSSPLENDIDE !!!!!!!!!!!!!!

mais ceci est une petite merveille ! .....
leaule, merci :D
je n'ai pas encore compris comment l'utiliser concrètement (l'expression à monter pour visualiser le résultat), mais j'ai marqué cette page, et j'ai bien l'intention d'étudier tous ceci de plus près !!
En fait, là aussi c'est assez simple, pour représenter l'expression 5 + 6, tu écris :
$exp = new Plus(Valeur(5),Valeur(6));
Et pour la calculer :
echo $exp->calcul();
J'ai pas testé mais si j'ai pas dit que des conneries, ça devrait donner 11. :D
le problème actuel est que je dois faire une très grande quantité d'opérations avec peu de paramètres ... donc ça n'est pas adapté, mais à l'inverse, se doit avoir un rendement merveilleux !!
Bah ça dépend ce que t'appelle rendement. Si tu veux une vitesse d'éxecution ultra-rapide, je ne peux pas te dire. Mais de toute façon, ce n'est pas forcément ce qui fait la qualité d'un programme, la propreté et la structure du code sont très importantes.
Tant que j'écris, ce doit etre très utile pour générer des menus à sous menus variés dont identifiant parent est inclus dans l'élément ...

array(array(id=>1, parent=>null, menu1), array(id=>1, parent=>1, menu1), array(id=>3, parent=>2, menu1), array(id=>4, parent=>2, menu1), ...)

actuellement, je faisait une boucle pour recomposer un tableau avec divers parametres ce qui me donne une suite indigeste de foreach à la conception, et tout autant à la régénération ...

mais là, ça doit m'apporter une grande aide et un dynamisme monstre, ainsi qu'une capacité infinie de sous menus ....
Tu commences à comprendre la POO. :)

Mais renseigne toi sur la Programmation Orientée Objet, cela te permettra de structurer ton code…
A savoir que les principaux langages utilisés en entreprise sont des langages orientés objet, c'est très important de connaître ces notions.

par Nours312 » 02 avr. 2009, 00:54

ssssssssSSSSSSPLENDIDE !!!!!!!!!!!!!!

mais ceci est une petite merveille ! .....

je n'ai pas encore compris comment l'utiliser concrètement (l'expression à monter pour visualiser le résultat), mais j'ai marqué cette page, et j'ai bien l'intention d'étudier tous ceci de plus près !!

... Mon imaginaire s'emballe !! ... il faut avant tout que je termine mon prog ... Dès que j'ai un peu de temps ou que je doivent intervenir sur des stats, je reprendrais contact sur le sujet !!

Merci beaucoup !!

le problème actuel est que je dois faire une très grande quantité d'opérations avec peu de paramètres ... donc ça n'est pas adapté, mais à l'inverse, se doit avoir un rendement merveilleux !!


Tant que j'écris, ce doit etre très utile pour générer des menus à sous menus variés dont identifiant parent est inclus dans l'élément ...

array(array(id=>1, parent=>null, menu1), array(id=>1, parent=>1, menu1), array(id=>3, parent=>2, menu1), array(id=>4, parent=>2, menu1), ...)

actuellement, je faisait une boucle pour recomposer un tableau avec divers parametres ce qui me donne une suite indigeste de foreach à la conception, et tout autant à la régénération ...

mais là, ça doit m'apporter une grande aide et un dynamisme monstre, ainsi qu'une capacité infinie de sous menus ....


Merci bien !

par SpintroniK » 01 avr. 2009, 23:17

Hyper intéressant, as-tu des liens vers des cours expliquant ces principes ?

par Sékiltoyai » 01 avr. 2009, 23:00

Je vais reprendre pas à pas et expliquer ce que j'ai fait.
Déjà on va expliciter avec un diagramme :
Image

Le bloc Expression est une interface et non une classe. Cela signifie que toute classe qui implémente Expression devra posséder une méthode calcul(). On fixe en fait un contrat de telle manière que l'on soit sûr que cette méthode existera dans toute classe qui se déclare être une Expression :
interface Expression 
{ 
   function calcul(); 
}

Les classes Valeur, Fois, et Plus (on peut en imaginer d'autres) sont des Expression.
La classe Valeur est une Expression assez simple, puisque sa seule donnée est un entier. Quand on va vouloir calculer la valeur de cette Expression, il suffira de retourner cet argument. C'est pourquoi la méthode calcul() est très simple :
class Valeur implements Expression 
{ 

   private $val;

   function __construct(int $val) 
   { 
      $this->val = $val; 
   } 

   function calcul() 
   { 
      return $this->val; 
   } 

}
Les classes Plus et Fois sont quasiment identiques (je ne décris que Plus). En fait, c'est là qu'arrive l'intérêt de la récursivité. Comment traiter les cas de 5 - (3 + 2 + 5) ou 8 * 9 * (3 + 1) ? Il y a des parenthèses à gérer. C'est là qu'on simplifie en revenant à la définition stricte d'une opération :
Une opération Plus (par exemple) prend deux Expression et les additionne. Cette opération étant elle-même une Expression. On applique simplement ce principe, notre opération Plus aura un attribut exp1 et un attribut exp2, tous deux des Expressions, et est elle-même une Expression (donc notre opération Plus va aussi implémenter la classe Expression).
Donc dans le cas où on a 5 + 6 + 8, notre première opération Plus aura pour arguments l'Expression 5 (représenté dans une classe Valeur), et l'Expression 6+8 (représenté dans une classe Plus).

Pour le calcul la récursivité est là aussi géniale. En fait, une fois que tu as ton Expression Plus composée de 5 et de 6+8, le résultat du calcul sera simplement le résultat du calcul de 5 ajouté au résultat du calcul de 6+8. L'objet Plus qui représentera 6+8 se chargera à son tour d'appeler le calcul de 6 et le calcul de 8 pour les additionner, et retourner le résultat à la méthode qui l'a appelé.

Ce qui donne :
class Plus implements Expression 
{ 

   private $exp1;
   private $exp2;

   function __construct(Expression $exp1, Expression $exp2) 
   { 
      $this->exp1 = $exp1; 
      $this->exp2 = $exp2; 
   } 

   function calcul() 
   { 
      return $this->exp1->calcul() + $this->exp2->calcul();
   } 

}
Pour les autres fonctions de calcul c'est exactement pareil. Après je ne dis pas que tu auras un truc forcément plus rapide, mais il faut que tu comprennes que cette approche est la meilleure. La Programmation Orientée Objet te permet de structurer tes programmes et de simplifier des opérations complexes.
Après c'est à toi de voir si tu l'appliques mais il faut absolument que tu comprennes cette solution pour élargir ta vision des choses. Si tu peux comprendre et réutiliser le principe de récursivité, tu feras un très grand pas dans l'informatique.

Voilà, n'hésite pas à poser d'autres questions. :)

par Sékiltoyai » 01 avr. 2009, 19:07

C'est bien que tu sois intéressé par cette solution, je développerais dans quelques heures. :)

par Nours312 » 01 avr. 2009, 16:12

:oops: :oops: :oops:

En effet, je sais plus ce que j'avais fait, mais ça fonctionne bien :oops: Merci Ryle !!!

par contre, c'est lent ... Bien plus que d'appeler es fonction de manière externalisée ...

Test réalisé :

function p($a, $b) {
	return $a + $b;
}
function m($a, $b) {
	return $a - $b;
}
function f($a, $b) {
	return $a * $b;
}
function d($a, $b) {
	return $a / $b;
}
function fonc($cont, $v){
		switch($v[0]) {
			case 'p' :
			$cont = $cont + $v[1];
			break;
			case 'm' :
			$cont = $cont - $v[1];
			break;
			case 'f' :
			$cont = $cont * $v[1];
			break;
			case 'd' :
			$cont = $cont / $v[1];
		}
	return $cont;
}
$var = array(
	array('p', 5),
	array('m', 1),
	array('f', 3),
	array('d', 2)
);

$nb_occur = 100000;

// 1er test
$time_start = microtime(true);
for($i = 0; $i < $nb_occur; $i++) {
$cont = 0;
	foreach ($var as $v){
		$cont = fonc($cont, $v);
	}
	reset($var);
}
$time_end = microtime(true);
$time = $time_end - $time_start;

echo "<p>Durée 1er test : ".$time.' secondes</p>';

// 2ème test
$time_start = microtime(true);
for($i = 0; $i < $nb_occur; $i++) {
$cont = 0;
	foreach ($var as $v){
		$cont = $v[0] ($cont,  $v[1]);
	}
	reset($var);
}
$time_end = microtime(true);
$time = $time_end - $time_start;
echo "<p>Durée 2ème test : ".$time.' secondes</p>';

$var = array(
	array('+', 5),
	array('-', 1),
	array('*', 3),
	array('/', 2)
);
// 3ème test
$time_start = microtime(true);
for($i = 0; $i < $nb_occur; $i++) {
$cont = 0;
	foreach ($var as $v){
		eval('$cont = $cont ' . $v[0] . $v[1] . ';');
	}
	reset($var);
}
$time_end = microtime(true);
$time = $time_end - $time_start;
echo "<p>Durée 3ème test : ".$time.' secondes</p>';

résultat :
Durée 1er test : 0.31564712524414 secondes

Durée 2ème test : 0.23449206352234 secondes

Durée 3ème test : 2.026309967041 secondes

et là, ça pèche drôlement !!

Quelqu'un pourrait-il m'aider à comprendre la solution déployée par Sékiltoyai, afin que je puisse réaliser un teste à ce niveau, et par la même occasion, comprendre cette méthodologie ... ?

Merci Beaucoup @ tous !! :d ;)

par Ryle » 01 avr. 2009, 15:56

Bah euh.... eval() il évalue une chaine comme si c'était une instruction php... il se fout de savoir si c'est des variables ou des opérations, tant que c'est du php :)

Alors naturellement tout dépend du niveau de compléxité de ton problème (et j'avoue pas avoir eu le courage de lire tout vos échanges), mais pour les opérations basiques indiquées au début, je trouve ça bien suffisant :
$cont = 0;

$var = array(
  array('+', 2),
  array('*', 3)

);

foreach ($var as $v){
	// affiche l'opération en cours
	echo $cont .' '. $v[0] .' '. $v[1] . ' = ';

	// evaluation de l'opération
	eval('$cont = $cont ' . $v[0] . $v[1] . ';');

	// affiche le résultat de l'opération
	echo $cont . '<br />';
}

echo 'Total : ' . $cont . '<br />';

par Nours312 » 01 avr. 2009, 14:23

heuu .. ouais .. donc c'est ce que je craignais ... je n'ai pas compris ... et je ne comprends pas ...

Pourrais tu développer Sékiltoyai s'il te plait ...

j'assaie, mais je ne pige pas le fonctionnement de ces classes ... j'ai jamais travaillé avec la notion d'interface .... :oops:

mais si tu peux Juste un exempleplus ou moins complet, aprés je le développerais pour toutes les opérations possible, mais histoir que je comprenne bien le fonctionnement ...

Merci Beaucoup ;)

@ Ryle >> eval() fonctionne avec des variable mais pas avec des opérateur, en tout cas, d'après les test que j'ai fais ...

Merci ;)

par Sékiltoyai » 01 avr. 2009, 13:07

Merci bien les gars, par contre je me suis peut-etre mal exprimé ou je n'ai pas compris les réponses, mais je ne veux pas concaténer les éléments ! ... je veux réaliser les opérations ....
Alors, relis ma réponse. Comme je te disais, là j'ai mis une méthode ( __toString() ) pour afficher l'opération, mais en plaçant des méthodes calcul(), tu peux réaliser les opérations. Exemple pour la classe plus :
function calcul()
{
   return $this->exp1->calcul() + $this->exp2->calcul();
}
Exemple pour la classe valeur :
function calcul()
{
   return $this->valeur;
}
Et pour stocker l'opération complète dans une chaine, tu serialises ton objet, de telle manière que tu puisses le mettre en base de données. Tu trouveras difficilement plus propre.

par Ryle » 01 avr. 2009, 12:01

eval() :?:

A faire à chaque opération pour gérer les priorités des opérateurs (* et / sur + et -)

par Nours312 » 01 avr. 2009, 11:42

Oups :

ce n'est pas fonc () mais {$v[0]}()

:oops: copié/collé raté ;)