Comment fonctionne une liste ?
Romain Berthon
#JobHacker
@RomainTrm
romainberthon.blog

Au programme
- Définir une liste
- Coder une liste
- Les listes C# et F#
- Volumétrie
Définir une liste
Aparté : récursivité
// int -> int
let factorial n =
// int -> int
let rec factorial n =
// Exit pattern that ends the recursion
if n <= 1 then
1
// Recursion process, apply and move to next element
else
n * factorial (n - 1)
> factorial -1;;
val it : int = 1
> factorial 1;;
val it : int = 1
> factorial 3;;
val it : int = 6
// int -> int
let rec factorial n =
// Recursion process, apply and move to next element
n * factorial (n - 1)
Une définition mathématique
List a = Nil | Cons a (List a)
Une liste d'éléments a est :
une liste vide
OU
un élément a (head) ET une liste d'éléments a (tail).
Alonzo Church - Lambda Calculus
Une définition mathématique
Exemple avec une liste [1; 5; 3] :
1
5
3
Nil
List a = Nil | Cons a (List a)
Cons 1 (
Cons 5 (
Cons 3 (
Nil )))
Coder une liste
Coder une liste
La liste
public class LinkedList<T> : IEquatable<LinkedList<T>>
{
public bool IsEmpty { get; }
public T Head { get; }
public LinkedList<T> Tail { get; }
public LinkedList()
{
IsEmpty = true;
}
public LinkedList(T head, LinkedList<T> tail)
{
Head = head;
Tail = tail;
}
// Equatable pattern [...]
}
Coder une liste
La liste
// [1; 5; 3]
var list = new LinkedList<int>(1,
new LinkedList<int>(5,
new LinkedList<int>(3,
new LinkedList<int>())))
1
5
3
Nil
Cons 1 (
Cons 5 (
Cons 3 (
Nil )))
Coder une liste
La méthode Map
[Test]
public void MapElements()
{
LinkedList<int> list = // [1; 2]
var result = list.Map(i => i.ToString());
LinkedList<string> expectedResult = // ["1"; "2"]
Assert.AreEqual(expectedResult, result);
}
Coder une liste
La méthode Map
public static LinkedList<TOutput> Map<TInput, TOutput>(
this LinkedList<TInput> list,
Func<TInput, TOutput> morphism)
{
// List is empty : we are at the end of the tail, return an empty list
if (list.IsEmpty) return new LinkedList<TOutput>();
}
public static LinkedList<TOutput> Map<TInput, TOutput>(
this LinkedList<TInput> list,
Func<TInput, TOutput> morphism)
{
}
public static LinkedList<TOutput> Map<TInput, TOutput>(
this LinkedList<TInput> list,
Func<TInput, TOutput> morphism)
{
// List is empty : we are at the end of the tail, return an empty list
if (list.IsEmpty) return new LinkedList<TOutput>();
// We apply morphism to head and apply Map to the tail
// Then return a new list
return new LinkedList<TOutput>(
morphism(list.Head),
Map(list.Tail, morphism));
}
Coder une liste
La méthode Filter
[Test]
public void FilterElements()
{
LinkedList<int> list = // [1; 2; 3]
var result = list.Filter(i => i % 2 == 1);
LinkedList<int> expectedResult = // [1; 3]
Assert.AreEqual(expectedResult, result);
}
Coder une liste
La méthode Filter
public static LinkedList<T> Filter<T>(
this LinkedList<T> list,
Func<T, bool> predicate)
{
// List is empty : we are at the end of the tail, return an empty list
if (list.IsEmpty) return list;
var headMatchPredicate = predicate(list.Head);
var filteredTail = Filter(list.Tail, predicate);
if (headMatchPredicate)
// Head matches predicate
// We return a new list with head and the filtered tail
return new LinkedList<T>(list.Head, filteredTail);
// Head doesn't match predicate, we only return the filtered tail
return filteredTail;
}
public static LinkedList<T> Filter<T>(
this LinkedList<T> list,
Func<T, bool> predicate)
{
// List is empty : we are at the end of the tail, return an empty list
if (list.IsEmpty) return list;
var headMatchPredicate = predicate(list.Head);
var filteredTail = Filter(list.Tail, predicate);
if (headMatchPredicate)
// Head matches predicate
// We return a new list with head and the filtered tail
return new LinkedList<T>(list.Head, filteredTail);
}
public static LinkedList<T> Filter<T>(
this LinkedList<T> list,
Func<T, bool> predicate)
{
// List is empty : we are at the end of the tail, return an empty list
if (list.IsEmpty) return list;
}
public static LinkedList<T> Filter<T>(
this LinkedList<T> list,
Func<T, bool> predicate)
{
}
public static LinkedList<T> Filter<T>(
this LinkedList<T> list,
Func<T, bool> predicate)
{
// List is empty : we are at the end of the tail, return an empty list
if (list.IsEmpty) return list;
var headMatchPredicate = predicate(list.Head);
var filteredTail = Filter(list.Tail, predicate);
}
Les listes C# et F#
Linked list
- Immutable
- Taille fixe
- On ne peut pas remplacer un élément
C# List
- Mutable
- Taille variable
- On peut remplacer un élément
- Objet .Net qui encapsule le type C# Array
Avec GetEnumerator()
using System.Collections.Generic;
public static List<TOutput> Map<TInput, TOutput>(
this List<TInput> list,
Func<TInput, TOutput> morphism)
{
IEnumerable<TOutput> Map(List<TInput>.Enumerator enumerator)
{
if (!enumerator.MoveNext())
return Enumerable.Empty<TOutput>();
var head = morphism(enumerator.Current);
var tail = Map(enumerator);
return new[] { head }.Concat(tail);
}
return Map(list.GetEnumerator()).ToList();
}
Sources dotnet System.Collections.Generic.List<> : https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs
Avec un langage fonctionnel
L'opérateur "cons" ::
> [1; 5; 3] = 1::[5; 3];;
val it : bool = true
> [1; 5; 3] = 1::[5; 3];;
val it : bool = true
> [1; 5; 3] = 1::5::[3];;
val it : bool = true
> [1; 5; 3] = 1::5::3::[];;
val it : bool = true
>
:: of Head : 'T * Tail : 'T list
// Head::Tail
:: en F# ou LISP, : en Haskell
Avec un langage fonctionnel
La fonction map
// ('a -> 'b) -> 'a list -> 'b list
let rec map morphism list =
match list with
| [] -> []
| head::tail -> (morphism head)::(map morphism tail)
> map string [1; 5; 3];;
val it : string list = ["1"; "5"; "3"]
>
Volumétrie
Volumétrie
simpleSum
list = [10000]
10000 + ?
simpleSum
list = [ ]
0
simpleSum
list = [2.. ]
2 + ?
simpleSum
list = [1.. ]
1 + ?
...
// int list -> int
let rec simpleSum list =
match list with
| [] -> 0
| head::tail -> head + simpleSum tail
> simpleSum [1..10000];;
> simpleSum [1..10000];;
val it : int = 50005000
simpleSum
list = [10000]
10000 + 0
simpleSum
list = [2.. ]
2 + 50004997
simpleSum
list = [1.. ]
1 + 50004999
...
Volumétrie
> simpleSum [1..100000];;
simpleSum
list = [64201.. ]
64201 + ?
stack limit
simpleSum
list = [2.. ]
2 + ?
simpleSum
list = [1.. ]
1 + ?
...
// int list -> int
let rec simpleSum list =
match list with
| [] -> 0
| head::tail -> head + simpleSum tail
> simpleSum [1..100000];;
// -> StackOverflowException
Volumétrie : Tail recursion
> sum [1..100000];;
val it : int = 705082704
sum
list = [100000]
total = 704982704
sum
list = [ ]
total = 705082704
sum
list = [2.. ]
total = 1
sum
list = [1.. ]
total = 0
...
// int list -> int
let sum list =
let rec sumUtil total list =
match list with
| [] -> total
| head::tail -> sumUtil (total + head) tail
sumUtil 0 list
> sum [1..100000];;
Merci !
Romain Berthon
#JobHacker
@RomainTrm
romainberthon.blog



Comment fonctionne une liste ?
By Romain Berthon
Comment fonctionne une liste ?
- 227