From 8b637f9bae17f5800174b5538f61a94f0a8971c0 Mon Sep 17 00:00:00 2001 From: Deng Ming <mingflycash@gmail.com> Date: Fri, 9 Sep 2022 18:56:30 +0800 Subject: [PATCH] =?UTF-8?q?slice:=20=E8=81=9A=E5=90=88=E5=87=BD=E6=95=B0?= =?UTF-8?q?=20Max,=20Min=20=E5=92=8C=20Sum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .CHANGELOG.md | 3 +- constrain.go | 27 ++++++ slice/aggregate.go | 55 +++++++++++ slice/aggregate_test.go | 205 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 constrain.go create mode 100644 slice/aggregate.go create mode 100644 slice/aggregate_test.go diff --git a/.CHANGELOG.md b/.CHANGELOG.md index 643e9562..24592bfb 100644 --- a/.CHANGELOG.md +++ b/.CHANGELOG.md @@ -23,4 +23,5 @@ - [ekit: 实现了 TaskPool](https://github.com/gotomicro/ekit/pull/57) - [ekit: 修复OnDemandBlockTaskPool测试不稳定](https://github.com/gotomicro/ekit/pull/70) - [syncx: 使用泛型封装 sync.Map](https://github.com/gotomicro/ekit/pull/79) -- [slice: 支持 Diff*, Intersection*, Union*, Index* 类方法](https://github.com/gotomicro/ekit/pull/83) \ No newline at end of file +- [slice: 支持 Diff*, Intersection*, Union*, Index* 类方法](https://github.com/gotomicro/ekit/pull/83) +- [slice: 聚合函数 Max, Min 和 Sum](https://github.com/gotomicro/ekit/pull/82) \ No newline at end of file diff --git a/constrain.go b/constrain.go new file mode 100644 index 00000000..33faccff --- /dev/null +++ b/constrain.go @@ -0,0 +1,27 @@ +// Copyright 2021 gotomicro +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ekit + +// RealNumber 实数 +// 绝大多数情况下,你都应该用这个来表达数字的含义 +type RealNumber interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~float32 | ~float64 +} + +type Number interface { + RealNumber | ~complex64 | ~complex128 +} diff --git a/slice/aggregate.go b/slice/aggregate.go new file mode 100644 index 00000000..889841f5 --- /dev/null +++ b/slice/aggregate.go @@ -0,0 +1,55 @@ +// Copyright 2021 gotomicro +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package slice + +import ( + "github.com/gotomicro/ekit" +) + +// Max 返回最大值。 +// 该方法假设你至少会传入一个值。 +// 在使用 float32 或者 float64 的时候要小心精度问题 +func Max[T ekit.RealNumber](ts []T) T { + res := ts[0] + for i := 1; i < len(ts); i++ { + if ts[i] > res { + res = ts[i] + } + } + return res +} + +// Min 返回最小值 +// 该方法会假设你至少会传入一个值 +// 在使用 float32 或者 float64 的时候要小心精度问题 +func Min[T ekit.RealNumber](ts []T) T { + res := ts[0] + for i := 1; i < len(ts); i++ { + if ts[i] < res { + res = ts[i] + } + } + return res +} + +// Sum 求和 +// 在使用 float32 或者 float64 的时候要小心精度问题 +func Sum[T ekit.Number](ts []T) T { + var res T + for _, n := range ts { + res += n + } + return res +} diff --git a/slice/aggregate_test.go b/slice/aggregate_test.go new file mode 100644 index 00000000..a1778822 --- /dev/null +++ b/slice/aggregate_test.go @@ -0,0 +1,205 @@ +// Copyright 2021 gotomicro +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package slice + +import ( + "fmt" + "testing" + + "github.com/gotomicro/ekit" + + "github.com/stretchr/testify/assert" +) + +func TestMax(t *testing.T) { + testCases := []struct { + name string + input []Integer + want Integer + }{ + { + name: "value", + input: []Integer{1}, + want: 1, + }, + { + name: "values", + input: []Integer{2, 3, 1}, + want: 3, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res := Max[Integer](tc.input) + assert.Equal(t, tc.want, res) + }) + } + + assert.Panics(t, func() { + Max[int](nil) + }) + assert.Panics(t, func() { + Max[int]([]int{}) + }) + + testMaxTypes[uint](t) + testMaxTypes[uint8](t) + testMaxTypes[uint16](t) + testMaxTypes[uint32](t) + testMaxTypes[uint64](t) + testMaxTypes[int](t) + testMaxTypes[int8](t) + testMaxTypes[int16](t) + testMaxTypes[int32](t) + testMaxTypes[int64](t) + testMaxTypes[float32](t) + testMaxTypes[float64](t) +} + +func TestMin(t *testing.T) { + testCases := []struct { + name string + input []Integer + want Integer + }{ + { + name: "value", + input: []Integer{3}, + want: 3, + }, + { + name: "values", + input: []Integer{3, 1, 2}, + want: 1, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res := Min[Integer](tc.input) + assert.Equal(t, tc.want, res) + }) + } + + assert.Panics(t, func() { + Min[int](nil) + }) + assert.Panics(t, func() { + Min[int]([]int{}) + }) + + testMinTypes[uint](t) + testMinTypes[uint8](t) + testMinTypes[uint16](t) + testMinTypes[uint32](t) + testMinTypes[uint64](t) + testMinTypes[int](t) + testMinTypes[int8](t) + testMinTypes[int16](t) + testMinTypes[int32](t) + testMinTypes[int64](t) + testMinTypes[float32](t) + testMinTypes[float64](t) +} + +func TestSum(t *testing.T) { + testCases := []struct { + name string + input []Integer + want Integer + }{ + { + name: "nil", + }, + { + name: "empty", + input: []Integer{}, + }, + { + name: "value", + input: []Integer{1}, + want: 1, + }, + { + name: "values", + input: []Integer{1, 2, 3}, + want: 6, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res := Sum[Integer](tc.input) + assert.Equal(t, tc.want, res) + }) + } + + testSumTypes[uint](t) + testSumTypes[uint8](t) + testSumTypes[uint16](t) + testSumTypes[uint32](t) + testSumTypes[uint64](t) + testSumTypes[int](t) + testSumTypes[int8](t) + testSumTypes[int16](t) + testSumTypes[int32](t) + testSumTypes[int64](t) + testSumTypes[float32](t) + testSumTypes[float64](t) +} + +// testMaxTypes 只是用来测试一下满足 Max 方法约束的所有类型 +func testMaxTypes[T ekit.RealNumber](t *testing.T) { + res := Max[T]([]T{1, 2, 3}) + assert.Equal(t, T(3), res) +} + +// testMinTypes 只是用来测试一下满足 Min 方法约束的所有类型 +func testMinTypes[T ekit.RealNumber](t *testing.T) { + res := Min[T]([]T{1, 2, 3}) + assert.Equal(t, T(1), res) +} + +// testSumTypes 只是用来测试一下满足 Sum 方法约束的所有类型 +func testSumTypes[T ekit.RealNumber](t *testing.T) { + res := Sum[T]([]T{1, 2, 3}) + assert.Equal(t, T(6), res) +} + +type Integer int + +func ExampleSum() { + res := Sum[int]([]int{1, 2, 3}) + fmt.Println(res) + res = Sum[int](nil) + fmt.Println(res) + // Output: + // 6 + // 0 +} + +func ExampleMin() { + res := Min[int]([]int{1, 2, 3}) + fmt.Println(res) + // Output: + // 1 +} + +func ExampleMax() { + res := Max[int]([]int{1, 2, 3}) + fmt.Println(res) + // Output: + // 3 +}