1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-04 12:52:28 +08:00

Compare commits

..

25 Commits

Author SHA1 Message Date
dudaodong
e614274f07 release v2.0.5 2022-04-24 10:34:25 +08:00
dudaodong
c524eb04a1 docs: add doc for concurrency in readme file 2022-04-24 10:32:55 +08:00
dudaodong
985b3cddd8 fix: fix TestTee func 2022-04-24 10:26:07 +08:00
dudaodong
abadeec007 docs: add doc for channel.go 2022-04-24 10:17:34 +08:00
dudaodong
3ab05154aa docs: add doc for channel.go 2022-04-22 17:12:41 +08:00
dudaodong
9f1c89bf0e refactor: refact FanIn func in channel.go 2022-04-21 14:31:43 +08:00
dudaodong
9444582e44 fix: fix OrDone func 2022-04-21 14:19:38 +08:00
dudaodong
c27ccad2b9 refactor: refact Or func in channel.go 2022-04-21 14:13:19 +08:00
dudaodong
d1c6c57700 feat: add func Bridge 2022-04-21 11:26:02 +08:00
dudaodong
922999037f feat: add func Tee 2022-04-21 11:18:45 +08:00
dudaodong
046e90486d feat: add func in channel.go 2022-04-19 16:21:23 +08:00
dudaodong
fc6dee9e77 feat: add func in channel.go 2022-04-19 16:03:12 +08:00
dudaodong
980ff2c363 feat: add FanIn for channel 2022-04-19 15:34:26 +08:00
dudaodong
763aa8e10d refactor: replace param done channel with ctx context 2022-04-19 14:42:55 +08:00
dudaodong
83c0d1d6e6 fix: fix misspellings 2022-04-15 17:02:34 +08:00
dudaodong
dfc6b868fb feat: add RepeateFn function 2022-04-15 16:32:55 +08:00
dudaodong
f66c0938e5 feat: add Take function to generate a chan of values taken from another chan 2022-04-15 16:22:19 +08:00
dudaodong
a4900fecb4 feat: add Repeate function to generate a chan of repeate values 2022-04-15 16:08:14 +08:00
dudaodong
dc25bdab2f feat: add Generate chan function in concurrency package 2022-04-15 15:56:06 +08:00
dudaodong
bb23c9eef8 release v2.0.4 2022-04-14 11:51:34 +08:00
dudaodong
b4cd0750e4 fix: fix http post to support multipart/form-data header 2022-04-14 11:48:39 +08:00
dudaodong
c960841491 docs: update sort function doc 2022-04-12 14:45:22 +08:00
dudaodong
5d6f9443fd refactor: rewrite sort function 2022-04-12 14:30:43 +08:00
dudaodong
5483380066 refactor: rewrite arrayqueue 2022-04-08 17:39:54 +08:00
dudaodong
20b9e5353e fix: fix goreport issue 2022-04-06 11:33:48 +08:00
16 changed files with 1570 additions and 210 deletions

View File

@@ -4,7 +4,7 @@
<br/> <br/>
![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf) ![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf)
[![Release](https://img.shields.io/badge/release-2.0.3-green.svg)](https://github.com/duke-git/lancet/releases) [![Release](https://img.shields.io/badge/release-2.0.5-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2) [![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2) [![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml) [![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -23,7 +23,7 @@ English | [简体中文](./README_zh-CN.md)
## Feature ## Feature
- 👏 Comprehensive, efficient and reusable. - 👏 Comprehensive, efficient and reusable.
- 💪 250+ go util functions, support string, slice, datetime, net, crypt... - 💪 300+ go util functions, support string, slice, datetime, net, crypt...
- 💅 Only depend on the go standard library. - 💅 Only depend on the go standard library.
- 🌍 Unit test for every exported function. - 🌍 Unit test for every exported function.
@@ -35,9 +35,9 @@ English | [简体中文](./README_zh-CN.md)
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
``` ```
2. <b>For users who use version below go1.18, you should install v1.x.x. now latest v1 is v1.2.7. </b> 2. <b>For users who use version below go1.18, you should install v1.x.x. now latest v1 is v1.2.9. </b>
```go ```go
go get github.com/duke-git/lancet@v1.2.7 // below go1.18, install latest version of v1.x.x go get github.com/duke-git/lancet@v1.2.9 // below go1.18, install latest version of v1.x.x
``` ```
## Usage ## Usage
@@ -89,6 +89,24 @@ import "github.com/duke-git/lancet/v2/algorithm"
- [LRUCache](https://github.com/duke-git/lancet/blob/main/docs/algorithm.md#LRUCache) - [LRUCache](https://github.com/duke-git/lancet/blob/main/docs/algorithm.md#LRUCache)
### Concurrency package contain some functions to support concurrent programming. eg, goroutine, channel, async.
```go
import "github.com/duke-git/lancet/v2/concurrency"
```
#### Function list:
- [NewChannel](https://github.com/duke-git/lancet/blob/main/docs/concurrency.md#NewChannel)
- [Bridge](https://github.com/duke-git/lancet/blob/main/docs/concurrency.md#Bridge)
- [FanIn](https://github.com/duke-git/lancet/blob/main/docs/concurrency.md#FanIn)
- [Generate](https://github.com/duke-git/lancet/blob/main/docs/concurrency.md#Generate)
- [Or](https://github.com/duke-git/lancet/blob/main/docs/concurrency.md#Or)
- [OrDone](https://github.com/duke-git/lancet/blob/main/docs/concurrency.md#OrDone)
- [Repeat](https://github.com/duke-git/lancet/blob/main/docs/concurrency.md#Repeat)
- [RepeatFn](https://github.com/duke-git/lancet/blob/main/docs/concurrency.md#RepeatFn)
- [Take](https://github.com/duke-git/lancet/blob/main/docs/concurrency.md#Take)
- [Tee](https://github.com/duke-git/lancet/blob/main/docs/concurrency.md#Tee)
### Convertor package contains some functions for data convertion. ### Convertor package contains some functions for data convertion.
```go ```go

View File

@@ -4,7 +4,7 @@
<br/> <br/>
![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf) ![Go version](https://img.shields.io/badge/go-%3E%3D1.16<recommend>-9cf)
[![Release](https://img.shields.io/badge/release-2.0.3-green.svg)](https://github.com/duke-git/lancet/releases) [![Release](https://img.shields.io/badge/release-2.0.5-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2) [![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2) [![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml) [![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -23,7 +23,7 @@
## 特性 ## 特性
- 👏 全面、高效、可复用 - 👏 全面、高效、可复用
- 💪 250+常用go工具函数支持string、slice、datetime、net、crypt... - 💪 300+常用go工具函数支持string、slice、datetime、net、crypt...
- 💅 只依赖go标准库 - 💅 只依赖go标准库
- 🌍 所有导出函数单元测试覆盖率100% - 🌍 所有导出函数单元测试覆盖率100%
@@ -35,9 +35,9 @@
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
``` ```
2. <b>使用go1.18以下版本的用户必须安装v1.x.x。目前最新的v1版本是v1.2.7。</b> 2. <b>使用go1.18以下版本的用户必须安装v1.x.x。目前最新的v1版本是v1.2.9。</b>
```go ```go
go get github.com/duke-git/lancet@v1.2.7 // 使用go1.18以下版本, 必须安装v1.x.x版本 go get github.com/duke-git/lancet@v1.2.9 // 使用go1.18以下版本, 必须安装v1.x.x版本
``` ```
## 用法 ## 用法
@@ -88,6 +88,24 @@ import "github.com/duke-git/lancet/v2/algorithm"
- [LinearSearch](https://github.com/duke-git/lancet/blob/main/docs/algorithm_zh-CN.md#LinearSearch) - [LinearSearch](https://github.com/duke-git/lancet/blob/main/docs/algorithm_zh-CN.md#LinearSearch)
- [LRUCache](https://github.com/duke-git/lancet/blob/main/docs/algorithm_zh-CN.md#LRUCache) - [LRUCache](https://github.com/duke-git/lancet/blob/main/docs/algorithm_zh-CN.md#LRUCache)
### 并发包包含一些支持并发编程的功能。例如goroutine, channel, async等。
```go
import "github.com/duke-git/lancet/v2/concurrency"
```
#### Function list:
- [NewChannel](https://github.com/duke-git/lancet/blob/main/docs/concurrency_zh-CN.md#NewChannel)
- [Bridge](https://github.com/duke-git/lancet/blob/main/docs/concurrency_zh-CN.md#Bridge)
- [FanIn](https://github.com/duke-git/lancet/blob/main/docs/concurrency_zh-CN.md#FanIn)
- [Generate](https://github.com/duke-git/lancet/blob/main/docs/concurrency_zh-CN.md#Generate)
- [Or](https://github.com/duke-git/lancet/blob/main/docs/concurrency_zh-CN.md#Or)
- [OrDone](https://github.com/duke-git/lancet/blob/main/docs/concurrency_zh-CN.md#OrDone)
- [Repeat](https://github.com/duke-git/lancet/blob/main/docs/concurrency_zh-CN.md#Repeat)
- [RepeatFn](https://github.com/duke-git/lancet/blob/main/docs/concurrency_zh-CN.md#RepeatFn)
- [Take](https://github.com/duke-git/lancet/blob/main/docs/concurrency_zh-CN.md#Take)
- [Tee](https://github.com/duke-git/lancet/blob/main/docs/concurrency_zh-CN.md#Tee)
### convertor转换器包支持一些常见的数据类型转换。 ### convertor转换器包支持一些常见的数据类型转换。
```go ```go

View File

@@ -7,7 +7,7 @@ package algorithm
import "github.com/duke-git/lancet/v2/lancetconstraints" import "github.com/duke-git/lancet/v2/lancetconstraints"
// BubbleSort use bubble to sort slice. // BubbleSort use bubble to sort slice.
func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator) []T { func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator) {
for i := 0; i < len(slice); i++ { for i := 0; i < len(slice); i++ {
for j := 0; j < len(slice)-1-i; j++ { for j := 0; j < len(slice)-1-i; j++ {
isCurrGreatThanNext := comparator.Compare(slice[j], slice[j+1]) == 1 isCurrGreatThanNext := comparator.Compare(slice[j], slice[j+1]) == 1
@@ -16,38 +16,26 @@ func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator) []T {
} }
} }
} }
return slice
} }
// InsertionSort use insertion to sort slice. // InsertionSort use insertion to sort slice.
func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator) []T { func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator) {
size := len(slice) for i := 0; i < len(slice); i++ {
if size <= 1 { for j := i; j > 0; j-- {
return slice isPreLessThanCurrent := comparator.Compare(slice[j], slice[j-1]) == -1
} if isPreLessThanCurrent {
swap(slice, j, j-1)
for i := 1; i < size; i++ { } else {
currentItem := slice[i] break
preIndex := i - 1 }
preItem := slice[preIndex]
isPreLessThanCurrent := comparator.Compare(preItem, currentItem) == -1
for preIndex >= 0 && isPreLessThanCurrent {
slice[preIndex+1] = slice[preIndex]
preIndex--
} }
slice[preIndex+1] = currentItem
} }
return slice
} }
// SelectionSort use selection to sort slice. // SelectionSort use selection to sort slice.
func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator) []T { func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator) {
for i := 0; i < len(slice); i++ { for i := 0; i < len(slice); i++ {
min := i min := i
for j := i + 1; j < len(slice); j++ { for j := i + 1; j < len(slice); j++ {
if comparator.Compare(slice[j], slice[min]) == -1 { if comparator.Compare(slice[j], slice[min]) == -1 {
min = j min = j
@@ -55,15 +43,11 @@ func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator) []
} }
swap(slice, i, min) swap(slice, i, min)
} }
return slice
} }
// ShellSort shell sort slice. // ShellSort shell sort slice.
func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator) []T { func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator) {
size := len(slice) size := len(slice)
if size <= 1 {
return slice
}
gap := 1 gap := 1
for gap < size/3 { for gap < size/3 {
@@ -76,21 +60,17 @@ func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator) []T {
swap(slice, j, j-gap) swap(slice, j, j-gap)
} }
} }
gap /= 3 gap = gap / 3
} }
return slice
} }
// QuickSort quick sorting for slice, lowIndex is 0 and highIndex is len(slice)-1 // QuickSort quick sorting for slice, lowIndex is 0 and highIndex is len(slice)-1
func QuickSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) []T { func QuickSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) {
if lowIndex < highIndex { if lowIndex < highIndex {
p := partition(slice, lowIndex, highIndex, comparator) p := partition(slice, lowIndex, highIndex, comparator)
QuickSort(slice, lowIndex, p-1, comparator) QuickSort(slice, lowIndex, p-1, comparator)
QuickSort(slice, p+1, highIndex, comparator) QuickSort(slice, p+1, highIndex, comparator)
} }
return slice
} }
// partition split slice into two parts // partition split slice into two parts
@@ -110,7 +90,7 @@ func partition[T any](slice []T, lowIndex, highIndex int, comparator lancetconst
} }
// HeapSort use heap to sort slice // HeapSort use heap to sort slice
func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator) []T { func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator) {
size := len(slice) size := len(slice)
for i := size/2 - 1; i >= 0; i-- { for i := size/2 - 1; i >= 0; i-- {
@@ -120,8 +100,6 @@ func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator) []T {
swap(slice, 0, j) swap(slice, 0, j)
sift(slice, 0, j-1, comparator) sift(slice, 0, j-1, comparator)
} }
return slice
} }
func sift[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) { func sift[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) {
@@ -145,15 +123,17 @@ func sift[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraint
} }
// MergeSort merge sorting for slice // MergeSort merge sorting for slice
func MergeSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) []T { func MergeSort[T any](slice []T, comparator lancetconstraints.Comparator) {
mergeSort(slice, 0, len(slice)-1, comparator)
}
func mergeSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) {
if lowIndex < highIndex { if lowIndex < highIndex {
mid := (lowIndex + highIndex) / 2 mid := (lowIndex + highIndex) / 2
MergeSort(slice, lowIndex, mid, comparator) mergeSort(slice, lowIndex, mid, comparator)
MergeSort(slice, mid+1, highIndex, comparator) mergeSort(slice, mid+1, highIndex, comparator)
merge(slice, lowIndex, mid, highIndex, comparator) merge(slice, lowIndex, mid, highIndex, comparator)
} }
return slice
} }
func merge[T any](slice []T, lowIndex, midIndex, highIndex int, comparator lancetconstraints.Comparator) { func merge[T any](slice []T, lowIndex, midIndex, highIndex int, comparator lancetconstraints.Comparator) {

View File

@@ -28,22 +28,15 @@ func (pc *peopleAgeComparator) Compare(v1 any, v2 any) int {
return 1 return 1
} }
return 0 return 0
//decending order
// if p1.Age > p2.Age {
// return -1
// } else if p1.Age < p2.Age {
// return 1
// }
} }
var peoples = []people{ // var peoples = []people{
{Name: "a", Age: 20}, // {Name: "a", Age: 20},
{Name: "b", Age: 10}, // {Name: "b", Age: 10},
{Name: "c", Age: 17}, // {Name: "c", Age: 17},
{Name: "d", Age: 8}, // {Name: "d", Age: 8},
{Name: "e", Age: 28}, // {Name: "e", Age: 28},
} // }
type intComparator struct{} type intComparator struct{}
@@ -60,40 +53,51 @@ func (c *intComparator) Compare(v1 any, v2 any) int {
return 0 return 0
} }
var intSlice = []int{2, 1, 5, 3, 6, 4} // var intSlice = []int{2, 1, 5, 3, 6, 4}
func TestBubbleSortForStructSlice(t *testing.T) { func TestBubbleSortForStructSlice(t *testing.T) {
asssert := internal.NewAssert(t, "TestBubbleSortForStructSlice") asssert := internal.NewAssert(t, "TestBubbleSortForStructSlice")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{} comparator := &peopleAgeComparator{}
sortedPeopleByAge := BubbleSort(peoples, comparator) BubbleSort(peoples, comparator)
t.Log(sortedPeopleByAge)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", sortedPeopleByAge) actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual) asssert.Equal(expected, actual)
} }
func TestBubbleSortForIntSlice(t *testing.T) { func TestBubbleSortForIntSlice(t *testing.T) {
asssert := internal.NewAssert(t, "TestBubbleSortForIntSlice") asssert := internal.NewAssert(t, "TestBubbleSortForIntSlice")
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedInt := BubbleSort(intSlice, comparator) BubbleSort(numbers, comparator)
expected := []int{1, 2, 3, 4, 5, 6}
asssert.Equal(expected, sortedInt) asssert.Equal([]int{1, 2, 3, 4, 5, 6}, numbers)
} }
func TestInsertionSort(t *testing.T) { func TestInsertionSort(t *testing.T) {
asssert := internal.NewAssert(t, "TestInsertionSort") asssert := internal.NewAssert(t, "TestInsertionSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{} comparator := &peopleAgeComparator{}
sortedPeopleByAge := InsertionSort(peoples, comparator) InsertionSort(peoples, comparator)
t.Log(sortedPeopleByAge)
expected := "[{e 28} {a 20} {c 17} {b 10} {d 8}]" expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", sortedPeopleByAge) actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual) asssert.Equal(expected, actual)
} }
@@ -101,12 +105,18 @@ func TestInsertionSort(t *testing.T) {
func TestSelectionSort(t *testing.T) { func TestSelectionSort(t *testing.T) {
asssert := internal.NewAssert(t, "TestSelectionSort") asssert := internal.NewAssert(t, "TestSelectionSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{} comparator := &peopleAgeComparator{}
sortedPeopleByAge := SelectionSort(peoples, comparator) SelectionSort(peoples, comparator)
t.Log(sortedPeopleByAge)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", sortedPeopleByAge) actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual) asssert.Equal(expected, actual)
} }
@@ -114,12 +124,18 @@ func TestSelectionSort(t *testing.T) {
func TestShellSort(t *testing.T) { func TestShellSort(t *testing.T) {
asssert := internal.NewAssert(t, "TestShellSort") asssert := internal.NewAssert(t, "TestShellSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{} comparator := &peopleAgeComparator{}
sortedPeopleByAge := ShellSort(peoples, comparator) ShellSort(peoples, comparator)
t.Log(sortedPeopleByAge)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", sortedPeopleByAge) actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual) asssert.Equal(expected, actual)
} }
@@ -127,12 +143,18 @@ func TestShellSort(t *testing.T) {
func TestQuickSort(t *testing.T) { func TestQuickSort(t *testing.T) {
asssert := internal.NewAssert(t, "TestQuickSort") asssert := internal.NewAssert(t, "TestQuickSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{} comparator := &peopleAgeComparator{}
sortedPeopleByAge := QuickSort(peoples, 0, len(peoples)-1, comparator) QuickSort(peoples, 0, len(peoples)-1, comparator)
t.Log(sortedPeopleByAge)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", sortedPeopleByAge) actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual) asssert.Equal(expected, actual)
} }
@@ -140,12 +162,18 @@ func TestQuickSort(t *testing.T) {
func TestHeapSort(t *testing.T) { func TestHeapSort(t *testing.T) {
asssert := internal.NewAssert(t, "TestHeapSort") asssert := internal.NewAssert(t, "TestHeapSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{} comparator := &peopleAgeComparator{}
sortedPeopleByAge := HeapSort(peoples, comparator) HeapSort(peoples, comparator)
t.Log(sortedPeopleByAge)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", sortedPeopleByAge) actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual) asssert.Equal(expected, actual)
} }
@@ -153,12 +181,18 @@ func TestHeapSort(t *testing.T) {
func TestMergeSort(t *testing.T) { func TestMergeSort(t *testing.T) {
asssert := internal.NewAssert(t, "TestMergeSort") asssert := internal.NewAssert(t, "TestMergeSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{} comparator := &peopleAgeComparator{}
sortedPeopleByAge := MergeSort(peoples, 0, len(peoples)-1, comparator) MergeSort(peoples, comparator)
t.Log(sortedPeopleByAge)
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]" expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", sortedPeopleByAge) actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual) asssert.Equal(expected, actual)
} }
@@ -166,6 +200,13 @@ func TestMergeSort(t *testing.T) {
func TestCountSort(t *testing.T) { func TestCountSort(t *testing.T) {
asssert := internal.NewAssert(t, "TestCountSort") asssert := internal.NewAssert(t, "TestCountSort")
peoples := []people{
{Name: "a", Age: 20},
{Name: "b", Age: 10},
{Name: "c", Age: 17},
{Name: "d", Age: 8},
{Name: "e", Age: 28},
}
comparator := &peopleAgeComparator{} comparator := &peopleAgeComparator{}
sortedPeopleByAge := CountSort(peoples, comparator) sortedPeopleByAge := CountSort(peoples, comparator)
t.Log(sortedPeopleByAge) t.Log(sortedPeopleByAge)

243
concurrency/channel.go Normal file
View File

@@ -0,0 +1,243 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel, async.
package concurrency
import (
"context"
"sync"
)
// Channel is a logic object which can generate or manipulate go channel
// all methods of Channel are in the book tilted《Concurrency in Go》
type Channel struct {
}
// NewChannel return a Channel instance
func NewChannel() *Channel {
return &Channel{}
}
// Generate a data of type any chan, put param `values` into the chan
func (c *Channel) Generate(ctx context.Context, values ...any) <-chan any {
dataStream := make(chan any)
go func() {
defer close(dataStream)
for _, v := range values {
select {
case <-ctx.Done():
return
case dataStream <- v:
}
}
}()
return dataStream
}
// Repeat return a data of type any chan, put param `values` into the chan repeatly until cancel the context.
func (c *Channel) Repeat(ctx context.Context, values ...any) <-chan any {
dataStream := make(chan any)
go func() {
defer close(dataStream)
for {
for _, v := range values {
select {
case <-ctx.Done():
return
case dataStream <- v:
}
}
}
}()
return dataStream
}
// RepeatFn return a chan, excutes fn repeatly, and put the result into retruned chan
// until close the `done` channel
func (c *Channel) RepeatFn(ctx context.Context, fn func() any) <-chan any {
dataStream := make(chan any)
go func() {
defer close(dataStream)
for {
select {
case <-ctx.Done():
return
case dataStream <- fn():
}
}
}()
return dataStream
}
// Take return a chan whose values are tahken from another chan
func (c *Channel) Take(ctx context.Context, valueStream <-chan any, number int) <-chan any {
takeStream := make(chan any)
go func() {
defer close(takeStream)
for i := 0; i < number; i++ {
select {
case <-ctx.Done():
return
case takeStream <- <-valueStream:
}
}
}()
return takeStream
}
// FanIn merge multiple channels into one channel
func (c *Channel) FanIn(ctx context.Context, channels ...<-chan any) <-chan any {
out := make(chan any)
go func() {
var wg sync.WaitGroup
wg.Add(len(channels))
for _, c := range channels {
go func(c <-chan any) {
defer wg.Done()
for v := range c {
select {
case <-ctx.Done():
return
case out <- v:
}
}
}(c)
}
wg.Wait()
close(out)
}()
return out
}
// Tee split one chanel into two channels
func (c *Channel) Tee(ctx context.Context, in <-chan any) (<-chan any, <-chan any) {
out1 := make(chan any)
out2 := make(chan any)
go func() {
defer close(out1)
defer close(out2)
for val := range c.OrDone(ctx, in) {
var out1, out2 = out1, out2
for i := 0; i < 2; i++ {
select {
case <-ctx.Done():
case out1 <- val:
out1 = nil
case out2 <- val:
out2 = nil
}
}
}
}()
return out1, out2
}
// Bridge link multiply channels into one channel
func (c *Channel) Bridge(ctx context.Context, chanStream <-chan <-chan any) <-chan any {
valStream := make(chan any)
go func() {
defer close(valStream)
for {
var stream <-chan any
select {
case maybeStream, ok := <-chanStream:
if ok == false {
return
}
stream = maybeStream
case <-ctx.Done():
return
}
for val := range c.OrDone(ctx, stream) {
select {
case valStream <- val:
case <-ctx.Done():
}
}
}
}()
return valStream
}
// Or read one or more channels into one channel, will close when any readin channel is closed
func (c *Channel) Or(channels ...<-chan any) <-chan any {
switch len(channels) {
case 0:
return nil
case 1:
return channels[0]
}
orDone := make(chan any)
go func() {
defer close(orDone)
switch len(channels) {
case 2:
select {
case <-channels[0]:
case <-channels[1]:
}
default:
m := len(channels) / 2
select {
case <-c.Or(channels[:m]...):
case <-c.Or(channels[m:]...):
}
// select {
// case <-channels[0]:
// case <-channels[1]:
// case <-channels[2]:
// case <-c.Or(append(channels[3:], orDone)...):
// }
}
}()
return orDone
}
// OrDone read a channel into another channel, will close until cancel context.
func (c *Channel) OrDone(ctx context.Context, channel <-chan any) <-chan any {
valStream := make(chan any)
go func() {
defer close(valStream)
for {
select {
case <-ctx.Done():
return
case v, ok := <-channel:
if !ok {
return
}
select {
case valStream <- v:
case <-ctx.Done():
}
}
}
}()
return valStream
}

206
concurrency/channel_test.go Normal file
View File

@@ -0,0 +1,206 @@
package concurrency
import (
"context"
"testing"
"time"
"github.com/duke-git/lancet/v2/internal"
)
func TestGenerate(t *testing.T) {
assert := internal.NewAssert(t, "TestGenerate")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel()
intStream := c.Generate(ctx, 1, 2, 3)
// for v := range intStream {
// t.Log(v) //1, 2, 3
// }
assert.Equal(1, <-intStream)
assert.Equal(2, <-intStream)
assert.Equal(3, <-intStream)
}
func TestRepeat(t *testing.T) {
assert := internal.NewAssert(t, "TestRepeat")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel()
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 5)
// for v := range intStream {
// t.Log(v) //1, 2, 1, 2, 1
// }
assert.Equal(1, <-intStream)
assert.Equal(2, <-intStream)
assert.Equal(1, <-intStream)
assert.Equal(2, <-intStream)
assert.Equal(1, <-intStream)
}
func TestRepeatFn(t *testing.T) {
assert := internal.NewAssert(t, "TestRepeatFn")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
fn := func() any {
s := "a"
return s
}
c := NewChannel()
dataStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
// for v := range dataStream {
// t.Log(v) //a, a, a
// }
assert.Equal("a", <-dataStream)
assert.Equal("a", <-dataStream)
assert.Equal("a", <-dataStream)
}
func TestTake(t *testing.T) {
assert := internal.NewAssert(t, "TestTake")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
numbers := make(chan any, 5)
numbers <- 1
numbers <- 2
numbers <- 3
numbers <- 4
numbers <- 5
defer close(numbers)
c := NewChannel()
intStream := c.Take(ctx, numbers, 3)
assert.Equal(1, <-intStream)
assert.Equal(2, <-intStream)
assert.Equal(3, <-intStream)
}
func TestFanIn(t *testing.T) {
assert := internal.NewAssert(t, "TestFanIn")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel()
channels := make([]<-chan any, 3)
for i := 0; i < 3; i++ {
channels[i] = c.Take(ctx, c.Repeat(ctx, i), 3)
}
mergedChannel := c.FanIn(ctx, channels...)
for val := range mergedChannel {
t.Logf("\t%d\n", val)
}
assert.Equal(1, 1)
}
func TestOr(t *testing.T) {
assert := internal.NewAssert(t, "TestOr")
sig := func(after time.Duration) <-chan any {
c := make(chan interface{})
go func() {
defer close(c)
time.Sleep(after)
}()
return c
}
start := time.Now()
c := NewChannel()
<-c.Or(
sig(1*time.Second),
sig(2*time.Second),
sig(3*time.Second),
sig(4*time.Second),
sig(5*time.Second),
)
t.Logf("done after %v", time.Since(start))
assert.Equal(1, 1)
}
func TestOrDone(t *testing.T) {
assert := internal.NewAssert(t, "TestOrDone")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel()
intStream := c.Take(ctx, c.Repeat(ctx, 1), 3)
var res any
for val := range c.OrDone(ctx, intStream) {
t.Logf("%v", val)
res = val
}
assert.Equal(1, res)
}
func TestTee(t *testing.T) {
assert := internal.NewAssert(t, "TestTee")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel()
inStream := c.Take(ctx, c.Repeat(ctx, 1), 4)
out1, out2 := c.Tee(ctx, inStream)
for val := range out1 {
val1 := val
val2 := <-out2
// t.Log("val1 is", val1)
// t.Log("val2 is", val2)
assert.Equal(1, val1)
assert.Equal(1, val2)
}
}
func TestBridge(t *testing.T) {
assert := internal.NewAssert(t, "TestBridge")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := NewChannel()
genVals := func() <-chan <-chan any {
chanStream := make(chan (<-chan any))
go func() {
defer close(chanStream)
for i := 0; i < 10; i++ {
stream := make(chan any, 1)
stream <- i
close(stream)
chanStream <- stream
}
}()
return chanStream
}
index := 0
for val := range c.Bridge(ctx, genVals()) {
// t.Logf("%v ", val) //0 1 2 3 4 5 6 7 8 9
assert.Equal(index, val)
index++
}
}

View File

@@ -1,79 +1,101 @@
package datastructure package datastructure
import ( import (
"errors"
"fmt" "fmt"
"reflect" "reflect"
) )
// ArrayQueue implements queue with slice // ArrayQueue implements queue with slice
type ArrayQueue[T any] struct { type ArrayQueue[T any] struct {
data []T items []T
length int head int
tail int
capacity int
size int
} }
// NewArrayQueue return a empty ArrayQueue pointer func NewArrayQueue[T any](capacity int) *ArrayQueue[T] {
func NewArrayQueue[T any](values ...T) *ArrayQueue[T] { return &ArrayQueue[T]{
data := make([]T, len(values)) items: make([]T, 0, capacity),
for i, v := range values { head: 0,
data[i] = v tail: 0,
capacity: capacity,
size: 0,
} }
return &ArrayQueue[T]{data: data, length: len(values)}
} }
// Data return queue data // Data return queue data
func (q *ArrayQueue[T]) Data() []T { func (q *ArrayQueue[T]) Data() []T {
return q.data items := []T{}
for i := q.head; i < q.tail; i++ {
items = append(items, q.items[i])
}
return items
} }
// Size return length of queue data // Size return length of queue data
func (q *ArrayQueue[T]) Size() int { func (q *ArrayQueue[T]) Size() int {
return q.length return q.size
} }
// IsEmpty checks if queue is empty or not // IsEmpty checks if queue is empty or not
func (q *ArrayQueue[T]) IsEmpty() bool { func (q *ArrayQueue[T]) IsEmpty() bool {
return q.length == 0 return q.size == 0
} }
// Front return front value of queue // Front return front value of queue
func (q *ArrayQueue[T]) Front() T { func (q *ArrayQueue[T]) Front() T {
return q.data[0] return q.items[0]
} }
// Back return back value of queue // Back return back value of queue
func (q *ArrayQueue[T]) Back() T { func (q *ArrayQueue[T]) Back() T {
return q.data[q.length-1] return q.items[q.size-1]
} }
// EnQueue put element into queue // EnQueue put element into queue
func (q *ArrayQueue[T]) EnQueue(value T) { func (q *ArrayQueue[T]) Enqueue(item T) bool {
q.data = append(q.data, value) if q.head == 0 && q.tail == q.capacity {
q.length++ return false
} else if q.head != 0 && q.tail == q.capacity {
for i := q.head; i < q.tail; i++ {
q.items[i-q.head] = q.items[i]
}
q.tail = q.tail - q.head
q.head = 0
}
q.items = append(q.items, item)
q.tail++
q.size++
return true
} }
// DeQueue remove head element of queue and return it, if queue is empty, return nil and error // DeQueue remove head element of queue and return it, if queue is empty, return nil and error
func (q *ArrayQueue[T]) DeQueue() (*T, error) { func (q *ArrayQueue[T]) Dequeue() (T, bool) {
if q.IsEmpty() { var item T
return nil, errors.New("queue is empty") if q.head == q.tail {
return item, false
} }
item = q.items[q.head]
headItem := q.data[0] q.head++
q.data = q.data[1:] q.size--
q.length-- return item, true
return &headItem, nil
} }
// Clear the queue data // Clear the queue data
func (q *ArrayQueue[T]) Clear() { func (q *ArrayQueue[T]) Clear() {
q.data = []T{} capacity := q.capacity
q.length = 0 q.items = make([]T, 0, capacity)
q.head = 0
q.tail = 0
q.size = 0
q.capacity = capacity
} }
// Contain checks if the value is in queue or not // Contain checks if the value is in queue or not
func (q *ArrayQueue[T]) Contain(value T) bool { func (q *ArrayQueue[T]) Contain(value T) bool {
for _, v := range q.data { for _, v := range q.items {
if reflect.DeepEqual(v, value) { if reflect.DeepEqual(v, value) {
return true return true
} }
@@ -84,8 +106,8 @@ func (q *ArrayQueue[T]) Contain(value T) bool {
// Print queue data // Print queue data
func (q *ArrayQueue[T]) Print() { func (q *ArrayQueue[T]) Print() {
info := "[" info := "["
for _, v := range q.data { for i := q.head; i < q.tail; i++ {
info += fmt.Sprintf("%+v, ", v) info += fmt.Sprintf("%+v, ", q.items[i])
} }
info += "]" info += "]"
fmt.Println(info) fmt.Println(info)

View File

@@ -6,13 +6,13 @@ import (
"github.com/duke-git/lancet/v2/internal" "github.com/duke-git/lancet/v2/internal"
) )
func TestArrayQueue_EnQueue(t *testing.T) { func TestArrayQueue_Enqueue(t *testing.T) {
assert := internal.NewAssert(t, "TestArrayQueue_EnQueue") assert := internal.NewAssert(t, "TestArrayQueue_Enqueue")
queue := NewArrayQueue[int]() queue := NewArrayQueue[int](5)
queue.EnQueue(1) queue.Enqueue(1)
queue.EnQueue(2) queue.Enqueue(2)
queue.EnQueue(3) queue.Enqueue(3)
expected := []int{1, 2, 3} expected := []int{1, 2, 3}
data := queue.Data() data := queue.Data()
@@ -24,26 +24,30 @@ func TestArrayQueue_EnQueue(t *testing.T) {
assert.Equal(3, size) assert.Equal(3, size)
} }
func TestArrayQueue_DeQueue(t *testing.T) { func TestArrayQueue_Dequeue(t *testing.T) {
assert := internal.NewAssert(t, "TestArrayQueue_DeQueue") assert := internal.NewAssert(t, "TestArrayQueue_Dequeue")
queue := NewArrayQueue(1, 2, 3) queue := NewArrayQueue[int](4)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
val, err := queue.DeQueue() val, ok := queue.Dequeue()
if err != nil { assert.Equal(true, ok)
t.Fail()
}
queue.Print() queue.Print()
assert.Equal(1, *val) assert.Equal(1, val)
assert.Equal([]int{2, 3}, queue.Data()) assert.Equal([]int{2, 3}, queue.Data())
} }
func TestArrayQueue_Front(t *testing.T) { func TestArrayQueue_Front(t *testing.T) {
assert := internal.NewAssert(t, "TestArrayQueue_Front") assert := internal.NewAssert(t, "TestArrayQueue_Front")
queue := NewArrayQueue(1, 2, 3) queue := NewArrayQueue[int](4)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
val := queue.Front() val := queue.Front()
queue.Print() queue.Print()
@@ -55,7 +59,11 @@ func TestArrayQueue_Front(t *testing.T) {
func TestArrayQueue_Back(t *testing.T) { func TestArrayQueue_Back(t *testing.T) {
assert := internal.NewAssert(t, "TestArrayQueue_Back") assert := internal.NewAssert(t, "TestArrayQueue_Back")
queue := NewArrayQueue(1, 2, 3) queue := NewArrayQueue[int](4)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
val := queue.Back() val := queue.Back()
queue.Print() queue.Print()
@@ -67,7 +75,10 @@ func TestArrayQueue_Back(t *testing.T) {
func TestArrayQueue_Contain(t *testing.T) { func TestArrayQueue_Contain(t *testing.T) {
assert := internal.NewAssert(t, "TestArrayQueue_Contain") assert := internal.NewAssert(t, "TestArrayQueue_Contain")
queue := NewArrayQueue(1, 2, 3) queue := NewArrayQueue[int](4)
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
assert.Equal(true, queue.Contain(1)) assert.Equal(true, queue.Contain(1))
assert.Equal(false, queue.Contain(4)) assert.Equal(false, queue.Contain(4))
@@ -76,11 +87,12 @@ func TestArrayQueue_Contain(t *testing.T) {
func TestArrayQueue_Clear(t *testing.T) { func TestArrayQueue_Clear(t *testing.T) {
assert := internal.NewAssert(t, "TestArrayQueue_Clear") assert := internal.NewAssert(t, "TestArrayQueue_Clear")
queue := NewArrayQueue[int]() queue := NewArrayQueue[int](4)
assert.Equal(true, queue.IsEmpty()) assert.Equal(true, queue.IsEmpty())
assert.Equal(0, queue.Size()) assert.Equal(0, queue.Size())
queue.EnQueue(1) queue.Enqueue(1)
assert.Equal(false, queue.IsEmpty()) assert.Equal(false, queue.IsEmpty())
assert.Equal(1, queue.Size()) assert.Equal(1, queue.Size())

View File

@@ -21,18 +21,23 @@ import (
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
## Index ## Index
- [BubbleSort](#BubbleSort) - [Algorithm](#algorithm)
- [CountSort](#CountSort) - [Source](#source)
- [HeapSort](#HeapSort) - [Usage](#usage)
- [InsertionSort](#InsertionSort) - [Index](#index)
- [MergeSort](#MergeSort) - [Documentation](#documentation)
- [QuickSort](#QuickSort) - [<span id="BubbleSort">BubbleSort</span>](#bubblesort)
- [SelectionSort](#SelectionSort) - [<span id="InsertionSort">InsertionSort</span>](#insertionsort)
- [ShellSort](#ShellSort) - [<span id="SelectionSort">SelectionSort</span>](#selectionsort)
- [BinarySearch](#BinarySearch) - [<span id="ShellSort">ShellSort</span>](#shellsort)
- [BinaryIterativeSearch](#BinaryIterativeSearch) - [<span id="QuickSort">QuickSort</span>](#quicksort)
- [LinearSearch](#LinearSearch) - [<span id="HeapSort">HeapSort</span>](#heapsort)
- [LRUCache](#LRUCache) - [<span id="MergeSort">MergeSort</span>](#mergesort)
- [<span id="CountSort">CountSort</span>](#countsort)
- [<span id="BinarySearch">BinarySearch</span>](#binarysearch)
- [<span id="BinaryIterativeSearch">BinaryIterativeSearch</span>](#binaryiterativesearch)
- [<span id="LinearSearch">LinearSearch</span>](#linearsearch)
- [<span id="LRUCache">LRUCache</span>](#lrucache)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -46,7 +51,7 @@ import (
<b>Signature:</b> <b>Signature:</b>
```go ```go
func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator) []T func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -76,9 +81,9 @@ func main() {
intSlice := []int{2, 1, 5, 3, 6, 4} intSlice := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedSlice := algorithm.BubbleSort(intSlice, comparator) algorithm.BubbleSort(intSlice, comparator)
fmt.Println(sortedSlice) //[]int{1, 2, 3, 4, 5, 6} fmt.Println(intSlice) //[]int{1, 2, 3, 4, 5, 6}
} }
``` ```
@@ -91,7 +96,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator) []T func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -141,9 +146,9 @@ func main() {
{Name: "e", Age: 28}, {Name: "e", Age: 28},
} }
comparator := &peopleAgeComparator{} comparator := &peopleAgeComparator{}
sortedPeople := algorithm.InsertionSort(peoples, comparator) algorithm.InsertionSort(peoples, comparator)
fmt.Println(sortedSlice) //[{d 8} {b 10} {c 17} {a 20} {e 28}] fmt.Println(peoples) //[{d 8} {b 10} {c 17} {a 20} {e 28}]
} }
``` ```
@@ -156,7 +161,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator) []T func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -186,9 +191,9 @@ func main() {
intSlice := []int{2, 1, 5, 3, 6, 4} intSlice := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedSlice := algorithm.SelectionSort(intSlice, comparator) algorithm.SelectionSort(intSlice, comparator)
fmt.Println(sortedSlice) //[]int{1, 2, 3, 4, 5, 6} fmt.Println(intSlice) //[]int{1, 2, 3, 4, 5, 6}
} }
``` ```
@@ -201,7 +206,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator) []T func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -231,9 +236,9 @@ func main() {
intSlice := []int{2, 1, 5, 3, 6, 4} intSlice := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedSlice := algorithm.ShellSort(intSlice, comparator) algorithm.ShellSort(intSlice, comparator)
fmt.Println(sortedSlice) //[]int{1, 2, 3, 4, 5, 6} fmt.Println(intSlice) //[]int{1, 2, 3, 4, 5, 6}
} }
``` ```
@@ -246,7 +251,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func QuickSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) []T func QuickSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -276,9 +281,9 @@ func main() {
intSlice := []int{2, 1, 5, 3, 6, 4} intSlice := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedSlice := algorithm.QuickSort(intSlice, 0, len(intSlice)-1, comparator) algorithm.QuickSort(intSlice, 0, len(intSlice)-1, comparator)
fmt.Println(sortedSlice) //[]int{1, 2, 3, 4, 5, 6} fmt.Println(intSlice) //[]int{1, 2, 3, 4, 5, 6}
} }
``` ```
@@ -291,7 +296,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator) []T func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -321,9 +326,9 @@ func main() {
intSlice := []int{2, 1, 5, 3, 6, 4} intSlice := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedSlice := algorithm.HeapSort(intSlice, comparator) algorithm.HeapSort(intSlice, comparator)
fmt.Println(sortedSlice) //[]int{1, 2, 3, 4, 5, 6} fmt.Println(intSlice) //[]int{1, 2, 3, 4, 5, 6}
} }
``` ```
@@ -336,7 +341,7 @@ func main() {
<b>Signature:</b> <b>Signature:</b>
```go ```go
func MergeSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) []T func MergeSort[T any](slice []T, comparator lancetconstraints.Comparator)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -366,9 +371,9 @@ func main() {
intSlice := []int{2, 1, 5, 3, 6, 4} intSlice := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedSlice := algorithm.MergeSort(intSlice, 0, len(intSlice)-1, comparator) algorithm.MergeSort(intSlice, comparator)
fmt.Println(sortedSlice) //[]int{1, 2, 3, 4, 5, 6} fmt.Println(intSlice) //[]int{1, 2, 3, 4, 5, 6}
} }
``` ```

View File

@@ -21,18 +21,23 @@ import (
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
## 目录 ## 目录
- [BubbleSort](#BubbleSort) - [Algorithm](#algorithm)
- [CountSort](#CountSort) - [源码](#源码)
- [HeapSort](#HeapSort) - [用法](#用法)
- [InsertionSort](#InsertionSort) - [目录](#目录)
- [MergeSort](#MergeSort) - [文档](#文档)
- [QuickSort](#QuickSort) - [<span id="BubbleSort">BubbleSort</span>](#bubblesort)
- [SelectionSort](#SelectionSort) - [<span id="InsertionSort">InsertionSort</span>](#insertionsort)
- [ShellSort](#ShellSort) - [<span id="SelectionSort">SelectionSort</span>](#selectionsort)
- [BinarySearch](#BinarySearch) - [<span id="ShellSort">ShellSort</span>](#shellsort)
- [BinaryIterativeSearch](#BinaryIterativeSearch) - [<span id="QuickSort">QuickSort</span>](#quicksort)
- [LinearSearch](#LinearSearch) - [<span id="HeapSort">HeapSort</span>](#heapsort)
- [LRUCache](#LRUCache) - [<span id="MergeSort">MergeSort</span>](#mergesort)
- [<span id="CountSort">CountSort</span>](#countsort)
- [<span id="BinarySearch">BinarySearch</span>](#binarysearch)
- [<span id="BinaryIterativeSearch">BinaryIterativeSearch</span>](#binaryiterativesearch)
- [<span id="LinearSearch">LinearSearch</span>](#linearsearch)
- [<span id="LRUCache">LRUCache</span>](#lrucache)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -46,7 +51,7 @@ import (
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator) []T func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -76,9 +81,9 @@ func main() {
intSlice := []int{2, 1, 5, 3, 6, 4} intSlice := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedSlice := algorithm.BubbleSort(intSlice, comparator) algorithm.BubbleSort(intSlice, comparator)
fmt.Println(sortedSlice) //[]int{1, 2, 3, 4, 5, 6} fmt.Println(intSlice) //[]int{1, 2, 3, 4, 5, 6}
} }
``` ```
@@ -91,7 +96,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator) []T func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -141,9 +146,9 @@ func main() {
{Name: "e", Age: 28}, {Name: "e", Age: 28},
} }
comparator := &peopleAgeComparator{} comparator := &peopleAgeComparator{}
sortedPeople := algorithm.InsertionSort(peoples, comparator) algorithm.InsertionSort(peoples, comparator)
fmt.Println(sortedSlice) //[{d 8} {b 10} {c 17} {a 20} {e 28}] fmt.Println(intSlice) //[{d 8} {b 10} {c 17} {a 20} {e 28}]
} }
``` ```
@@ -156,7 +161,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator) []T func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -186,9 +191,9 @@ func main() {
intSlice := []int{2, 1, 5, 3, 6, 4} intSlice := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedSlice := algorithm.SelectionSort(intSlice, comparator) algorithm.SelectionSort(intSlice, comparator)
fmt.Println(sortedSlice) //[]int{1, 2, 3, 4, 5, 6} fmt.Println(intSlice) //[]int{1, 2, 3, 4, 5, 6}
} }
``` ```
@@ -201,7 +206,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator) []T func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -231,9 +236,9 @@ func main() {
intSlice := []int{2, 1, 5, 3, 6, 4} intSlice := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedSlice := algorithm.ShellSort(intSlice, comparator) algorithm.ShellSort(intSlice, comparator)
fmt.Println(sortedSlice) //[]int{1, 2, 3, 4, 5, 6} fmt.Println(intSlice) //[]int{1, 2, 3, 4, 5, 6}
} }
``` ```
@@ -276,9 +281,9 @@ func main() {
intSlice := []int{2, 1, 5, 3, 6, 4} intSlice := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedSlice := algorithm.QuickSort(intSlice, 0, len(intSlice)-1, comparator) algorithm.QuickSort(intSlice, 0, len(intSlice)-1, comparator)
fmt.Println(sortedSlice) //[]int{1, 2, 3, 4, 5, 6} fmt.Println(intSlice) //[]int{1, 2, 3, 4, 5, 6}
} }
``` ```
@@ -321,9 +326,9 @@ func main() {
intSlice := []int{2, 1, 5, 3, 6, 4} intSlice := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedSlice := algorithm.HeapSort(intSlice, comparator) algorithm.HeapSort(intSlice, comparator)
fmt.Println(sortedSlice) //[]int{1, 2, 3, 4, 5, 6} fmt.Println(intSlice) //[]int{1, 2, 3, 4, 5, 6}
} }
``` ```
@@ -336,7 +341,7 @@ func main() {
<b>函数签名:</b> <b>函数签名:</b>
```go ```go
func MergeSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) []T func MergeSort[T any](slice []T, comparator lancetconstraints.Comparator)
``` ```
<b>Example:</b> <b>Example:</b>
@@ -366,9 +371,9 @@ func main() {
intSlice := []int{2, 1, 5, 3, 6, 4} intSlice := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{} comparator := &intComparator{}
sortedSlice := algorithm.MergeSort(intSlice, 0, len(intSlice)-1, comparator) algorithm.MergeSort(intSlice, comparator)
fmt.Println(sortedSlice) //[]int{1, 2, 3, 4, 5, 6} fmt.Println(intSlice) //[]int{1, 2, 3, 4, 5, 6}
} }
``` ```

390
docs/concurrency.md Normal file
View File

@@ -0,0 +1,390 @@
# Concurrency
Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel, async.
<div STYLE="page-break-after: always;"></div>
## Source:
- [https://github.com/duke-git/lancet/blob/main/concurrency/channel.go](https://github.com/duke-git/lancet/blob/main/concurrency/channel.go)
<div STYLE="page-break-after: always;"></div>
## Usage:
```go
import (
"github.com/duke-git/lancet/v2/concurrency"
)
```
<div STYLE="page-break-after: always;"></div>
## Index
### Channel
- [NewChannel](#NewChannel)
- [Bridge](#Bridge)
- [FanIn](#FanIn)
- [Generate](#Generate)
- [Or](#Or)
- [OrDone](#OrDone)
- [Repeat](#Repeat)
- [RepeatFn](#RepeatFn)
- [Take](#Take)
- [Tee](#Tee)
<div STYLE="page-break-after: always;"></div>
## Documentation
## Channel
### <span id="NewChannel">NewChannel</span>
<p>return a Channel pointer instance.</p>
<b>Signature:</b>
```go
type Channel struct {}
func NewChannel() *Channel
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
c := concurrency.NewChannel()
}
```
### <span id="Bridge">Bridge</span>
<p>Link multiple channels into one channel until cancel the context.</p>
<b>Signature:</b>
```go
func (c *Channel) Bridge(ctx context.Context, chanStream <-chan <-chan any) <-chan any
```
<b>Example:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel()
genVals := func() <-chan <-chan any {
chanStream := make(chan (<-chan any))
go func() {
defer close(chanStream)
for i := 0; i < 10; i++ {
stream := make(chan any, 1)
stream <- i
close(stream)
chanStream <- stream
}
}()
return chanStream
}
index := 0
for val := range c.Bridge(ctx, genVals()) {
fmt.Printf("%v ", val) //0 1 2 3 4 5 6 7 8 9
}
}
```
### <span id="FanIn">FanIn</span>
<p>merge multiple channels into one channel until cancel the context.</p>
<b>Signature:</b>
```go
func (c *Channel) FanIn(ctx context.Context, channels ...<-chan any) <-chan any
```
<b>Example:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel()
channels := make([]<-chan any, 3)
for i := 0; i < 3; i++ {
channels[i] = c.Take(ctx, c.Repeat(ctx, i), 3)
}
mergedChannel := c.FanIn(ctx, channels...)
for val := range mergedChannel {
fmt.Println("\t%d\n", val) //1,2,1,0,0,1,0,2,2 (order not for sure)
}
}
```
### <span id="Repeat">Repeat</span>
<p>Return a chan, put param `values` into the chan repeatly until cancel the context.</p>
<b>Signature:</b>
```go
func (c *Channel) Repeat(ctx context.Context, values ...any) <-chan any
```
<b>Example:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel()
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 5)
for v := range intStream {
fmt.Println(v) //1, 2, 1, 2, 1
}
}
```
### <span id="RepeatFn">RepeatFn</span>
<p>Return a chan, excutes fn repeatly, and put the result into retruned chan until cancel context.</p>
<b>Signature:</b>
```go
func (c *Channel) RepeatFn(ctx context.Context, fn func() any) <-chan any
```
<b>Example:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
fn := func() any {
s := "a"
return s
}
c := concurrency.NewChannel()
dataStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
for v := range dataStream {
fmt.Println(v) //a, a, a
}
}
```
### <span id="Or">Or</span>
<p>Read one or more channels into one channel, will close when any readin channel is closed.</p>
<b>Signature:</b>
```go
func (c *Channel) Or(channels ...<-chan any) <-chan any
```
<b>Example:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
sig := func(after time.Duration) <-chan any {
c := make(chan interface{})
go func() {
defer close(c)
time.Sleep(after)
}()
return c
}
start := time.Now()
c := concurrency.NewChannel()
<-c.Or(
sig(1*time.Second),
sig(2*time.Second),
sig(3*time.Second),
sig(4*time.Second),
sig(5*time.Second),
)
fmt.Println("done after %v", time.Since(start)) //1.003s
}
```
### <span id="OrDone">OrDone</span>
<p>Read a channel into another channel, will close until cancel context.</p>
<b>Signature:</b>
```go
func (c *Channel) OrDone(ctx context.Context, channel <-chan any) <-chan any
```
<b>Example:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel()
intStream := c.Take(ctx, c.Repeat(ctx, 1), 3)
for val := range c.OrDone(ctx, intStream) {
fmt.Println(val) //1
}
}
```
### <span id="Take">Take</span>
<p>Return a chan whose values are tahken from another chan until cancel context.</p>
<b>Signature:</b>
```go
func (c *Channel) Take(ctx context.Context, valueStream <-chan any, number int) <-chan any
```
<b>Example:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
numbers := make(chan any, 5)
numbers <- 1
numbers <- 2
numbers <- 3
numbers <- 4
numbers <- 5
defer close(numbers)
c := concurrency.NewChannel()
intStream := c.Take(ctx, numbers, 3)
for val := range intStream {
fmt.Println(val) //1, 2, 3
}
}
```
### <span id="Tee">Tee</span>
<p>Split one chanel into two channels until cancel context.</p>
<b>Signature:</b>
```go
func (c *Channel) Tee(ctx context.Context, in <-chan any) (<-chan any, <-chan any)
```
<b>Example:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel()
inStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4)
out1, out2 := c.Tee(ctx, inStream)
for val := range out1 {
fmt.Println(val) //1
fmt.Println(<-out2) //1
}
}
```

390
docs/concurrency_zh-CN.md Normal file
View File

@@ -0,0 +1,390 @@
# Concurrency
并发包包含一些支持并发编程的功能。例如goroutine, channel, async等。
<div STYLE="page-break-after: always;"></div>
## 源码:
- [https://github.com/duke-git/lancet/blob/main/concurrency/channel.go](https://github.com/duke-git/lancet/blob/main/concurrency/channel.go)
<div STYLE="page-break-after: always;"></div>
## 用法:
```go
import (
"github.com/duke-git/lancet/v2/concurrency"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
### Channel
- [NewChannel](#NewChannel)
- [Bridge](#Bridge)
- [FanIn](#FanIn)
- [Generate](#Generate)
- [Or](#Or)
- [OrDone](#OrDone)
- [Repeat](#Repeat)
- [RepeatFn](#RepeatFn)
- [Take](#Take)
- [Tee](#Tee)
<div STYLE="page-break-after: always;"></div>
## 文档
### Channel
### <span id="NewChannel">NewChannel</span>
<p>返回一个 Channel 指针实例</p>
<b>函数签名:</b>
```go
type Channel struct {}
func NewChannel() *Channel
```
<b>例子:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
c := concurrency.NewChannel()
}
```
### <span id="Bridge">Bridge</span>
<p>将多个通道链接到一个通道,直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel) Bridge(ctx context.Context, chanStream <-chan <-chan any) <-chan any
```
<b>例子:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel()
genVals := func() <-chan <-chan any {
chanStream := make(chan (<-chan any))
go func() {
defer close(chanStream)
for i := 0; i < 10; i++ {
stream := make(chan any, 1)
stream <- i
close(stream)
chanStream <- stream
}
}()
return chanStream
}
index := 0
for val := range c.Bridge(ctx, genVals()) {
fmt.Printf("%v ", val) //0 1 2 3 4 5 6 7 8 9
}
}
```
### <span id="FanIn">FanIn</span>
<p>将多个通道合并为一个通道,直到取消上下文</p>
<b>函数签名:</b>
```go
func (c *Channel) FanIn(ctx context.Context, channels ...<-chan any) <-chan any
```
<b>例子:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel()
channels := make([]<-chan any, 3)
for i := 0; i < 3; i++ {
channels[i] = c.Take(ctx, c.Repeat(ctx, i), 3)
}
mergedChannel := c.FanIn(ctx, channels...)
for val := range mergedChannel {
fmt.Println("\t%d\n", val) //1,2,1,0,0,1,0,2,2 (order not for sure)
}
}
```
### <span id="Repeat">Repeat</span>
<p>返回一个chan将参数`values`重复放入chan直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel) Repeat(ctx context.Context, values ...any) <-chan any
```
<b>例子:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel()
intStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 5)
for v := range intStream {
fmt.Println(v) //1, 2, 1, 2, 1
}
}
```
### <span id="RepeatFn">RepeatFn</span>
<p>返回一个chan重复执行函数fn并将结果放入返回的chan直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel) RepeatFn(ctx context.Context, fn func() any) <-chan any
```
<b>例子:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
fn := func() any {
s := "a"
return s
}
c := concurrency.NewChannel()
dataStream := c.Take(ctx, c.RepeatFn(ctx, fn), 3)
for v := range dataStream {
fmt.Println(v) //a, a, a
}
}
```
### <span id="Or">Or</span>
<p>将一个或多个通道读取到一个通道中,当任何读取通道关闭时将结束读取。</p>
<b>函数签名:</b>
```go
func (c *Channel) Or(channels ...<-chan any) <-chan any
```
<b>例子:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
sig := func(after time.Duration) <-chan any {
c := make(chan interface{})
go func() {
defer close(c)
time.Sleep(after)
}()
return c
}
start := time.Now()
c := concurrency.NewChannel()
<-c.Or(
sig(1*time.Second),
sig(2*time.Second),
sig(3*time.Second),
sig(4*time.Second),
sig(5*time.Second),
)
fmt.Println("done after %v", time.Since(start)) //1.003s
}
```
### <span id="OrDone">OrDone</span>
<p>将一个通道读入另一个通道,直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel) OrDone(ctx context.Context, channel <-chan any) <-chan any
```
<b>例子:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel()
intStream := c.Take(ctx, c.Repeat(ctx, 1), 3)
for val := range c.OrDone(ctx, intStream) {
fmt.Println(val) //1
}
}
```
### <span id="Take">Take</span>
<p>返回一个chan其值从另一个chan获取直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel) Take(ctx context.Context, valueStream <-chan any, number int) <-chan any
```
<b>例子:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
numbers := make(chan any, 5)
numbers <- 1
numbers <- 2
numbers <- 3
numbers <- 4
numbers <- 5
defer close(numbers)
c := concurrency.NewChannel()
intStream := c.Take(ctx, numbers, 3)
for val := range intStream {
fmt.Println(val) //1, 2, 3
}
}
```
### <span id="Tee">Tee</span>
<p>将一个通道分成两个通道,直到取消上下文。</p>
<b>函数签名:</b>
```go
func (c *Channel) Tee(ctx context.Context, in <-chan any) (<-chan any, <-chan any)
```
<b>例子:</b>
```go
package main
import (
"context"
"fmt"
"github.com/duke-git/lancet/v2/concurrency"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := concurrency.NewChannel()
inStream := c.Take(ctx, c.Repeat(ctx, 1, 2), 4)
out1, out2 := c.Tee(ctx, inStream)
for val := range out1 {
fmt.Println(val) //1
fmt.Println(<-out2) //1
}
}
```

View File

@@ -69,7 +69,7 @@ func Intersect[K comparable, V any](maps ...map[K]V) map[K]V {
return maps[0] return maps[0]
} }
res := make(map[K]V) var res map[K]V
reducer := func(m1, m2 map[K]V) map[K]V { reducer := func(m1, m2 map[K]V) map[K]V {
m := make(map[K]V) m := make(map[K]V)
@@ -93,7 +93,6 @@ func Intersect[K comparable, V any](maps ...map[K]V) map[K]V {
return res return res
} }
// Minus creates an map of whose key in mapA but not in mapB // Minus creates an map of whose key in mapA but not in mapB
func Minus[K comparable, V any](mapA, mapB map[K]V) map[K]V { func Minus[K comparable, V any](mapA, mapB map[K]V) map[K]V {
res := make(map[K]V) res := make(map[K]V)

View File

@@ -6,7 +6,8 @@
// HttpGet, HttpPost, HttpDelete, HttpPut, HttpPatch, function param `url` is required. // HttpGet, HttpPost, HttpDelete, HttpPut, HttpPatch, function param `url` is required.
// HttpGet, HttpPost, HttpDelete, HttpPut, HttpPatch, function param `params` is variable, the order is: // HttpGet, HttpPost, HttpDelete, HttpPut, HttpPatch, function param `params` is variable, the order is:
// params[0] is header which type should be http.Header or map[string]string, // params[0] is header which type should be http.Header or map[string]string,
// params[1] is query param which type should be url.Values or map[string]any, // params[1] is query param which type should be url.Values or map[string]any, when content-type header is
// multipart/form-data or application/x-www-form-urlencoded, params[1] should be url.Values
// params[2] is post body which type should be []byte. // params[2] is post body which type should be []byte.
// params[3] is http client which type should be http.Client. // params[3] is http client which type should be http.Client.
package netutil package netutil

View File

@@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"log" "log"
"net/url"
"testing" "testing"
"github.com/duke-git/lancet/v2/internal" "github.com/duke-git/lancet/v2/internal"
@@ -46,6 +47,29 @@ func TestHttpPost(t *testing.T) {
t.Log("response: ", resp.StatusCode, string(body)) t.Log("response: ", resp.StatusCode, string(body))
} }
func TestHttpPostFormData(t *testing.T) {
apiUrl := "https://jsonplaceholder.typicode.com/todos"
header := map[string]string{
// "Content-Type": "application/x-www-form-urlencoded",
"Content-Type": "multipart/form-data",
}
type Todo struct {
UserId int `json:"userId"`
Title string `json:"title"`
}
postData := url.Values{}
postData.Add("userId", "1")
postData.Add("title", "TestAddToDo")
resp, err := HttpPost(apiUrl, header, postData, nil)
if err != nil {
log.Fatal(err)
t.FailNow()
}
body, _ := ioutil.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
}
func TestHttpPut(t *testing.T) { func TestHttpPut(t *testing.T) {
url := "https://jsonplaceholder.typicode.com/todos/1" url := "https://jsonplaceholder.typicode.com/todos/1"
header := map[string]string{ header := map[string]string{

View File

@@ -81,10 +81,16 @@ func setHeaderAndQueryAndBody(req *http.Request, reqUrl string, header, queryPar
if err != nil { if err != nil {
return err return err
} }
err = setBodyByte(req, body) if req.Header.Get("Content-Type") == "multipart/form-data" || req.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
formData := queryParam.(url.Values)
err = setBodyByte(req, []byte(formData.Encode()))
} else {
err = setBodyByte(req, body)
}
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }