\makeatletter
\@ifundefined{complet}
{\documentclass[10pt]{report}
\title{Python}
\newcommand{\categorie}{Python}
\input{latex_entete_perso}

\begin{document} %Cours

\setcounter{chapter}{13}
% xelatex --shell-escape cours_python
}
{}
\makeatother



\chapter{Opérateurs booléens}






\section{Opérateur booléen not}



\subsection{Tester un prédicat à True}


\subsubsection{Présentation du prédicat-exemple}

Soit le prédicat \fo{contient()} qui permet de tester si une chaine contient un mot précis, en comparant les deux en minuscules.

\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
def contient(chaine:str, mot:str) -> bool:
    """Prédicat qui renvoie True si le mot est dans le string"""
    
    chaine_minus = chaine.lower()  # La chaine mais en miniscules
    mot_minus = mot.lower()        # Le mot mais en miniscules            
    return mot_minus in chaine_minus
\end{minted}

\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
>>> contient("Je veux me plaindre de mes notes bla bla", "PLAINDRE")
True
\end{minted}


\subsubsection{Bonne pratique de programmation}

On exécute le bloc si l'expression booléenne fournie derrière le \fo{if} est évaluée à \fo{True}. Ce programme affiche les plats qui contiennent des poivrons :

\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
plats = [
    "crêpes au sucre: farine de blé; oeufs; lait; sucre",
    "pates carbonara : pâtes(blé); fromage; lardon(porc); crême épaisse; basilic",
    "moules frites : moule; vin blanc; oignons; persil; frites(pommes de terre)",
    "ratatouille : poivrons verts; poivrons rouges; aubergines; courgettes; tomates; oignons; ail; thym; laurier; persil",
    "boeuf aux carottes : boeuf; carottes; oignons; vin blanc; ail"
    "poulet basquaise : tomates; poivrons rouges; poivrons verts; oignons; poulet; thym; laurier; persil"
    ]
 
def contient(chaine:str, mot:str) -> bool:
    """Prédicat qui renvoie True si le mot est dans le string"""
    chaine_minus = chaine.lower()  # La chaine mais en miniscules
    mot_minus = mot.lower()        # Le mot mais en miniscules
    return mot_minus in chaine_minus
 
for plat in plats:                  # pour chaque plat du tableau plats
    if contient(plat, "Poivrons"):  # si le plat contient des poivrons
        print(plat)
\end{minted}


\subsubsection{Mauvaise pratique de programmation}

Nous aurions pu écrire ceci mais c'est plus long et inutile :

\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
for plat in plats:                          # pour chaque plat du tableau plats
    if contient(plat, "Poivrons") == True:  # si le plat contient des poivrons
        print(plat)
\end{minted}


\begin{multicols}{2}

\subsection{Tester un prédicat à False}

On veut récupérer les plats qui ne contiennent pas de poivron. 

\subsubsection{Très mauvaise pratique de programmation}

A éviter absolument. De façon naïve, on pourrait se dire : si le prédicat est vrai, on ne fait rien, sinon on agit.
	
\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
for plat in plats:
    if contient(plat, "poivron"):
        pass
    else:
        print(plat)
\end{minted}


\subsubsection{Assez mauvaise pratique de programmation}

On peut tester le fait que l'expression soit évaluée à \fo{False}.

\begin{small}
\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
# pour chaque plat du tableau plats
for plat in plats: 
  # si plat ne contient pas poivron                          
  if contient(plat, "Poivrons") == False:  
      print(plat)
\end{minted}
\end{small}

\subsubsection{Bonne pratique de programmation : utiliser not}

On peut encore faire mieux en utilisant le mot-clé \fo{not} qui inverse la valeur du booléen.

Signature : \fo{not bool -> bool}
	
\begin{multicols}{2}

\begin{minted}{python}
>>> b = False
>>> b
False
 
>>> not b
True
\end{minted}
\end{multicols}

On peut alors écrire plus proprement le fait d'agir uniquement si le mot \fo{"Poivron"} n'apparaît pas :

\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
# pour chaque plat du tableau plats
for plat in plats:
    # si plat ne contient pas poivron                      
    if not contient(plat, "Poivrons"):  
        print(plat)
\end{minted}







\end{multicols}







\section{Opérateur booléen and}


\begin{multicols}{2}

\subsection{and pour utiliser plusieurs conditions en même temps}

Imaginons qu'on veuille récupérer les plats qui contiennent du poivron \gras{et} de l'aubergine. On dispose du prédicat \fo{contient()}.




\subsubsection{Méthode possible mais problématique dans certains cas}

On filtre sur les poivrons puis sur les aubergines.

\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
for plat in plats:
   if contient(plat, "poivron"):
      if contient(plat, "aubergine"):
         print(plat)
\end{minted}


Si on ne doit rien faire lorsque c'est faux, c'est correct. Par contre, si on doit agir lorsque c'est faux, on doit faire du copier-coller.


\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
for plat in plats:
   if contient(plat, "poivron"):
      if contient(plat, "aubergine"):
         print(plat)
      else:
         faire_un_truc()
   else:
      faire_un_truc()
\end{minted}



\subsubsection{Bonne pratique de programmation : utiliser and}

On peut faire mieux en utilisant le mot-clé \fo{and} : ce mot-clé est un opérateur booléen qui

\begin{itemize}
\item \gras{renvoie Vrai uniquement si les entrées sont toutes vraies}
\item renvoie Faux dans tous les autres cas.
\end{itemize}

Signature : \fo{bool and bool -> bool}

\begin{multicols}{3}
\begin{minted}{python}
>>> a = True
>>> b = True
>>> a and b
True
>>> a = True 
>>> b = False
>>> a and b
False
>>> a = False 
>>> b = True
>>> a and b
False
\end{minted}
\end{multicols}

\paragraph{Exemple d'utilisation} : pour afficher les plats qui contiennent poivrons et aubergines :

\end{multicols}



\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
# pour chaque plat du tableau plats
for plat in plats:                      
    # si le plat contient poivrons ET aubergines
    if contient(plat, "Poivron") and contient(plat, "Aubergine"):  
        print(plat) 
\end{minted}


Notez bien la présence d'une condition à gauche et à droite du mot-clé and.

Si on veut agir lorsque c'est faux, pas de copier-coller :

\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
# pour chaque plat du tableau plats
for plat in plats:                      
    # si le plat contient poivrons ET aubergines
    if contient(plat, "Poivron") and contient(plat, "Aubergine"):  
       print(plat)
    else:  # sinon, il en manque au moins un des deux
       faire_un_truc() 
\end{minted}

\

\begin{multicols}{2}

\subsection{Une erreur typique}

Voici une erreur courante que tout le monde va faire un jour ou l'autre.

Imaginons qu'on veuille \gras{vérifier que deux notes soient bien supérieures à 10} : en français, nous dirions que note1 et note2 sont supérieures à 10.

Voici l'erreur sémantique courante :

\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
return note1 and note2 > 10
\end{minted}

Pourquoi est-ce que cela ne fonctionne pas comme vous le voulez ? 

D'abord car \gras{les opérateurs de comparaison sont prioritaires sur les opérateurs logiques}. En réalité, vous avez écrit ceci :

\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
return note1 and (note2 > 10)
\end{minted}    
    
L'opérateur \fo{and} travaille donc sur les deux propositions suivantes :

{        
\begin{itemize}
\item \fo{note1} : faux si \fo{note1} est nulle ou vide, vrai sinon.
\item \fo{note2 > 10} : rien d'étrange pour celle-ci.
\end{itemize}
}

La traduction qui fait Python de cette ligne est donc : est-ce que \fo{note1} contient quelque chose et est-ce que \fo{note2} est supérieure à 10. Cela n'est donc pas ce que nous voulions.


\subsection{La bonne façon de traduire ceci}

Il faut parler à Python comme il le désire. Il faut placer une propriété à gauche du \fo{and} et une propriété à droite du \fo{and}.

\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
return note1 > 10 and note2 > 10
\end{minted}   

Les opérateurs de comparaison étant prioritaires sur les opérateurs logiques, c'est comme si nous avions écrit ceci :

\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
return (note1 > 10) and (note2 > 10)
\end{minted} 

Cela veut bien dire ce qu'on voulait au départ : est-ce que \fo{note1} est supérieure à 10 et est-ce que \fo{note2} est supérieure à 10.

\end{multicols}





\section{Opérateur booléen or}


\begin{multicols}{2}

\subsection{Principe}

Imaginons qu'on veuille récupérer les plats qui contiennent soit du poivron, soit de l'aubergine, voire les deux à la fois. On commence par filtrer sur les poivrons, sinon, on filtre sur les aubergines.

\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
for plat in plats:
    if contient(plat, "poivron"):
        print(plat)
    elif contient(plat, "aubergine"):
        print(plat)
\end{minted}

Mais on a la même instruction sur les lignes 3 et 5.




\subsection{Bonne pratique de programmation : utiliser or}

Le mot-clé \fo{not} est un opérateur booléen qui

\begin{itemize}
\item renvoie Faux uniquement si les entrées sont toutes fausses
\item renvoie Vrai si l'une des entrées est vraie.
\end{itemize}


Signature : \fo{bool or bool -> bool}

\begin{multicols}{3}
\begin{minted}{python}
>>> a = True
>>> b = True
>>> a or b
True
>>> a = True 
>>> b = False
>>> a or b
True
>>> a = False 
>>> b = True
>>> a or b
True
\end{minted}
\end{multicols}


\end{multicols}



Pour afficher proprement les plats qui contiennent des poivrons OU des aubergines, ou les deux :
	
\begin{minted}[linenos=true, bgcolor=lightgray, tabsize=4, gobble=0, fontfamily=courier, fontsize=\small, xleftmargin=20pt, xrightmargin=5pt]{python}
for plat in plats:                      # pour chaque plat du tableau plats
    if contient(plat, "Poivron") or contient(plat, "Aubergine"):  # si le plat contient poivrons Ou aubergines
        print(plat)
\end{minted}
 
Notez bien la présence d'une condition à gauche et à droite du mot-clé \fo{or}.


\section{Priorité des opérateurs logiques (P.A.C.Bo)}

\subsubsection{Par rapport aux autres opérateurs : P.A.C.Bo}


\paragraph{P} Les plus prioritaires : les parenthèses ()
\paragraph{A} Ensuite les opérateurs arithmétiques : \fo{** * / // \% + -}
\paragraph{C} Ensuite les opérateurs de comparaison : [\fo{in} et \fo{not in}], ensuite [\fo{>}, \fo{>=}...], ensuite [\fo{==}, \fo{!=}]
    
\paragraph{Bo} Ensuite les opérateurs logiques booléens : \fo{not} puis \fo{and} puis \fo{or}

\paragraph{Encore moins prioritaire} : \fo{if}, \fo{elif}, \fo{else}

\paragraph{Le moins prioritaire de tous} : l'affectation \fo{=}







\section{Tables de vérité}

\subsection{Table de vérité du non/not}

\fo{
\begin{tabular}{|*{3}{c|}}
    \hline
     a  & not a \\
    \hline
     False  & True \\
    \hline
     True  & False \\
    \hline
\end{tabular}
}

\begin{multicols}{3}

\subsection{Table de vérité du et/and}

\fo{
\begin{tabular}{|*{3}{c|}}
    \hline
     a  & b & a and b \\
    \hline
     False  & False & False \\
    \hline
     False  & True & False \\
    \hline
     True  & False & False \\
    \hline
     True  & True & True \\
    \hline    
\end{tabular}
}

A RETENIR : le ET est VRAI uniquement lorsque toutes les entrées sont à VRAI.

\subsection{Table de vérité du ou/or}


\fo{
\begin{tabular}{|*{3}{c|}}
    \hline
     a  & b & a or b \\
    \hline
     False  & False & False \\
    \hline
     False  & True & True \\
    \hline
     True  & False & True \\
    \hline
     True  & True & True \\
    \hline    
\end{tabular}
}

A RETENIR : le OU est FAUX uniquement lorsque toutes les entrées sont à FAUX.


\subsection{Table de vérité du ou exclusif/xor}


\fo{
\begin{tabular}{|*{3}{c|}}
    \hline
     a  & b & a xor b \\
    \hline
     False  & False & False \\
    \hline
     False  & True & True \\
    \hline
     True  & False & True \\
    \hline
     True  & True & False \\
    \hline    
\end{tabular}
}

A RETENIR : le \fo{xor} logique n'existe pas nativement en Python, si vous voulez l'utiliser, il faudra utiliser \fo{not(a and b) and (a or b)}.



\end{multicols}



\input{licence}


\makeatletter
\@ifundefined{complet}
{\end{document}}

{}
\makeatother

