golang实现惰性求值与函数式数组操作的插件库koazee的使用

Golang实现惰性求值与函数式数组操作的插件库Koazee的使用

Koazee是一个StreamLike、不可变、惰性加载且智能的Golang库,用于处理切片(slices)。它提供了类似函数式编程的操作方式,同时保持高性能。

Koazee特点

  • 不可变(Immutable): Koazee不会修改输入数据
  • 流式操作(StreamLike): 可以方便地组合多个操作
  • 惰性加载(Lazy loading): 操作在真正需要时才会执行
  • 通用(Generic): 可以处理任何类型的切片,无需创建自定义函数
  • 高性能(Focusing on performance): 实现新操作时的首要考虑是提供最佳性能

安装

使用Go modules安装:

module github.com/me/project
require ( 
  github.com/wesovilabs/koazee vX.Y.Z
)

或者使用go get:

go get github.com/wesovilabs/koazee

基本使用示例

创建流

package main

import (
	"fmt"
	"github.com/wesovilabs/koazee"
)

var numbers = []int{1, 5, 4, 3, 2, 7, 1, 8, 2, 3}

func main() {
	fmt.Printf("slice: %v\n", numbers)
	stream := koazee.StreamOf(numbers)
	fmt.Printf("stream: %v\n", stream.Out().Val())
}

/**
输出:
slice: [1 5 4 3 2 7 1 8 2 3]
stream: [1 5 4 3 2 7 1 8 2 3]
*/

元素访问操作

package main

import (
	"fmt"
	"github.com/wesovilabs/koazee"
)

var numbers = []int{1, 5, 4, 3, 2, 7, 1, 8, 2, 3}

func main() {
	stream := koazee.StreamOf(numbers)
	fmt.Printf("stream.At(4): %d\n", stream.At(4).Int())
	fmt.Printf("stream.First: %d\n", stream.First().Int())
	fmt.Printf("stream.Last: %d\n", stream.Last().Int())
}

/**
输出:
stream.At(4): 2
stream.First: 1
stream.Last: 3
*/

添加/删除元素

package main

import (
	"fmt"
	"github.com/wesovilabs/koazee"
)

var numbers = []int{1, 5, 4, 3, 2, 7, 1, 8, 2, 3}

func main() {
	fmt.Printf("input: %v\n", numbers)

	stream := koazee.StreamOf(numbers)
	fmt.Print("stream.Add(10): ")
	fmt.Println(stream.Add(10).Do().Out().Val())

	fmt.Print("stream.Drop(5): ")
	fmt.Println(stream.Drop(5).Do().Out().Val())
	
	fmt.Print("stream.DropWhile(val<=5): ")
	fmt.Println(stream.DropWhile(func(element int)bool{return element<=5}).Do().Out().Val())

	fmt.Print("stream.DeleteAt(4): ")
	fmt.Println(stream.DeleteAt(4).Do().Out().Val())

	fmt.Print("stream.Set(0,5): ")
	fmt.Println(stream.Set(0, 5).Do().Out().Val())

	fmt.Print("stream.Pop(): ")
	val, newStream := stream.Pop()
	fmt.Printf("%d ... ", val.Int())
	fmt.Println(newStream.Out().Val())
}

/**
输出:
input: [1 5 4 3 2 7 1 8 2 3]
stream.Add(10): [1 5 4 3 2 7 1 8 2 3 10]
stream.Drop(5): [1 4 3 2 7 1 8 2 3]
stream.DropWhile(val<=5): [7 8]
stream.DeleteAt(4): [1 5 4 3 7 1 8 2 3]
stream.Set(0,5): [5 5 4 3 2 7 1 8 2 3]
stream.Pop(): 1 ... [5 4 3 2 7 1 8 2 3]
*/

元素信息查询

package main

import (
	"fmt"
	"github.com/wesovilabs/koazee"
)

var numbers = []int{1, 5, 4, 3, 2, 7, 1, 8, 2, 3}

func main() {
	fmt.Printf("input: %v\n", numbers)
	stream := koazee.StreamOf(numbers)
	count, _ := stream.Count()
	fmt.Printf("stream.Count(): %d\n", count)
	index, _ := stream.IndexOf(2)
	fmt.Printf("stream.IndexOf(2): %d\n", index)
	indexes, _ := stream.IndexesOf(2)
    fmt.Printf("stream.IndexesOf(2): %d\n", indexes)
	index, _ = stream.LastIndexOf(2)
	fmt.Printf("stream.LastIndexOf(2): %d\n", index)
	contains, _ := stream.Contains(7)
	fmt.Printf("stream.Contains(7): %v\n", contains)
}

/**
输出:
input: [1 5 4 3 2 7 1 8 2 3]
stream.Count(): 10
stream.IndexOf(2): 4
stream.IndexesOf(2): [4 8]
stream.LastIndexOf(2): 8
stream.Contains(7): true
*/

排序和反转

package main

import (
	"fmt"
	"github.com/wesovilabs/koazee"
	"strings"
)

var animals = []string{"lynx", "dog", "cat", "monkey", "fox", "tiger", "lion"}

func main() {
	fmt.Print("input: ")
	fmt.Println(animals)
	stream := koazee.StreamOf(animals)

	fmt.Print("stream.Reverse(): ")
	fmt.Println(stream.Reverse().Out().Val())

	fmt.Print("stream.Sort(strings.Compare): ")
	fmt.Println(stream.Sort(strings.Compare).Out().Val())
}

/**
输出:
input: [lynx dog cat monkey fox tiger lion]
stream.Reverse(): [lion tiger fox monkey cat dog lynx]
stream.Sort(strings.Compare): [cat dog fox lion lynx monkey tiger]
*/

过滤操作

package main

import (
	"fmt"
	"github.com/wesovilabs/koazee"
)

var animals = []string{"lynx", "dog", "cat", "monkey", "dog", "fox", "tiger", "lion"}

func main() {
	fmt.Print("input: ")
	fmt.Println(animals)
	stream := koazee.StreamOf(animals)

	fmt.Print("stream.Take(1,4): ")
	fmt.Println(stream.Take(1, 4).Out().Val())

	fmt.Print("stream.Filter(len==4): ")
	fmt.Println(stream.
		Filter(
			func(val string) bool {
				return len(val) == 4
			}).
		Out().Val(),
	)
	fmt.Print("stream.RemoveDuplicates(): ")
	fmt.Println(stream.RemoveDuplicates().Out().Val())
}

/**
输出:
input: [lynx dog cat monkey dog fox tiger lion]
stream.Take(1,4): [dog cat monkey dog]
stream.Filter(len==4): [lynx lion]
stream.RemoveDuplicates(): [lynx dog cat monkey fox tiger lion]
*/

分组操作

package main

import (
	"fmt"
	"github.com/wesovilabs/koazee"
	"strings"
)

var animals = []string{"lynx", "dog", "cat", "monkey", "dog", "fox", "tiger", "lion"}

func main() {
	fmt.Printf("input: %v\n", animals)
	stream := koazee.StreamOf(animals)
	fmt.Print("stream.GroupBy(strings.Len): ")
	out, _ := stream.GroupBy(func(val string)int{return len(val)})
	fmt.Println(out)
}

/**
输出:
input: [lynx dog cat monkey dog fox tiger lion]
stream.GroupBy(strings.Len): map[5:[tiger] 4:[lynx lion] 3:[dog cat dog fox] 6:[monkey]]
*/

映射操作

package main

import (
	"fmt"
	"github.com/wesovilabs/koazee"
	"strings"
)

var animals = []string{"lynx", "dog", "cat", "monkey", "dog", "fox", "tiger", "lion"}

func main() {
	fmt.Printf("input: %v\n", animals)
	stream := koazee.StreamOf(animals)
	fmt.Print("stream.Map(strings.Title): ")
	fmt.Println(stream.Map(strings.Title).Do().Out().Val())
}

/**
输出:
input: [lynx dog cat monkey dog fox tiger lion]
stream.Map(strings.Title): [Lynx Dog Cat Monkey Dog Fox Tiger Lion]
*/

归约操作

package main

import (
	"fmt"
	"github.com/wesovilabs/koazee"
)

var numbers = []int{1, 5, 4, 3, 2, 7, 1, 8, 2, 3}

func main() {
	fmt.Printf("input: %v\n", numbers)
	stream := koazee.StreamOf(numbers)
	fmt.Print("stream.Reduce(sum): ")
	fmt.Println(stream.Reduce(func(acc, val int) int {
		return acc + val
	}).Int())
}

/**
输出:
input: [1 5 4 3 2 7 1 8 2 3]
stream.Reduce(sum): 36
*/

遍历操作

package main

import (
	"fmt"
	"github.com/wesovilabs/koazee"
)

type message struct {
	user    string
	message string
}

var messages = []*message{
	{user: "John", message: "Hello Jane"},
	{user: "Jane", message: "Hey John, how are you?"},
	{user: "John", message: "I'm fine! and you?"},
	{user: "Jane", message: "Me too"},
}

func main() {

	stream := koazee.StreamOf(messages)
	stream.ForEach(func(m *message) {
		fmt.Printf("%s: \"%s\"\n", m.user, m.message)
	}).Do()
}

/**
输出:
John: "Hello Jane"
Jane: "Hey John, how are you?"
John: "I'm fine! and you?"
Jane: "Me too"
*/

组合操作与惰性求值

Koazee的主要目标是提供一组可以组合并且惰性求值的操作:

package main

import (
	"fmt"
	"github.com/wesovilabs/koazee"
	"strings"
)

type Person struct {
	Name string
	Male bool
	Age  int
}

var people = []*Person{
	{"John Smith", true, 32},
	{"Peter Pan", true, 17},
	{"Jane Doe", false, 20},
	{"Anna Wallace", false, 35},
	{"Tim O'Brian", true, 13},
	{"Celia Hills", false, 15},
}

func main() {
	stream := koazee.
		StreamOf(people).
		Filter(func(person *Person) bool {
			return !person.Male
		}).
		Sort(func(person, otherPerson *Person) int {
			return strings.Compare(person.Name, otherPerson.Name)
		}).
		ForEach(func(person *Person) {
			fmt.Printf("%s is %d years old\n", person.Name, person.Age)
		})

	fmt.Println("Operations are not evaluated until we perform stream.Do()\n")
	stream.Do()
}

/**
输出:
Operations are not evaluated until we perform stream.Do()

Anna Wallace is 35 years old
Celia Hills is 15 years old
Jane Doe is 20 years old
*/

可用操作列表

操作 描述 版本
Add 在最后位置添加新元素 v0.0.1
At 返回给定位置的元素 v0.0.1
Contains 检查流中是否存在给定元素 v0.0.1
Count 返回流中元素数量 v0.0.1
DeleteAt 删除给定位置的元素 v0.0.3
Drop 从流中移除元素 v0.0.1
DropWhile 移除流中与给定函数匹配的元素 v0.0.4
Filter 丢弃与提供过滤器不匹配的元素 v0.0.1
First 返回第一个位置的元素 v0.0.1
ForEach 对流中所有元素执行操作 v0.0.1
GroupBy 根据返回的函数值创建分组 v0.0.4
IndexOf 返回元素在流中的第一个索引 v0.0.3
IndexesOf 返回元素在流中所有出现的索引 v0.0.4
Last 返回最后一个位置的元素 v0.0.1
LastIndexOf 返回元素在流中最后一次出现的索引 v0.0.3
Map 转换流中的元素 v0.0.1
Pop 提取流中的第一个元素并返回该元素和新流 v0.0.3
Reduce 通过为流的每个值执行提供的函数将流减少为单个值 v0.0.1
RemoveDuplicates 移除重复元素 v0.0.1
Reverse 反转流中元素的顺序 v0.0.3
Set 用提供的值替换给定索引处的元素 v0.0.3
Sort 对流中的元素进行排序 v0.0.1
Take 返回给定索引之间的元素的流 v0.0.3

Koazee提供了丰富的函数式操作,可以方便地处理切片数据,同时保持惰性求值和不可变性等函数式编程特性。


更多关于golang实现惰性求值与函数式数组操作的插件库koazee的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现惰性求值与函数式数组操作的插件库koazee的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang惰性求值与函数式数组操作:koazee库使用指南

什么是koazee

koazee是一个Go语言库,提供了类似Lodash或Underscore的功能,允许开发者以函数式风格处理集合数据。它的特点是支持惰性求值(Lazy Evaluation),这意味着操作不会立即执行,而是在需要结果时才进行计算。

主要特性

  1. 惰性求值:操作链不会立即执行,直到需要结果时才计算
  2. 不可变数据:所有操作都返回新的集合,不会修改原数据
  3. 链式调用:支持流畅的链式API
  4. 类型安全:编译时类型检查

安装

go get github.com/wesovilabs/koazee

基本使用示例

1. 创建流(Stream)

package main

import (
	"fmt"
	"github.com/wesovilabs/koazee"
)

func main() {
	// 从切片创建流
	numbers := []int{1, 2, 3, 4, 5}
	stream := koazee.StreamOf(numbers)
	
	// 直接创建流
	stream2 := koazee.StreamOf(1, 2, 3, 4, 5)
	
	fmt.Println(stream.Out().Val()) // [1 2 3 4 5]
	fmt.Println(stream2.Out().Val()) // [1 2 3 4 5]
}

2. 基本操作

// 过滤
filtered := stream.
    Filter(func(num int) bool { return num%2 == 0 }).
    Out().Val()
fmt.Println(filtered) // [2 4]

// 映射
mapped := stream.
    Map(func(num int) int { return num * 2 }).
    Out().Val()
fmt.Println(mapped) // [2 4 6 8 10]

// 归约
sum := stream.
    Reduce(func(acc, num int) int { return acc + num }).
    Out().Val()
fmt.Println(sum) // 15

3. 惰性求值示例

lazyStream := koazee.StreamOf(1, 2, 3, 4, 5).
    Map(func(x int) int {
        fmt.Printf("Processing %d\n", x)
        return x * 2
    }).
    Filter(func(x int) bool {
        return x > 5
    })

// 此时还没有任何输出,因为操作是惰性的

result := lazyStream.Out().Val()
// 输出:
// Processing 1
// Processing 2
// Processing 3
// Processing 4
// Processing 5
fmt.Println(result) // [6 8 10]

4. 更多实用操作

// 去重
unique := koazee.StreamOf(1, 2, 2, 3, 3, 3).
    RemoveDuplicates().
    Out().Val()
fmt.Println(unique) // [1 2 3]

// 排序
sorted := koazee.StreamOf(3, 1, 4, 2).
    Sort(func(a, b int) int {
        if a < b {
            return -1
        } else if a > b {
            return 1
        }
        return 0
    }).
    Out().Val()
fmt.Println(sorted) // [1 2 3 4]

// 取前N个
firstTwo := koazee.StreamOf(1, 2, 3, 4, 5).
    Take(2).
    Out().Val()
fmt.Println(firstTwo) // [1 2]

5. 复杂类型操作

type Person struct {
    Name string
    Age  int
}

people := []Person{
    {"Alice", 25},
    {"Bob", 30},
    {"Charlie", 20},
}

// 按年龄过滤
youngPeople := koazee.StreamOf(people).
    Filter(func(p Person) bool { return p.Age < 30 }).
    Out().Val()
fmt.Println(youngPeople) // [{Alice 25} {Charlie 20}]

// 提取姓名
names := koazee.StreamOf(people).
    Map(func(p Person) string { return p.Name }).
    Out().Val()
fmt.Println(names) // [Alice Bob Charlie]

性能考虑

虽然koazee提供了方便的API,但需要注意:

  1. 惰性求值会带来一定的内存开销
  2. 对于小型数据集,性能差异不明显
  3. 对于大型数据集或性能敏感场景,建议进行基准测试

与标准库对比

// 标准库实现过滤
var filteredStd []int
for _, num := range numbers {
    if num%2 == 0 {
        filteredStd = append(filteredStd, num)
    }
}

// koazee实现
filteredKoazee := koazee.StreamOf(numbers).
    Filter(func(num int) bool { return num%2 == 0 }).
    Out().Val()

koazee代码更简洁,但标准库版本通常性能更好。

总结

koazee为Go语言带来了函数式编程风格和惰性求值能力,特别适合:

  • 需要处理复杂数据转换的场景
  • 希望代码更声明式、更易读的情况
  • 不处于极端性能敏感路径的代码

对于简单的操作,标准库可能更合适,但对于复杂的数据处理管道,koazee可以显著提高代码的可读性和可维护性。

回到顶部