Created
June 9, 2022 12:36
-
-
Save rstropek/9b78601f56c8990a127e002e488c06c7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"reflect" | |
"golang.org/x/exp/constraints" | |
) | |
type lentil struct { | |
isGood bool | |
} | |
func (l lentil) shouldEat() bool { return !l.isGood } | |
type snail struct { | |
hasHouse bool | |
} | |
func (s snail) shouldEat() bool { return !s.hasHouse } | |
type eatOrKeep interface { | |
shouldEat() bool | |
} | |
type bird struct{} | |
func (b bird) process(items []eatOrKeep) []eatOrKeep { | |
result := []eatOrKeep{} | |
for _, item := range items { | |
if !item.shouldEat() { | |
result = append(result, item) | |
} | |
} | |
return result | |
} | |
func processInterface(itemsSlice interface{}, filter interface{}) interface{} { | |
items := reflect.ValueOf(itemsSlice) | |
result := reflect.MakeSlice(items.Type(), 0, 0) | |
funcValue := reflect.ValueOf(filter) | |
for i := 0; i < items.Len(); i++ { | |
item := items.Index(i) | |
keep := funcValue.Call([]reflect.Value{item})[0].Bool() | |
if keep { | |
result = reflect.Append(result, item) | |
} | |
} | |
return result.Interface() | |
} | |
func process[I any](items []I, filter func(i I) bool) []I { | |
result := []I{} | |
for _, item := range items { | |
if filter(item) { | |
result = append(result, item) | |
} | |
} | |
return result | |
} | |
type itemGroup[T any] struct { | |
item T | |
count int | |
} | |
type itemsBag[T any] struct { | |
bag []itemGroup[T] | |
equalityComparer func(T, T) bool | |
} | |
func newItemsBag[T any](comparer func(T, T) bool) *itemsBag[T] { | |
return &itemsBag[T]{ | |
bag: make([]itemGroup[T], 0), | |
equalityComparer: comparer, | |
} | |
} | |
func (b *itemsBag[T]) append(item T) { | |
if len(b.bag) == 0 || !b.equalityComparer(item, b.bag[len(b.bag)-1].item) { | |
b.bag = append(b.bag, itemGroup[T]{item: item, count: 1}) | |
} else { | |
b.bag[len(b.bag)-1].count++ | |
} | |
} | |
func (b itemsBag[T]) getItems() []T { | |
result := []T{} | |
for _, item := range b.bag { | |
for i := 0; i < item.count; i++ { | |
result = append(result, item.item) | |
} | |
} | |
return result | |
} | |
func processChannel[I any](items <-chan I, filter func(i I) bool) <-chan I { | |
out := make(chan I) | |
go func() { | |
defer close(out) | |
for item := range items { | |
if filter(item) { | |
out <- item | |
} | |
} | |
}() | |
return out | |
} | |
const ( | |
SMALL = 1 | |
MEDIUM = 2 | |
LARGE = 3 | |
) | |
// Lentil with size information | |
type sizedLentil struct { | |
lentil | |
lentilSize int | |
} | |
func (l sizedLentil) size() int { return l.lentilSize } | |
type sized interface { | |
size() int | |
} | |
// Define an interface that will be used as a type constraint | |
type sizedEatOrKeep interface { | |
sized | |
eatOrKeep | |
} | |
func processAndSort[I sizedEatOrKeep](items []I, filter func(i I) bool) []I { | |
result := []I{} | |
for _, item := range items { | |
if filter(item) { | |
result = append(result, item) | |
} | |
} | |
bubblesort(result, func(item I) int { return item.size() }) | |
return result | |
} | |
func bubblesort[I any, O constraints.Ordered](items []I, toOrdered func(item I) O) { | |
for itemCount := len(items) - 1; ; itemCount-- { | |
hasChanged := false | |
for index := 0; index < itemCount; index++ { | |
// We use the provided function to turn each item into a type compatible | |
// with Ordered. With that, we can use comparison operators. | |
if toOrdered(items[index]) > toOrdered(items[index+1]) { | |
items[index], items[index+1] = items[index+1], items[index] | |
hasChanged = true | |
} | |
} | |
if !hasChanged { | |
break | |
} | |
} | |
} | |
func main() { | |
bag := newItemsBag(func(lhs eatOrKeep, rhs eatOrKeep) bool { return lhs.shouldEat() == rhs.shouldEat() }) | |
bag.append(lentil{isGood: true}) | |
bag.append(lentil{isGood: true}) | |
bag.append(snail{hasHouse: false}) | |
bag.append(snail{hasHouse: false}) | |
processedItems := process(bag.getItems(), func(item eatOrKeep) bool { return !item.shouldEat() }) | |
fmt.Println("Eaten:", 4-len(processedItems), "Kept:", len(processedItems)) | |
in := make(chan eatOrKeep, 4) | |
in <- lentil{isGood: true} | |
in <- lentil{isGood: true} | |
in <- snail{hasHouse: false} | |
in <- snail{hasHouse: false} | |
close(in) | |
total := len(in) | |
remaining := 0 | |
asyncProcessedItems := processChannel(in, func(item eatOrKeep) bool { return !item.shouldEat() }) | |
for range asyncProcessedItems { | |
remaining++ | |
} | |
fmt.Println("Eaten:", total-remaining, "Kept:", remaining) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment