Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Python] Calculatrice -> Atelier

27 décembre 2009 à 3:15:20

Bonsoir, je suis nouveau en programmation et j'ai décidé de me lancer dans l'atelier calculatrice. J'ai pour le moment quelques bases en python (conditions, boucles, fonctions) et j'ai décidé de me lancer. Je n'ai bien sur jamais "parser" quelque chose de ma vie.

Voici le code:
def tern(condition, fstTerm, sndTerm):
	if condition:
		return fstTerm
	else:
		return sndTerm

def calcul(tab):
	i = 1
	resultat = 0

	while(i < len(tab)):
		if tab[i] == '+':
			resultat = tern(i==1, tab[i-1] + tab[i+1], resultat + tab[i+1])
		elif tab[i] == '-':
			resultat = tern(i==1, tab[i-1] - tab[i+1], resultat - tab[i+1])
		elif tab[i] == '*':
			resultat = tern(i==1, tab[i-1] * tab[i+1], resultat * tab[i+1])
		elif tab[i] == '/':
			if tab[i+1] == 0:
				resultat = 'impossible'
			else:
				resultat = tern(i==1, tab[i-1] / tab[i+1], resultat / tab[i+1])
		elif tab[i] == '^':
			resultat = tern(i==1, pow(tab[i-1],tab[i+1]), pow(resultat, tab[i+1]))
		else:
			resultat = "impossible"
			break	
		i+=2
	
	print "Resultat = " + str(resultat)
	
def parser(operation):
	tab = []
	operande = ''
	for element in operation:
		if element == ' ':
			continue
		elif element.isdigit():
				operande += element
		else:
			tab.append(int(operande))
			tab.append(element)
			operande = ""
	tab.append(int(operande))
	calcul(tab)

def main():
	operation = raw_input('Saisir une operation: ')
	parser(operation)

main()


Le seul problème (bien que le code soit basique et ne marche qu'avec des entiers) c'est qu'il ne respecte pas l'ordre des signes (* avant + par exemple). J'ai beau me casser la tête je vois pas trop comment faire.
J'ai penser a parcourir le tableau au départ et si je trouve * comme signe je fais tab[i-1] * tab[i+1] que j'ajoute dans resultat et ensuite je les suppriment du tableau. Ensuite je reparcourt le tableau avec un résultat déjà présent mais cela ne doit pas être top top. Auriez vous des pistes pour améliorer le code actuel mais aussi arriver à gerer les priorités dans les opérations.

Merci.
  • Partager sur Facebook
  • Partager sur Twitter
27 décembre 2009 à 9:53:31

Bonjour, il y a deux trois choses...
Beaucoup de personne aurai utilisé le module des expressions régulières mais j'ai une petite astuce pour toi...

crée une fonction récursive....
string a une méthode split qui crée une liste coupée aux endroit du caractère en argument exemple:
def calc(mystring):
    try:
        return int(mystring) # est-ce une valeur directe?
    except ValueError:
        try:
            return float(mystring) # est-ce une valeur directe à virgule?
        except ValueError:
            pass
    if "+" in mystring:   #application de l'addition
        r=0
        [r+=calc(i) for i in mystring.split("*")]
        return r
    #gérer la soustraction... la division...
    if "*" in mystring:   #application de la multiplication
        r=1
        [r*=calc(i) for i in mystring.split("*")]
        return r
    if "^" in mystring:# ... à ton tour de jouer 
    # puissance... (à composer du moins au plus prio)


Ce code n'a pas été testé: à l'arrache... et à toi de le compléter en ajoutant l'addition avant la multiplication que ça respecte les prio... tu dois même pouvoir gérer les parenthèses...

après python a une méthode "eval" qui pourait parfaitement servir de calculette...



Dans le cas de mon exemple de code: la méthode vas "couper" les string... ça descend dans un arbre qui se compose de la racine aux feuilles puis s'exécute des feuilles à la racine...

Les feuilles dans ce cas c'est les chiffres, la racine c'est l'expression complete...

Dans le sens de composition on divise l'arbre au niveau des faibles priorités (additions) car ce sera exécuté en remontant.

Cas particulier pour les parenthèses, on dit que c'est la priorité ultime mais il faut placer dans le code sa gestion avant l'addition... pour isoler les branches.

Bonne chance, hésite pas à répondre si tu cherche une autre voie ou si tu aboutie avec celle ci (rappel:ce code est non testé et certainement pas exempt de bugs)
  • Partager sur Facebook
  • Partager sur Twitter
Si la violence ne résout pas ton problème c'est que tu n'as pas été assez violent.
27 décembre 2009 à 15:43:28

Merci pour ta réponse.
Je suis allez voir sur wikipedia et le super cours de Bluestorm à propos de la recursivité et je pense que pour le moment c'est un peu trop haut niveau pour moi comme façon de penser. Ce sera donc mon dernier objectif, reussir la calculette en recursif :D

Je te remercie pour la petite astuce avec la fonction split que j'ai implémenter et qui allège pas mal le code. Par contre tes exemples ont l'air d'etre d'un niveau assez élevé je vais donc essayer d'alléger le code une fois la gestion des parenthèses mise en place. Désormais la calculette prend en compte l'ordre des opérations avec les '*'. Je vous met le code:

def tern(condition, fstTerm, sndTerm):
	if condition:
		return fstTerm
	else:
		return sndTerm

def multiplication(tab):
	i = len(tab) - 2
	while i > 0:
		if tab[i] == '*':
			tab[i-1] = int(tab[i-1]) * int(tab[i+1])
			del tab[i]
			del tab[i]
		i = i - 2
	return tab

def calcul(tab):
	i = 1
	resultat = 0
	
	tab = multiplication(tab)
	while(i < len(tab)):
		tab[i-1] = int(tab[i-1])
		tab[i+1] = int(tab[i+1])
		if tab[i] == '+':
			resultat = tern(i==1, tab[i-1] + tab[i+1], resultat + tab[i+1])
		elif tab[i] == '-':
			resultat = tern(i==1, tab[i-1] - tab[i+1], resultat - tab[i+1])
		elif tab[i] == '/':
			if tab[i+1] == 0:
				resultat = 'impossible'
			else:
				resultat = tern(i==1, tab[i-1] / tab[i+1], resultat / tab[i+1])
		elif tab[i] == '^':
			resultat = tern(i==1, pow(tab[i-1],tab[i+1]), pow(resultat, tab[i+1]))
		else:
			resultat = "impossible"
			break	
		i+=2
	
	print "Resultat = " + str(resultat)
	
def parser(operation):
	tab = operation.split(' ')
	calcul(tab)

def main():
	operation = raw_input('Saisir une operation: ')
	parser(operation)

main()


Mon prochain objectif sera les parenthèses, j'ai une petite idée sur comment faire, pensez vous que créer des sous chaine serait bien? Je vous explique, je parcours le tableau depuis la fin, des que je vois une parenthese fermante je prend la suite dans une sous chaine jusqu'a tombé sur une parenthese ouvrante. Si je tombe sur une autre parenthese fermante avant une ouvrante j'ouvre une nouvelle sous chaine et je répète l'opération. Ensuite je traite les sous chaines en remontant de la dernière trouvée à la première puis je traite ce qu'il y avait a l'extérieur des parenthèses.

Qu'en pensez vous?
Merci ^^
  • Partager sur Facebook
  • Partager sur Twitter
28 décembre 2009 à 17:32:38

deuxième remarque, l'opérateur ternaire existe en python depuis le 2.5
a = fstTerm if condition else sndTerm

Après il est plus pythonique de mettre un for à la place du while

for i, e in enumerate(tab):
		tab[i-1] = int(tab[i-1])
		tab[i+1] = int(tab[i+1])
		if e == '+':
			resultat = tern(i==1, tab[i-1] + tab[i+1], resultat + tab[i+1])
		elif e == '-':
			resultat = tern(i==1, tab[i-1] - tab[i+1], resultat - tab[i+1])
		elif e == '/':
			if tab[i+1] == 0:
				resultat = 'impossible'
			else:
				resultat = tern(i==1, tab[i-1] / tab[i+1], resultat / tab[i+1])
		elif e == '^':
			resultat = tern(i==1, pow(tab[i-1],tab[i+1]), pow(resultat, tab[i+1]))
		else:
			resultat = "impossible"
			break

Mais je vien de voir une chose ... étonnante... tu concidère qu'il y a nécessairement des espaces entre opérandes et opérateur et que les nombres négatifs ne sont pas en début d'opération...

Ce détail me choque, les éléments pairs ne sont pas nécessairement des opérendes ^^

Ma méthode peut se faire sans récursion...
ma fonction récursive ramenne à peut pret à ça...
Code testé et fonctionnel
def dividelist(l):
    div=False
    for e in l.split("/"):
        e=float(e)
        div= div/e if div else e
    return div

def multlist(l):
    mult=1
    for e in l.split("*"):
        mult*=dividelist(e)
    return mult

def souslist(l):
    sous=False
    for e in l.split("-"):
        e=multlist(e)
        sous= sous-e if sous else e
    return sous

def addliste(l):
    add=0
    for e in l.split("+"):
        add+=souslist(e)
    return add

def calc(mystring):
    mystring=mystring.replace(" ","")
    return addliste(mystring)

  • Partager sur Facebook
  • Partager sur Twitter
Si la violence ne résout pas ton problème c'est que tu n'as pas été assez violent.
6 décembre 2019 à 1:40:20

merci

-
Edité par achrafChared 6 décembre 2019 à 1:41:01

  • Partager sur Facebook
  • Partager sur Twitter
6 décembre 2019 à 9:30:08

Bonjour,

Déterrage

Citation des règles générales du forum :

Avant de poster un message, vérifiez la date du sujet dans lequel vous comptiez intervenir.

Si le dernier message sur le sujet date de plus de deux mois, mieux vaut ne pas répondre.
En effet, le déterrage d'un sujet nuit au bon fonctionnement du forum, et l'informatique pouvant grandement changer en quelques mois il n'est donc que rarement pertinent de déterrer un vieux sujet.

Au lieu de déterrer un sujet il est préférable :

  • soit de contacter directement le membre voulu par messagerie privée en cliquant sur son pseudonyme pour accéder à sa page profil, puis sur le lien "Ecrire un message"
  • soit de créer un nouveau sujet décrivant votre propre contexte
  • ne pas répondre à un déterrage et le signaler à la modération

Je ferme ce sujet. En cas de désaccord, me contacter par MP.

  • Partager sur Facebook
  • Partager sur Twitter

Pas d'aide concernant le code par MP, le forum est là pour ça :)