Correction : Listes

Implémentation par tableau

Création d'une liste de taille éléments

Plusieurs manières de faire pour créer un tableau de taille éléments :


class Liste:

	def __init__(self, taille: int):
		self.liste = [None]*taille	
		self.taille = taille 
			

On peut également utiliser une liste par compréhension :


class Liste:

	def __init__(self, taille: int):
		self.liste = [None for i in range(taille)]
		self.taille = taille 
			

Accès/affectation des éléments


class Liste:

	def __init__(self, taille: int):
		self.liste = [None]*taille
		self.taille = taille 
	
	def get(self, indice):
		return self.liste[indice]
	
	def set(self, indice, valeur):
		self.liste[indice] = valeur
			

On ne gère pas les cas où les indices sont en dehors du tableau, puisque Python signalera dans ce cas une erreur...ce qui, encore une fois, ne sera pas le cas en C/C++ !

Si on veut vraiment être aussi proche que possible de ces langages, on peut rajouter des assertions :


...
	
	def get(self, indice):
		assert indice >=0 and indice < len(self.liste), "Index out of range !"
		return self.liste[indice]
	
	def set(self, indice, valeur):
		assert indice >=0 and indice < len(self.liste), "Index out of range !"
		self.liste[indice] = valeur						
				

Ajout d'un élément en fin de liste pleine

Comme on doit créer une nouvelle liste d'un élément de plus que précédemment, il est nécessaire de stocker dans un attribut la taille de la liste :


class Liste:

    def __init__(self, taille: int):
        self.taille = taille
        self.liste = [None]*self.taille
    
    def append(self, valeur):
        #dans le cas ou la liste n'est pas encore pleine
        if self.get(self.taille-1)==None:
            i=self.taille-1
            #on détermine l'indice du dernier None dans le tableau python (en partant de la fin)
            while(self.get(i)==None):
                i=i-1
            self.set(i+1,valeur)

        #dans le cas ou la liste est pleine
        else:
            nv_liste = Liste(self.taille+1)
            for i in range(self.taille) :
                nv_liste.set(i,self.get(i))
            nv_liste.set(self.taille,valeur)

            # la liste est la nouvelle liste
            self.taille = nv_liste.taille
            self.liste = nv_liste.liste

			

Complexité : il est nécessaire de faire n opérations de recopies élémentaires pour chaque élément de la liste → complexité en O(n)

Insertion en n'importe quelle position


class Liste:

    def __init__(self, taille: int):
        self.taille = taille
        self.liste = [None]*self.taille
    
    def insert(self, indice, valeur):
        self.taille += 1
        nv_liste = liste(self.taille)           # création d'une nouvelle liste d'une unité de plus en taille

        for i in range(0, indice):              # recopie de la première partie de la liste ( de 0 à indice-1 )
            nv_liste.get(i) = self.get(i)

        nv_liste[indice] = valeur               # on intercale le nouvel élément ( indice )

        for i in range(indice+1, self.taille):  # recopie de la deuxième partie de la liste ( de indice à self.taille-1 )
            nv_liste.get(i) = self.get(i-1)

        self.liste = nv_liste                   # la liste est la nouvelle liste
			

Complexité : idem ( O(n) ).

Code complet


class Liste:

    def __init__(self, taille: int):
        self.taille = taille
        self.liste = [None]*self.taille
    
    def append(self, valeur):
        # dans le cas ou la liste n'est pas encore pleine
        if self.get(self.taille-1) == None:
            i = self.taille-1
            # on détermine l'indice du dernier None dans le tableau python (en partant de la fin)
            while (self.get(i) == None):
                i = i-1
            self.set(i+1,valeur)

        # dans le cas ou la liste est pleine
        else:
            nv_liste = Liste(self.taille+1)
            for i in range(self.taille) :
                nv_liste.set(i,self.get(i))
            nv_liste.set(self.taille,valeur)

            # la liste est la nouvelle liste
            self.taille = nv_liste.taille
            self.liste = nv_liste.liste
        
    def insert(self, indice, valeur):
        self.taille += 1
        nv_liste = liste(self.taille)           # création d'une nouvelle liste d'une unité de plus en taille

        for i in range(0, indice):              # recopie de la première partie de la liste ( de 0 à indice-1 )
            nv_liste.get(i) = self.get(i)

        nv_liste[indice] = valeur               # on intercale le nouvel élément ( indice )

        for i in range(indice+1, self.taille):  # recopie de la deuxième partie de la liste ( de indice à self.taille-1 )
            nv_liste.get(i) = self.get(i-1)

        self.liste = nv_liste     
			

Implémentation par liste chaînée

Instanciation d'une liste

Pour une liste vide

On ne passe aucun argument au constructeur de la classe Liste :


l = Liste()			
			

Pour une liste avec quelques éléments

Attention, plus délicat : il faut passer comme argument au constructeur de la liste la structure entière des maillons de cette dernière :


l = Liste(Maillon(2, Maillon(5, Maillon(68, Maillon(3)))))			
			

Pour chaque maillon, il faut bien passer la valeur de l'élément stocké + la suite des maillons, qui se retrouvent ainsi "imbriqués" les uns dans les autres : c'est le concept !

Par contre, pas la peine de préciser None pour le dernier maillon : il est automatiquement rajouté par le constructeur des objets Maillon.

Test si la liste est vide

Ce doit bien sûr être une méthode de la classe Liste. Il suffit de tester si la tête de la liste est None ou pas :


class Maillon:

    def __init__(self, valeur, suivant = None):
        self.valeur = valeur
        self.suivant = suivant
        
class Liste:

    def __init__(self, tete = None):
        self.tete = tete
        
    def is_empty(self):
        return self.tete is None

l1 = Liste()
l2 = Liste(Maillon(2, Maillon(5, Maillon(68, Maillon(3)))))		
print(l1.is_empty()) # True
print(l2.is_empty()) # False
			

On utilise une manière particulière pour tester si l'objet self.tete est None; on aurait pu tout aussi bien écrire :


    ...
        return self.tete == None
				

Il existe une très subtile différence entre ces deux écritures; celle utilisée ici l'est principalement parce qu'elle est beaucoup plus proche du langage naturel !

Primitives sans parcours de la liste

Insertion au début


    def insert_first(self, elt):
        if self.is_empty():             # si la liste est vide,
            self.tete = Maillon(elt)    # on crée simplement son maillon de tête.
        else:
            maillon = Maillon(elt)      # création d'un nouveau maillon
            maillon.suivant = self.tete # le nouveau maillon pointe vers la tête
            self.tete = maillon         # on remplace la tête par le nouveau maillon ( l'ancienne tête est donc devenue le deuxième maillon de la liste )			
        	

On peut simplifier ces 3 étapes en une seule :


    def insert_first(self, elt):
        if self.is_empty():             
            self.tete = Maillon(elt)    
        else:
            self.tete = Maillon(elt, self.tete)
        	

Voire même, ne pas faire de test du tout pour savoir si la liste est vide ou pas :


    def insert_first(self, elt):
        self.tete = Maillon(elt, self.tete)
        	

En effet, si la liste est vide, self.tete est None, que l'on passe alors en argument au constructeur de la classe Maillon, mais ce n'est pas un problème...

Complexité : on ne fait qu'un nombre donné d'affectations ( 3 dans la première version ), donc en O(1), indépendant du nombre d'éléments dans la liste ( = en temps constant ).

Suppression au début


	def pop_first(self):
	    assert not self.is_empty(); "La liste est vide !"   # renvoi d'erreur si la liste est déja vide !
	   
	    t = self.tete.valeur          # on stocke l'élément en tête pour pouvoir le renvoyer
	    self.tete = self.tete.suivant # on remplace simplement la tête par sa suite -> l'élément en tête disparaît !	
	    
	    return t	                          # renvoi de l'élément supprimé		
			

Complexité : là aussi, on ne fait qu'un nombre donné d'affectations, donc aussi en O(1).

Primitives avec parcours de la liste - version itérative

Nombre d'éléments dans la liste

On arrive en fin de liste lorsque le champ suivant du maillon courant est None.
Pour passer d'un maillon au suivant, il suffit de remplacer le maillon courant par son champ suivant, qui contient effectivement la référence vers le maillon suivant, donc la suite de la liste :


	def len(self):
	    if self.is_empty():                 # cas particulier de la liste vide
	        return 0
	    
	    maillon = self.tete                 # référence vers la tête de la liste
	
	    n = 1                               # nombre d'éléments initialisé à 1
	
	    while maillon.suivant is not None:  # tant que l'on n'a pas atteint la fin de la liste...
	        n += 1                          # ... on incrémente de 1 le nombre d'éléments
	        maillon = maillon.suivant       # et on remplace le maillon par sa suite
	
	    return n                            # renvoi du nombre d'éléments
			

Complexité : on parcourt toute la liste, donc en O(n).

Affichage de la liste


    def __str__(self):
        maillon = self.tete                     # référence vers la tête de la liste

        if not self.is_empty():
            t = str(maillon.valeur) + ' '       # nouvelle chaîne, initialisée avec le champ 'valeur' de la tête
            while maillon.suivant is not None:  # tant que l'on n'a pas atteint la fin de la liste...
                maillon = maillon.suivant       # ...on remplace le maillon en cours par son champ 'suivant', c'est à dire la suite de la liste; 'maillon' contient l'élément courant, donc en fin de boucle, le dernier de la liste
                t += str(maillon.valeur) + ' '  # et on ajoute à la chaîne le champ 'valeur' de l'élément courant
        else:
            t = '[]'                            # chaîne renvoyée au cas où la liste est vide

        return t                            # renvoi du résultat
			

Complexité : on parcourt toute la liste, donc en O(n).

Accès à un élément de la liste

On va utiliser le paramètre indice comme compteur, que l'on décrémente à chaque nouveau maillon : quand le compteur est arrivé à 0, on a alors atteint l'indice recherché :


	def get(self, indice):
	    maillon = self.tete
	    
	    while indice != 0:                      # tant que l'on n'a pas atteint l'indice recherché 
	        maillon = maillon.suivant           # on avance d'un maillon
	        if maillon == None:                 # si on est arrivés en fin de liste,
	            return "Index out of range !"   # c'est que l'indice recherché est en dehors de la liste !
	        indice -= 1                         # décrémentation de l'indice/du compteur
	    
	    return maillon.valeur                   # arrivé à l'indice recherché, on renvoie la valeur du maillon courant
        

Complexité : on parcourt toute la liste ( au pire ), donc en O(n).

Insertion à la fin


    def append(self, elt):
        if self.is_empty():                     # si la liste est vide,
            self.tete = Maillon(elt)            # on crée simplement son maillon de tête.
        else:
            maillon = self.tete                 # référence vers la tête de la liste

            while maillon.suivant is not None:  # tant que l'on n'a pas atteint la fin de la liste...
                maillon = maillon.suivant       # ...on remplace le maillon par sa suite.

            maillon.suivant = Maillon(elt)      # création d'un nouveau maillon une fois la fin de liste atteinte
        	

Complexité : on parcourt toute la liste, donc en O(n) même si l'insertion proprement dite se fait en O(1).

Pour lever ce problème, certaines implémentations, en plus de garder une référence vers la tête ( = premier maillon ) gardent également une référence vers le dernier maillon, ce qui fait qu'une insertion en fin de liste peut se faire dans ce cas en O(1).

Insertion en n'importe quelle position

On va se servir là aussi de l'indice de compteur pour "avancer" jusqu'à l'indice recherché, sauf qu'il faut ici "s'arrêter" une position avant pour insérer au "bon endroit" :


    def insert(self, indice, elt):
        if self.is_empty():
            self.tete = Maillon(elt)
        else:
            maillon = self.tete             # référence vers la tête

            while indice != 1:              # parcours de la liste jusqu'à la position indice-1 !
                indice -= 1                 # décrémentation du compteur
                maillon = maillon.suivant   # passage au maillon suivant

            new = Maillon(elt)              # création d'un nouveau maillon
            new.suivant = maillon.suivant   # la suite du nouveau maillon pointe vers la suite du maillon courant ( donc la suite de la liste )
            maillon.suivant = new           # le maillon courant pointe vers le nouveau maillon
        	

On peut là aussi simplifier la création du maillon inséré :


    def insert(self, indice, elt):
        if self.is_empty():
            self.tete = Maillon(elt)
        else:
            maillon = self.tete             # référence vers la tête

            while indice != 1:              # parcours de la liste jusqu'à la position indice-1 !
                indice -= 1                 # décrémentation du compteur
                maillon = maillon.suivant   # passage au maillon suivant

            maillon.suivant = Maillon(elt, maillon.suivant)
        	

Complexité : on parcourt toute la liste ( au pire ), donc en O(n) même si l'insertion proprement dite se fait en O(1).

Suppression d'élément

Suppression à la fin

Dans la boucle de parcours de la liste, le maillon courant est désigné par maillon.suivant; le maillon suivant sera donc désigné par maillon.suivant.suivant ( = la "suite de la suite " ! )

L'avant-dernier maillon est donc atteint quand maillon.suivant.suivant est égal à None.


    def pop_last(self):
        assert not self.is_empty(); "La liste est vide !"
        
		  maillon = self.tete                             # référence vers la tête
		
		  if maillon.suivant is None:                     # si la liste n'a qu'un seul élément,
		      t = maillon.valeur                          # on stocke la valeur de cet élément,
		      self.tete = None                            # et on "vide" la liste.
		  else:
		      while maillon.suivant.suivant is not None:  # parcours de la liste jusqu'à l'avant dernier élément
		          maillon = maillon.suivant               # 'maillon' = élément courant, donc l'avant-dernier de la liste en fin de boucle
		      
		      t = maillon.suivant.valeur                  # on stocke la valeur de l'élément qui va être supprimé
		      maillon.suivant = None                      # on remplace la suite de l'avant-dernier maillon par 'None' : le dernier élément disparaît !
		
		  return t
			
Suppression en un indice quelconque

    def pop(self, indice):
        assert not self.is_empty(); "La liste est vide !"
        
		  maillon = self.tete                             # référence vers la tête
		
		  if maillon.suivant is None:                     # si la liste n'a qu'un seul élément,
		      t = maillon.valeur                          # on stocke la valeur de cet élément,
		      self.tete = None                            # et on "vide" la liste.
		  else:
		      while indice != 1:                          # parcours de la liste jusqu'à la position indice-1
		          indice -= 1
		          maillon = maillon.suivant
		
		      t = maillon.suivant.valeur
		      maillon.suivant = maillon.suivant.suivant   # on remplace la suite du maillon courant par la "suite de sa suite" : l'élément en position n ( = la "suite" ) est donc supprimé
		
		  return t			
			

Version récursive


class Maillon:

    def __init__(self, valeur, suivant = None):
        self.valeur = valeur
        self.suivant = suivant

class ListeRec:

    def __init__(self, tete = None):
        self.tete = tete

    def is_empty(self):
        return self.tete is None

    # Affichage de la liste
    def _str(self, maillon):
        if maillon.suivant is None:
            return str(maillon.valeur)
        return str(maillon.valeur) + ' ' + self._str(maillon.suivant)

    def __str__(self):
        if self.tete is None:
            return '[]'
        return self._str(self.tete)

    # Insertion en tête ( identique à la version itérative )
    def insert_first(self, elt):
        self.tete = Maillon(elt, self.tete)      
            
    # Suppression en tête ( identique à la version itérative )
    def pop_first(self):
        assert not self.is_empty(); "La liste est vide !"
        
        t = self.tete.valeur	        
        self.tete = self.tete.suivant 

        return t								  
    
    # Nombre d'éléments
    def _len(self, maillon):
        if maillon.suivant is None:
            return 1
        return 1 + self._len(maillon.suivant)

    def len(self):
        return self._len(self.tete)

    # Accès à un élément
    def _get(self, indice, maillon):
        if maillon is None:
            return "Index out of range !"
        if indice == 0:
            return maillon.valeur
        return self._get(indice-1, maillon.suivant)

    def get(self, indice):
        return self._get(indice, self.tete)

    # Insertion à la fin
    def _append(self, maillon, elt):
        if maillon.suivant is None:
            maillon.suivant = Maillon(elt)
        else:
            self._append(maillon.suivant, elt)

    def append(self, elt):
        if self.is_empty():
            self.tete = Maillon(elt)
        else:
            self._append(self.tete, elt)

    # Insertion en un indice
    def _insert(self, maillon, indice, elt):
        if indice == 1:
            maillon.suivant = Maillon(elt, maillon.suivant)
        else:
            self._insert(maillon.suivant, indice-1, elt)

    def insert(self, indice, elt):
        if self.is_empty():
            self.tete = Maillon(elt)
        else:
            self._insert(self.tete, indice, elt)

    # Suppression en fin
    def _pop_last(self, maillon):
        if maillon.suivant.suivant is None:
            t = maillon.suivant.valeur
            maillon.suivant = None
            return t
        return self._pop_last(maillon.suivant)

    def pop_last(self):
        assert not self.is_empty(); "La liste est vide !"
        
        maillon = self.tete
		
		  if maillon.suivant is None:
		      t = maillon.valeur
		      self.tete = None
		      return t
		  else:
		      return self._pop_last(maillon)

    # Suppression en un indice
    def _pop(self, maillon, indice):
        if indice == 1:
            t = maillon.suivant.valeur
            maillon.suivant = maillon.suivant.suivant
            return t
        return self._pop(maillon.suivant, indice-1)

    def pop(self, indice):
        assert not self.is_empty(); "La liste est vide !"
        
		  maillon = self.tete
		
		  if maillon.suivant is None:
		      t = maillon.valeur
		      self.tete = None
		      return t
		  else:
		      return self._pop(maillon, indice)
			

Le code complet des classes Liste et ListeRec est ici.

Exercices autour des listes chaînées

La correction de ces exercices est donnée sous forme itérative :

Test d'appartenance


from liste_chainee import Liste, Maillon

def is_in(liste, valeur)->bool:
    maillon = liste.tete
    
    while maillon is not None:       # il faut bien aller jusqu'"après" le dernier élément de la liste !
        if maillon.valeur == valeur:
            return True
        maillon = maillon.suivant
    return False

l = Liste(Maillon(2, Maillon(56, Maillon(6, Maillon(89, Maillon(78))))))
print(is_in(l, 87)) # False
print(is_in(l, 78)) # True
			

Liste Python vers liste chaînée

Une version "naïve" de cette fonction serait de simplement utiliser la méthode append() de la classe Liste écrite précédemment :


def python_2_linked(tab):
    L = Liste()			# liste vide

    for elt in tab:		# pour chaque élément du tableau	
    	L.append(elt)	# on l'insère en fin de liste

    return L			# renvoi de la liste                    
			

C'est d'ailleurs une méthode "standard" pour construire un tableau avec l'implémentation Python des listes ( tableaux dynamiques ).

Mais, dans le cas des listes chaînées :

  • on parcourt les n éléments du tableau
  • pour insérer chacun de ces éléments en fin de liste, on doit donc parcourir celle-ci en totalité, et elle a un élément en plus après chaque insertion !
  • on a donc : 1 + 2 + 3 + ..... + n-1 opérations pour insérer les n éléments, soit un algorithme en O(n²) !

Pour améliorer cette complexité, on peut parcourir le tableau ET la liste en même temps, et ramener ainsi la complexité à O(n) :


def python_2_linked(tab):
    L = Liste()                     # liste vide

    L.tete = Maillon(tab[0])        # premier élément du tableau -> tête de la liste

    maillon = L.tete                # référence vers la tête de la liste

    for i in range(1, len(tab)):    # pour chacun des éléments du tableau, en partant du DEUXIÈME
        new = Maillon(tab[i])       # on créé un nouveau maillon dont la valeur est celle de l'élément du tableau
        maillon.suivant = new       # le maillon courant pointe vers le nouveau maillon

        maillon = maillon.suivant   # le maillon courant = le nouveau maillon ( donc le dernier de la liste )

    return L                        # renvoi de la liste                    
			

Ceci illustre bien un des intérêts de la liste chaînée : une insertion à une position donnée ne coûte "rien"; ici on insère toujours à la fin, sans jamais revenir au début de la liste, c'est donc très efficace !

Occurrences d'une valeur


def occurrences(L, valeur):
    maillon = L.tete                    # référence vers la tête de la liste
    c = 0                               # initialisation d'un compteur d'occurrence

    while maillon.suivant is not None:  # tant que l'on n'a pas atteint la fin de la chaîne
        if maillon.valeur == valeur:    # est-ce que la valeur du maillon courant est égale à la valeur recherchée ?
            c = c + 1                   # si oui, on incrémente le compteur
        maillon = maillon.suivant       # dans tous les cas, on passe au maillon suivant ( donc en dehors de la condition ! )

    return c
    		

Renverser une liste chaînée


def reverse(L):
    L2 = Liste()                    # création d'une nouvelle liste
    m = L.tete                      # référence vers le premier élément
    L2.insert_first(m.valeur)       # on insère cet élément en tête de la nouvelle liste
    while m.suivant is not None:    # puis on parcourt la liste à inverser

        m = m.suivant
        L2.insert_first(m.valeur)   # et on insère EN TÊTE de la nouvelle liste les éléments de la première

    return L2                       # renvoi de la nouvelle liste
			

L'insertion en tête de liste chaînée est toujours en O(1), il est donc tout à fait légitime d'utiliser ici la méthode insert_first().

Concaténer deux listes chaînées

Très simple : on fait pointer le champ suivant du dernier maillon de la première liste vers la tête de la deuxième !


def concatenate(l1, l2):
    maillon = l1.tete                   # référence vers la tête de la liste

    while maillon.suivant is not None:  # tant que l'on n'a pas atteint la fin de la chaîne
        maillon = maillon.suivant       # on passe au maillon suivant

    maillon.suivant = l2.tete           # et une fois le dernier maillon atteint, on fait pointer son champ 'suivant' vers la tête de la deuxième liste

    return l1
			

Insérer dans l'ordre

On part du principe que, quelque soit son nombre d'éléments, la liste est déjà triée. Pour insérer un élément à sa bonne place, il faut donc parcourir la liste depuis sa tête, jusqu'à trouver un élément dont la valeur est supérieure à celle de l'élément à insérer; on insère alors l'élément AVANT cette position.

Pour cela, il faut donc, tester la valeur du maillon SUIVANT le maillon courant à chaque étape du parcours ( en commençant bien sûr par un test sur la valeur du maillon de tête ! )


def insert_sort(L, elt):

    if L.is_empty():            # insertion dans une liste vide
        L.tete = Maillon(elt)   # on ne fait que créer une nouvelle tête

    elif L.tete.valeur > elt:   # insertion EN TETE de liste
        new = Maillon(elt)
        new.suivant = L.tete    # dans ce cas, on insère l'élément AVANT cette tête
        L.tete = new            # l'élément inséré devient donc la nouvelle tête
    else:
        maillon = L.tete        # dans les autres cas, on parcourt la liste...

        while maillon.suivant is not None and maillon.suivant.valeur < elt : #...tant que l'on n' pas trouvé la "bonne" position d'insertion
            maillon = maillon.suivant

        if maillon is None:                 # si on  est alors arrivé APRÈS le dernier maillon,
            maillon.suivant = Maillon(elt)  # alors on insère EN FIN de liste
        else:
            new = Maillon(elt)              # sinon on insère à la position courante
            new.suivant = maillon.suivant
            maillon.suivant = new
			

Tri insertion d'une liste chaînée

Maintenant que l'on dispose d'une fonction insérant automatiquement un élément à sa "bonne" place, c'est un jeu d'enfant de créer une fonction de tri insertion.

Si vous ne voyez pas tout de suite pourquoi cela va être facile, peut-être faut-il revoir le principe du tri par insertion ? 😎


def tri_insertion(L):
    L_triee = Liste()                       # nouvelle liste vide

    maillon = L.tete                        # référence vers la tête de a liste à trier
    insert_sort(L_triee, maillon.valeur)    # on insère la tête de liste

    while maillon.suivant is not None:      # tant que l'on n'est pas arrivé à la fin de la liste à trier
        maillon = maillon.suivant           # on passe au maillon suivant
        insert_sort(L_triee, maillon.valeur)# et on insère sa valeur à la bonne place dans la liste triée

    return L_triee                          # renvoi de la liste triée
   		

Quelques fonctions en Lisp

Fonctions simples

Produit


(defun produit(a b)
    (* a b)
    )

(write (produit 12 5)) # 60
   		

Bissext

Ici, les opérations logiques se font à l'aide de fonctions : elles doivent donc être en tête de liste dans une expression booléenne en Lisp :


(defun bissext(n)
    (if (or (and (= (mod N 4) 0) (/= (mod N 100) 0)) (= (mod N 400) 0))
        T
        NIL
    )
    )

(write (bissext 2023)) ; NIL
(write (bissext 2016)) ; T
   		

Somme


(defun somme(n)
    (if (= N 0)
        0
        (+ N (somme (- N 1)))
        )
    )

(write (somme 10)) ; 55
   		

Fonctions sur les listes

somme-liste


(defun somme-list (L)
  (if (null L)
      0
    (+ (first L) (somme-list (rest L)))
    )
  )

(setf l '(12 56 3 7 89 15))
(write (somme-list l)) ; 182
   		

len-liste


(defun len-list(L)
    (if (null L)
        0
        (+ 1 (len-list (rest L)))
        )
    )

(setf l '(12 56 3 7 89 15))
(write (len-list l)) ; 6
   		

get-element


(defun get-element(L indice)
    (if (null L)
        nil
        (if (= indice 0)
            (first L)
            (get-element (rest L) (- indice 1))
            )
        )
    )

(setf l '(12 56 3 7 89 15))
(write (get-element l 3)) ; 7