Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Python] Fonction palindrome

Sujet résolu
Anonyme
28 mai 2008 à 20:25:24

Bonjour :)

Comme indiqué dans un cours de Python trouvé sur Internet, j'ai réalisé une fonction qui détermine si un mot est un palindrome :

# -*- coding: utf-8 -*-
def palindrome(chaine):
	i, longueur = 0, len(chaine)

	while i < longueur:
		if chaine[i] != chaine[-i - 1]:
		      return False
		i += 1

	return True

if palindrome(raw_input()):
    print "Palindrome."
else:
    print "Pas palindrome."


Si vous pouviez me faire des suggestions, tout ça...
  • Partager sur Facebook
  • Partager sur Twitter
28 mai 2008 à 23:02:12

palindrome = lambda s: s[:] == s[::-1]
  • Partager sur Facebook
  • Partager sur Twitter
28 mai 2008 à 23:25:02

tu peux même enlever le [:]

(t'es chiant j'allais le poster)
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
28 mai 2008 à 23:25:56

C'est quoi une fonction lambda ?
  • Partager sur Facebook
  • Partager sur Twitter
28 mai 2008 à 23:50:02

Raaa il tente de t'embrouillé avec sa :)

Si tu commence python, ne t'occupe pas de sa, tu le verra plus tard lorsque tu le maitrisera un peu mieux.

Pour ton code, rien a redire pour ma part. Bien structuré, des noms de variables clair.
  • Partager sur Facebook
  • Partager sur Twitter
29 mai 2008 à 23:21:36

Une fonction lambda, c'est une fonction qui ne prends qu'un seul argument. Les deux codes suivant sont donc équivalent :

palindrome = lambda s: s == s[::-1]

def palindrome(s):
    return s == s[::-1]


L'intérêt des lambdas, c'est qu'on est pas obligé de leur donner un nom. On peut également les utiliser comme paramètre d'une fonction :

>>> def deux_fois(f, x):
...    return f(f(x))

>>> deux_fois(lambda a: a * a, 2)
16

>>> def square(a):
...     return a * a
>>> deux_fois(square, 2)
16

>>> square(square(2))
16


Bref, c'est très pratique :) .

Concernant ton code, j'ai une suggestion : Est-il vraiment nécessaire de parcourir toute la chaîne de caractère ? Est-ce qu'on ne peut pas s'arrêter avant ? :)
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
29 mai 2008 à 23:23:09

La fonction s'arrête avec le return False de ma condition ! o_O
  • Partager sur Facebook
  • Partager sur Twitter
29 mai 2008 à 23:24:39

Justement, avec son code il s'arrête dès la première lettre non symétrique (ce qui n'est pas le cas avec la fonction lambda présenté).
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
29 mai 2008 à 23:25:42

Donc ma fonction serait la meilleure ? Super ! :)
  • Partager sur Facebook
  • Partager sur Twitter
29 mai 2008 à 23:27:59

Oui, mais pour les palindromes valides, elle fait des calculs en trop :) .

Exemple: "elle"
Ta boucle principale va faire 4 comparaisons, alors qu'il en suffit de deux pour savoir s'il s'agit d'un palindrome.
  • Partager sur Facebook
  • Partager sur Twitter
30 mai 2008 à 0:23:37

exact, il faudrait que tu changes la valeur de longeur.
Y'a une ptite différence quand le nombre de lettre est pair/impair, tu peux éviter un test en t'y prenant bien.
  • Partager sur Facebook
  • Partager sur Twitter
30 mai 2008 à 7:36:15

Citation : raphamil

Bonjour :)

Comme indiqué dans un cours de Python trouvé sur Internet, j'ai réalisé une fonction qui détermine si un mot est un palindrome :

# -*- coding: utf-8 -*-
def palindrome(chaine):
	i, longueur = 0, len(chaine)

	while i < longueur:
		if chaine[i] != chaine[-i - 1]:
		      return False
		i += 1

	return True

if palindrome(raw_input()):
    print "Palindrome."
else:
    print "Pas palindrome."



Si vous pouviez me faire des suggestions, tout ça...



Moi bizarrement, je vois tout de suite un truc que tu peux améliorer, c'est la boucle.
le while, c'est faire une boucle avec une condition. Mais si tu veux utiliser un compteur, le for est plus approprié :
# -*- coding: utf-8 -*-
def palindrome(chaine):

	for i in range(0, len(chaine) + 1):
		if chaine[i] != chaine[-i - 1]:
		      return False

	return True

if palindrome(raw_input()):
    print "Palindrome."
else:
    print "Pas palindrome."

il fera une boucle allant de 0 jusqu'au nombre de caractères contenu dans ta chaine.
  • Partager sur Facebook
  • Partager sur Twitter
30 mai 2008 à 14:23:24

ca semble plus pythonnesque, mais algorithmiquement je ne trouve pas ça mieux : tu crées une liste (qui sait, peut-être immense...) alors que tu auras peut-être à quitter la fonction à la première vérification.

Sinon, l'étendue de la recherche s'avère encore deux fois trop grande (mais je suppose que tu n'as pas cherché à prendre en compte les dernières réponses et que tu ne t'es basé que sur le post originel).
  • Partager sur Facebook
  • Partager sur Twitter
30 mai 2008 à 18:31:36

range crée la liste directement oui, extrêmement lourd pour les grosse liste. Il vaut mieux utilisé xrange a la place qui est plus économique sur les grosses listes.
  • Partager sur Facebook
  • Partager sur Twitter
30 mai 2008 à 20:33:56

Ou pour ne vérifier qu'une moitié de liste :
def palindrome(s):
    l, r = divmod(len(s), 2)
    return s[:l + r] == s[l::-1]


C'est une solution simple et rapide, et cat_loic, en voulant faire ton « plus pythonnesque », tu te plantes complétement sachant que jamais on ne devrait itérer sur un (x)range pour parcourir une liste/chaîne (déconseillé par GvR, par exemple), et qu'on doit pouvoir trouver dans itertools un générateur qui parcourt la liste depuis le début et la fin.

Ah, et btw, sans même prendre le temps de tester ta solution, les IndexError sont flagrants. Tu devrais tester tes codes avant de les poster (après relecture, il n'y a qu'un IndexError, l'autre débordement provoquera juste un comportement inattendu de ta fonction :) ).
  • Partager sur Facebook
  • Partager sur Twitter
30 mai 2008 à 20:52:45

Tiens 42, je n'avais jamais vu la fonction divmod, c'est plutôt pas mal sa
  • Partager sur Facebook
  • Partager sur Twitter
30 mai 2008 à 22:25:53

def palyndrome(str):
    length = len(str)
    def paly(pos):
        return 2*pos >= length or str[pos] == str[length-1-pos] and paly(pos+1)
    return paly(0)
  • Partager sur Facebook
  • Partager sur Twitter
31 mai 2008 à 0:22:32

C'est ce qu'on appelle de l'Ocaml traduit :-° . Bluestorm, tu dépasseras pas des chaînes de 4000 caractères avec ça en Python (bon ok, des palindromes de 4000 chars c'est rare), c'est une solution lente, moins élégante que celle des slices, et mal orthographiée.
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
31 mai 2008 à 11:40:41

Quelqu'un pourrait-il m'expliquer ce qu'a pondu bluestorm :-°

(Je débute encore un peu en Python.)
  • Partager sur Facebook
  • Partager sur Twitter
31 mai 2008 à 11:51:36

Le principe c'est en gros « si ça marche pour le caractère n et que le caractère n n'est pas après le milieu de la chaîne, alors c'est un palindrome si ça marche pour le caractère n + 1 ». Il utilise un procédé que l'on appelle la récursivité, qu'on évite en Python car c'est plutôt lent et qu'on est plutôt limité, mais qui peut être plus rapide chez certains langages (Ocaml, Lisp, par exemple).

Cf. son tuto sur le sujet, d'ailleurs ;) .
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
31 mai 2008 à 11:53:53

J'ai lu son tuto (très intéressant par ailleurs :) )
J'ai déjà essayé de faire de la récursivité (en C, pour calculer de PGCD de 2 nombres) et ça m'a fait une boucle infinie... Vive l'itératif ! :D
  • Partager sur Facebook
  • Partager sur Twitter
31 mai 2008 à 12:38:29

Suffit de bien choisir sa condition d'arrêt, c'est tout :) . On peut faire des tonnes de trucs marrants avec la récursivité : dès qu'on traite des arbres d'ailleurs, c'est presque obligé si on veut avoir un truc élégant (donc par exemple du XML ou autre, c'est fun à traiter récursivement). Après, contrairement à certains, je trouve pas ça adapté partout ;) .

Et cadeau :
>>> def pgcd(a, b):
...     if a < b: a, b = b, a
...     if a % b == 0: return b
...     else: return pgcd(b, a % b)
...
>>> pgcd(35, 14)
7
  • Partager sur Facebook
  • Partager sur Twitter
31 mai 2008 à 18:38:22

On peut aussi écrire :
def PGCD(a,b):
    if a>b:
        return PGCD(a-b,b)
    elif a<b:
        return PGCD(a,b-a)
    else:
        return a
  • Partager sur Facebook
  • Partager sur Twitter
31 mai 2008 à 20:02:24

Citation : 42 !

C'est ce qu'on appelle de l'Ocaml traduit :-° . Bluestorm, tu dépasseras pas des chaînes de 4000 caractères avec ça en Python (bon ok, des palindromes de 4000 chars c'est rare), c'est une solution lente, moins élégante que celle des slices, et mal orthographiée.



La lenteur et la limite de caractères vient du fait que l'implémentation de Python suce (disons le clairement). Pour la faute d'orthographe, toutes mes condoléances, et pour les slices effectivement, c'est plus court et sans doute plus idiomatique.

Cependant, dans l'absolu cette solution n'est pas spécialement lente, et en particulier elle aussi s'arrête à la première lettre qui ne convient pas (à partir du moment où les "and" et "or" sont à court-circuit).

def pgcd(a, b):
    return b == 0 and a or pgcd(b, a % b)
  • Partager sur Facebook
  • Partager sur Twitter
31 mai 2008 à 20:27:14

Bien vu, j'avais pas remarqué que si b > a ça revenait au même à une récursion près. Ça simplifie effectivement beaucoup le code et ça le rend lambda-isable :) .
pgcd = lambda a, b: b == 0 and a or pgcd(b, a % b)
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
8 juin 2008 à 0:28:54

Pour revenir à ma fonction, j'ai codé ça aussi :

# -*- coding: utf-8 -*-

if (lambda c: c == c[::-1])(raw_input()): #if palindrome(raw_input()):
    print "Palindrome."
else:
    print "Pas palindrome."


En fait, je pense que sauf si on considère la fonction comme un exercice d'algorithmique, on peut utiliser une simple comparaison a == a[::-1], puisque Python doit sûrement s'arrêter au premier caractère non identique : son interpréteur a été écrit par des personnes bien plus compétentes que moi, et mon implémentation de l'opérateur == dans ZString (cf. tuto C++) est comme ceci :

bool ZString::operator==(const ZString &chaine) const
{
	if (m_longueur != chaine.m_longueur) return false;
	for (size_t i = 0; i < m_longueur; i++) {
		if (m_chaine[i] != chaine.m_chaine[i])
			return false;
	}
	return true;
}


Tout ce débat est donc un peu vain :-° , bien que constructif.

Dans tous les cas, merci à tous :)
  • Partager sur Facebook
  • Partager sur Twitter
8 juin 2008 à 16:48:24

Le problème avec cette solution, c'est que python retourne entierement la chaine de caracteres quand tu fais str[::-1] :) (or ce n'est pas nécessaire).
  • Partager sur Facebook
  • Partager sur Twitter
8 juin 2008 à 19:35:39

Une autre solution de 42!, qui résout le problème de lastsseldon en utilisant des itérateurs :
palindrome = lambda s: all(imap(lambda (a, b): a == b, izip(s, reversed(s))))

  • Partager sur Facebook
  • Partager sur Twitter
10 juin 2008 à 10:09:07

Et ne pas oublier d'importer le bon module, sans quoi le code ne fonctionne pas :
from itertool import imap, izip
palindrome = lambda s: all(imap(lambda (a, b): a == b, izip(s, reversed(s))))
  • Partager sur Facebook
  • Partager sur Twitter