Module collection (1)

This commit is contained in:
Benjamin VAUDOUR 2023-09-23 15:39:05 +02:00
parent a524d1d0dc
commit 485fca69ec
6 changed files with 629 additions and 0 deletions

54
collection/chain.go Normal file
View File

@ -0,0 +1,54 @@
package collection
import (
. "gitea.zaclys.com/bvaudour/gob/option"
)
type chain[T any] struct {
value T
next *chain[T]
}
func prevChain[T any](value T, next *chain[T]) *chain[T] {
return &chain[T]{
value: value,
next: next,
}
}
func nextChain[T any](value T, prev Option[*chain[T]]) *chain[T] {
c := &chain[T]{
value: value,
}
if p, ok := prev.Get(); ok {
p.next = c
}
return c
}
type dualChain[T any] struct {
value T
prev *dualChain[T]
next *dualChain[T]
}
func newDual[T any](e T) *dualChain[T] {
return &dualChain[T]{
value: e,
}
}
func link[T any](o1, o2 Option[*dualChain[T]]) {
if c1, ok := o1.Get(); ok {
if c2, ok := o2.Get(); ok {
c1.next = c2
c2.prev = c1
} else {
c1.next = nil
}
} else if c2, ok := o2.Get(); ok {
c2.prev = nil
}
}

125
collection/list.go Normal file
View File

@ -0,0 +1,125 @@
package collection
import (
. "gitea.zaclys.com/bvaudour/gob/option"
)
// List représente une collection délément qui peut être modifiée (ajout/suppression) par chaque bout.
// Elle peut donc se comporter à la fois comme une pile et une file.
type List[T any] struct {
first *dualChain[T]
last *dualChain[T]
size uint
}
// Len retourne le nombre déléments de la liste.
func (l *List[T]) Len() uint {
return l.size
}
func (l *List[T]) push_front(e T) {
var (
c = newDual(e)
oldFirst Option[*dualChain[T]]
)
if l.size == 0 {
l.last = c
} else {
oldFirst = Some(l.first)
}
link(Some(c), oldFirst)
l.first = c
l.size++
}
func (l *List[T]) push_back(e T) {
var (
c = newDual(e)
oldLast Option[*dualChain[T]]
)
if l.size == 0 {
l.first = c
} else {
oldLast = Some(l.last)
}
link(oldLast, Some(c))
l.last = c
l.size++
}
// PushFront ajoute tous les éléments dentrée en début de liste.
func (l *List[T]) PushFront(elems ...T) {
for _, e := range elems {
l.push_front(e)
}
}
// PushBack ajoute tous les éléments dentrée en fin de liste.
func (l *List[T]) PushBack(elems ...T) {
for _, e := range elems {
l.push_back(e)
}
}
// PopFront supprime le premier élément de la liste.
func (l *List[T]) PopFront() (e Option[T]) {
if l.size > 0 {
e = Some(l.first.value)
l.first = l.first.next
l.size--
link(None[*dualChain[T]](), Some(l.first))
if l.size == 0 {
l.last = l.first
}
}
return
}
// PopLast supprime le dernier élément de la liste.
func (l *List[T]) PopLast() (e Option[T]) {
if l.size > 0 {
e = Some(l.last.value)
l.last = l.last.prev
l.size--
link(Some(l.last), None[*dualChain[T]]())
if l.size == 0 {
l.first = l.last
}
}
return
}
// Concatenate concatène deux listes.
func (q1 *List[T]) Concatenate(l2 *List[T]) {
c, l := l2.first, l2.size
for i := uint(0); i < l; i++ {
q1.push_back(c.value)
c = c.next
}
}
// ToSlice convertit une liste en slice.
func (l *List[T]) ToSlice() (sl []T) {
sl = make([]T, l.size)
c := l.first
for i := uint(0); i < l.size; i++ {
sl[i] = c.value
c = c.next
}
return
}
// NewList retourne une liste initialisée à partir des données dentrées.
func NewList[T any](elems ...T) (l *List[T]) {
l = new(List[T])
l.PushBack(elems...)
return
}

95
collection/queue.go Normal file
View File

@ -0,0 +1,95 @@
package collection
import (
. "gitea.zaclys.com/bvaudour/gob/option"
)
// Queue représente une file délément.
// Une file est une collection où les éléments suivant la logique “premier arrivé, premier servi“.
type Queue[T any] struct {
first *chain[T]
last *chain[T]
size uint
}
// Len retourne le nombre déléments de la file.
func (q Queue[T]) Len() uint {
return q.size
}
func (q *Queue[T]) push(e T) {
var (
isEmpty = q.size == 0
oldLast Option[*chain[T]]
)
if !isEmpty {
oldLast = Some(q.last)
}
q.last = nextChain(e, oldLast)
if isEmpty {
q.first = q.last
}
q.size++
}
// Push ajoute tous les éléments dentrée dans la file.
func (q *Queue[T]) Push(elems ...T) {
for _, e := range elems {
q.push(e)
}
}
// Concatenate concatène deux files.
func (q1 *Queue[T]) Concatenate(q2 *Queue[T]) {
c, l := q2.first, q2.size
for i := uint(0); i < l; i++ {
q1.push(c.value)
c = c.next
}
}
// Pop dépile le premier élément de la file et le retourne.
func (q *Queue[T]) Pop() (e Option[T]) {
if q.size > 0 {
e = Some(q.first.value)
q.first = q.first.next
q.size--
if q.size == 0 {
q.last = q.first
}
}
return
}
// ToSlice convertit une file en slice.
func (q *Queue[T]) ToSlice() (sl []T) {
sl = make([]T, q.size)
c := q.first
for i := uint(0); i < q.size; i++ {
sl[i] = c.value
c = c.next
}
return
}
// ToStack convertit une file en pile.
func (q *Queue[T]) ToStack() (s *Stack[T]) {
s = &Stack[T]{
first: q.first,
size: q.size,
}
return
}
// NewQueue retourne une file initialisée à partir des données dentrées.
func NewQueue[T any](elems ...T) (q *Stack[T]) {
q = new(Stack[T])
q.Push(elems...)
return
}

78
collection/set.go Normal file
View File

@ -0,0 +1,78 @@
package collection
var empty struct{}
// Set est une collection de valeurs uniques.
type Set[T comparable] map[T]struct{}
// Add ajoute les valeurs en entrée dans le set et retourne le set lui-même.
func (s Set[T]) Add(in ...T) Set[T] {
for _, e := range in {
s[e] = empty
}
return s
}
// Del supprime les valeurs données en entrées du set et retourne le set lui-même.
func (s Set[T]) Del(in ...T) Set[T] {
for _, e := range in {
delete(s, e)
}
return s
}
// Contains retourne vrai si le set contient la valeur d'entrée.
func (s Set[T]) Contains(in T) (out bool) {
_, out = s[in]
return
}
// ContainsOneOf retourne vrai si le set contient au moins une des valeurs dentrée.
func (s Set[T]) ContainsOneOf(in ...T) bool {
for _, e := range in {
if s.Contains(e) {
return true
}
}
return false
}
// ContainsAll retourne vrai si le set contient toutes les valeurs dentrée.
func (s Set[T]) ContainsAll(in ...T) bool {
for _, e := range in {
if !s.Contains(e) {
return false
}
}
return true
}
// Len retourne le nombre déléments du set.
func (s Set[T]) Len() int {
return len(s)
}
// ToSlice convertit un set en slice d'éléments de même type.
func (s Set[T]) ToSlice() (out []T) {
out = make([]T, len(s))
i := 0
for e := range s {
out[i] = e
i++
}
return
}
// NewSet retourne un set initialisé avec les valeurs d'entrée.
func NewSet[T comparable](in ...T) (out Set[T]) {
out = make(Set[T])
out.Add(in...)
return
}

205
collection/slice.go Normal file
View File

@ -0,0 +1,205 @@
package collection
// Slice crée un slice générique contenant les valeurs dentrée.
func Slice[T any](in ...T) (out []any) {
out = make([]any, len(in))
for i, e := range in {
out[i] = e
}
return
}
// Add ajoute les valeurs dentrée en fin du slice.
func Add[T any](s *([]T), in ...T) {
*s = append(*s, in...)
}
// Insert ajoute les valeurs dentrée en début du slice.
func Insert[T any](s *([]T), in ...T) {
*s = append(in, (*s)...)
}
// InsertAt ajoute les valeurs dentrée à lindex i du slice.
// - Si lindex est inférieur à 0, agit comme Insert.
// - Si lindex est supérieur ou égal à la taille du slice, agit comme Add.
func InsertAt[T any](s *([]T), i int, in ...T) {
if i <= 0 {
Insert(s, in...)
return
} else if i >= len(*s) {
Add(s, in...)
return
}
l1, l2 := len(*s), len(in)
result := make([]T, l1+l2)
copy(result[:i], (*s)[:i])
copy(result[i:i+l2], in)
copy(result[i+l2:], (*s)[i:])
*s = result
}
// Diff retourne un slice contenant les entrées de s1 qui ne sont pas dans s2.
func Diff[T comparable](s1, s2 []T) (out []T) {
ss := NewSet(s2...)
for _, e := range s1 {
if !ss.Contains(e) {
Add(&out, e)
}
}
return
}
// Intersect retourne un slice contenant les entrées contenues à la fois dans s1 et s2.
func Intersect[T comparable](s1, s2 []T) (out []T) {
ss := NewSet(s2...)
for _, e := range s1 {
if ss.Contains(e) {
Add(&out, e)
}
}
return
}
// Uniq supprime les valeurs redondantes du slice.
func Uniq[T comparable](s []T) (out []T) {
ss := NewSet[T]()
for _, e := range s {
if !ss.Contains(e) {
ss.Add(e)
Add(&out, e)
}
}
return
}
// AddUniq ajoute les entrées en fin de slice si elles nexistent pas dans le slice.
func AddUniq[T comparable](s *([]T), in ...T) {
in2 := Uniq(Diff(in, *s))
Add(s, in2...)
}
// InsertUniq ajoute les entrées en début de slice si elles nexistent pas dans le slice.
func InsertUniq[T comparable](s *([]T), in ...T) {
in2 := Uniq(Diff(in, *s))
Insert(s, in2...)
}
// InsertAtUniq ajoute les entrées dans le slice à la position donnée si elles nexistent pas dans le slice.
func InsertAtUniq[T comparable](s *([]T), i int, in ...T) {
in2 := Uniq(Diff(in, *s))
InsertAt(s, i, in2...)
}
// Contains retourne vrai si le slice contient la valeur dentrée.
func Contains[T comparable](s []T, in T) bool {
for _, e := range s {
if e == in {
return true
}
}
return false
}
// ContainsOneOf retourne vrai si le slice contient une des valeurs dentrée.
func ContainsOneOf[T comparable](s []T, in ...T) bool {
return NewSet(s...).ContainsOneOf(in...)
}
// ContainsAll retourne vrai si le slice contient toutes les valeurs dentrée.
func ContainsAll[T comparable](s []T, in ...T) bool {
return NewSet(s...).ContainsAll(in...)
}
// Maps retourne un slice à partir dun autre slice en appliquant une fonction de transformation pour chaque valeur.
func Maps[T1, T2 any](s []T1, f func(T1) T2) (out []T2) {
out = make([]T2, len(s))
for i, e := range s {
out[i] = f(e)
}
return
}
// Filter retourne un nouveau slice dont les valeurs passent la fonction.
func Filter[T any](s []T, f func(T) bool) (out []T) {
for _, e := range s {
if f(e) {
Add(&out, e)
}
}
return
}
// Reduce applique une fonction à tous les éléments dont le résultat sert à la prochaine itération.
func Reduce[T any](s []T, f func(T, T) T, initial ...T) (out T) {
if len(initial) > 0 {
out = initial[0]
}
for _, e := range s {
out = f(out, e)
}
return
}
// Reverse inverse lordre du slice.
func Reverse[T any](s []T) (out []T) {
l := len(s)
out = make([]T, l)
for i, e := range s {
out[l-1-i] = e
}
return
}
// Zip retourne une map dont les clés sont les valeurs du premier slice et les valeurs, les valeurs du second.
func Zip[K comparable, V any](s1 []K, s2 []V) (out map[K]V) {
out = make(map[K]V)
l := min(len(s1), len(s2))
for i, k := range s1[:l] {
out[k] = s2[i]
}
return
}
// Unzip sépare les clés et les valeurs de la map en 2 slices distincts.
func Unzip[K comparable, V any](m map[K]V) (keys []K, values []V) {
for k, v := range m {
Add(&keys, k)
Add(&values, v)
}
return
}
// Equal retourne vrai si les slices sont identiques.
func Equal[T comparable](sl1, sl2 []T) bool {
if len(sl1) != len(sl2) {
return false
}
for i, e1 := range sl1 {
if e1 != sl2[i] {
return false
}
}
return true
}

72
collection/stack.go Normal file
View File

@ -0,0 +1,72 @@
package collection
import (
. "gitea.zaclys.com/bvaudour/gob/option"
)
// Stack représente une pile délément.
// Une pile est une collection où les éléments suivant la logique “dernier arrivé, premier servi“.
type Stack[T any] struct {
first *chain[T]
size uint
}
// Len retourne le nombre déléments de la pile.
func (s Stack[T]) Len() uint {
return s.size
}
// Push ajoute tous les éléments dentrée dans la pile.
func (s *Stack[T]) Push(elems ...T) {
for _, e := range elems {
s.first = prevChain(e, s.first)
s.size++
}
}
// Pop dépile le premier élément de la pile et le retourne.
func (s *Stack[T]) Pop() (e Option[T]) {
if s.size > 0 {
e = Some(s.first.value)
s.first = s.first.next
s.size--
}
return
}
// ToSlice convertit une pile en slice.
func (s *Stack[T]) ToSlice() (sl []T) {
sl = make([]T, s.size)
c := s.first
for i := uint(0); i < s.size; i++ {
sl[i] = c.value
c = c.next
}
return
}
// ToQueue convertit une pile en file.
func (s *Stack[T]) ToQueue() (q *Queue[T]) {
q = &Queue[T]{
first: s.first,
size: s.size,
}
var c = s.first
for i := uint(1); i < s.size; i++ {
c = c.next
}
q.last = c
return
}
// NewStack retourne une pile initialisée à partir des données dentrées.
func NewStack[T any](elems ...T) (s *Stack[T]) {
s = new(Stack[T])
s.Push(elems...)
return
}