Module collection (1)
This commit is contained in:
parent
a524d1d0dc
commit
485fca69ec
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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 d’entré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 d’entré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 d’entrées.
|
||||
func NewList[T any](elems ...T) (l *List[T]) {
|
||||
l = new(List[T])
|
||||
l.PushBack(elems...)
|
||||
|
||||
return
|
||||
}
|
|
@ -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 d’entré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 d’entrées.
|
||||
func NewQueue[T any](elems ...T) (q *Stack[T]) {
|
||||
q = new(Stack[T])
|
||||
q.Push(elems...)
|
||||
|
||||
return
|
||||
}
|
|
@ -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 d’entré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 d’entré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
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
package collection
|
||||
|
||||
// Slice crée un slice générique contenant les valeurs d’entré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 d’entrée en fin du slice.
|
||||
func Add[T any](s *([]T), in ...T) {
|
||||
*s = append(*s, in...)
|
||||
}
|
||||
|
||||
// Insert ajoute les valeurs d’entrée en début du slice.
|
||||
func Insert[T any](s *([]T), in ...T) {
|
||||
*s = append(in, (*s)...)
|
||||
}
|
||||
|
||||
// InsertAt ajoute les valeurs d’entrée à l’index i du slice.
|
||||
// - Si l’index est inférieur à 0, agit comme Insert.
|
||||
// - Si l’index 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 n’existent 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 n’existent 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 n’existent 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 d’entré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 d’entré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 d’entrée.
|
||||
func ContainsAll[T comparable](s []T, in ...T) bool {
|
||||
return NewSet(s...).ContainsAll(in...)
|
||||
}
|
||||
|
||||
// Maps retourne un slice à partir d’un 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 l’ordre 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
|
||||
}
|
|
@ -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 d’entré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 d’entrées.
|
||||
func NewStack[T any](elems ...T) (s *Stack[T]) {
|
||||
s = new(Stack[T])
|
||||
s.Push(elems...)
|
||||
|
||||
return
|
||||
}
|
Loading…
Reference in New Issue