Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sliceop{,/f64s}: introduce generics sliceop #931

Merged
merged 2 commits into from
Apr 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 5 additions & 81 deletions sliceop/f64s/f64s.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,72 +5,28 @@
// Package f64s provides common operations on float64 slices.
package f64s

import (
"fmt"
)

var (
errLength = fmt.Errorf("f64s: length mismatch")
errSortedIndices = fmt.Errorf("f64s: indices not sorted")
errDuplicateIndices = fmt.Errorf("f64s: duplicate indices")
)
import "go-hep.org/x/hep/sliceop"

// Filter creates a slice with all the elements x_i of src for which f(x_i) is true.
// Filter uses dst as work buffer, storing elements at the start of the slice.
// Filter clears dst if a slice is passed, and allocates a new slice if dst is nil.
func Filter(dst, src []float64, f func(v float64) bool) []float64 {

if dst == nil {
dst = make([]float64, 0, len(src))
}

dst = dst[:0]
for _, x := range src {
if f(x) {
dst = append(dst, x)
}
}

return dst
return sliceop.Filter(dst, src, f)
}

// Map creates a slice with all the elements f(x_i) where x_i are elements from src.
// Map uses dst as work buffer, storing elements at the start of the slice.
// Map allocates a new slice if dst is nil.
// Map will panic if the lengths of src and dst differ.
func Map(dst, src []float64, f func(v float64) float64) []float64 {

if dst == nil {
dst = make([]float64, len(src))
}

if len(src) != len(dst) {
panic(errLength)
}

for i, x := range src {
dst[i] = f(x)
}
return dst
return sliceop.Map(dst, src, f)
}

// Find creates a slice with all indices corresponding to elements for which f(x) is true.
// Find uses dst as work buffer, storing indices at the start of the slice.
// Find clears dst if a slice is passed, and allocates a new slice if dst is nil.
func Find(dst []int, src []float64, f func(v float64) bool) []int {

if dst == nil {
dst = make([]int, 0, len(src))
}

dst = dst[:0]
for i, x := range src {
if f(x) {
dst = append(dst, i)
}
}

return dst
return sliceop.Find(dst, src, f)
}

// Take creates a sub-slice of src with all elements indiced by the provided indices.
Expand All @@ -80,37 +36,5 @@ func Find(dst []int, src []float64, f func(v float64) bool) []int {
// Take will panic if length of indices is larger than length of src.
// Take will panic if length of indices is different from length of dst.
func Take(dst, src []float64, indices []int) []float64 {

if len(indices) > len(src) {
panic(errLength)
}

if dst == nil {
dst = make([]float64, len(indices))
}

if len(dst) != len(indices) {
panic(errLength)
}

if len(indices) == 0 {
return dst
}

dst[0] = src[indices[0]]
for i := 1; i < len(indices); i++ {
v0 := indices[i-1]
v1 := indices[i]
switch {
case v0 < v1:
// ok.
case v0 == v1:
panic(errDuplicateIndices)
case v0 > v1:
panic(errSortedIndices)
}
dst[i] = src[v1]
}

return dst
return sliceop.Take(dst, src, indices)
}
7 changes: 7 additions & 0 deletions sliceop/f64s/f64s_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,19 @@ func panics(t *testing.T, want error) func() {
}

func TestMap(t *testing.T) {
var errLength = fmt.Errorf("sliceop: length mismatch")
defer panics(t, errLength)()

_ = Map(make([]float64, 3), make([]float64, 5), nil)
}

func TestTake(t *testing.T) {
var (
errLength = fmt.Errorf("sliceop: length mismatch")
errSortedIndices = fmt.Errorf("sliceop: indices not sorted")
errDuplicateIndices = fmt.Errorf("sliceop: duplicate indices")
)

for _, tc := range []struct {
dst, src []float64
inds []int
Expand Down
124 changes: 124 additions & 0 deletions sliceop/sliceop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright ©2021 The go-hep Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build go1.18

// Package sliceop provides operations on slices not available in the stdlib
// slices package.
package sliceop // import "go-hep.org/x/hep/sliceop"

import (
"fmt"
)

var (
errLength = fmt.Errorf("sliceop: length mismatch")
errSortedIndices = fmt.Errorf("sliceop: indices not sorted")
errDuplicateIndices = fmt.Errorf("sliceop: duplicate indices")
)

// Filter creates a slice with all the elements x_i of src for which f(x_i) is true.
// Filter uses dst as work buffer, storing elements at the start of the slice.
// Filter clears dst if a slice is passed, and allocates a new slice if dst is nil.
func Filter[T any](dst, src []T, f func(v T) bool) []T {

if dst == nil {
dst = make([]T, 0, len(src))
}

dst = dst[:0]
for _, x := range src {
if f(x) {
dst = append(dst, x)
}
}

return dst
}

// Map creates a slice with all the elements f(x_i) where x_i are elements from src.
// Map uses dst as work buffer, storing elements at the start of the slice.
// Map allocates a new slice if dst is nil.
// Map will panic if the lengths of src and dst differ.
func Map[T, U any](dst []U, src []T, f func(v T) U) []U {

if dst == nil {
dst = make([]U, len(src))
}

if len(src) != len(dst) {
panic(errLength)
}

for i, x := range src {
dst[i] = f(x)
}
return dst
}

// Find creates a slice with all indices corresponding to elements for which f(x) is true.
// Find uses dst as work buffer, storing indices at the start of the slice.
// Find clears dst if a slice is passed, and allocates a new slice if dst is nil.
func Find[T any](dst []int, src []T, f func(v T) bool) []int {

if dst == nil {
dst = make([]int, 0, len(src))
}

dst = dst[:0]
for i, x := range src {
if f(x) {
dst = append(dst, i)
}
}

return dst
}

// Take creates a sub-slice of src with all elements indiced by the provided indices.
// Take uses dst as work buffer, storing elements at the start of the slice.
// Take clears dst if a slice is passed, and allocates a new slice if dst is nil.
// Take will panic if indices is not sorted or has duplicates.
// Take will panic if length of indices is larger than length of src.
// Take will panic if length of indices is different from length of dst.
func Take[T any](dst, src []T, indices []int) []T {

if len(indices) > len(src) {
panic(errLength)
}

if dst == nil {
dst = make([]T, len(indices))
}

if len(dst) != len(indices) {
panic(errLength)
}

if len(indices) == 0 {
return dst
}

dst[0] = src[indices[0]]
var (
v0 = indices[0]
nn = len(indices)
)
for i := 1; i < nn; i++ {
v1 := indices[i]
switch {
case v0 < v1:
// ok.
case v0 == v1:
panic(errDuplicateIndices)
case v0 > v1:
panic(errSortedIndices)
}
dst[i-1] = src[v0]
v0 = v1
}
dst[nn-1] = src[v0]

return dst
}
51 changes: 51 additions & 0 deletions sliceop/sliceop_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright ©2022 The go-hep Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sliceop_test

import (
"fmt"

"go-hep.org/x/hep/sliceop"
)

// An example of slice filtering
func ExampleFilter() {
slice := []float64{1, 2, -99, 4, 5, -99, 7}
condition := func(x float64) bool { return x > 0 }
fmt.Println(sliceop.Filter(nil, slice, condition))

// Output:
// [1 2 4 5 7]
}

// An example of slice mapping
func ExampleMap() {
slice := []float64{1, 2, -99, 4, 5, -99, 7}
operation := func(x float64) float64 { return x * x }
fmt.Println(sliceop.Map(nil, slice, operation))

// Output:
// [1 4 9801 16 25 9801 49]
}

// An example of slice finding
func ExampleFind() {
slice := []float64{1, 2, -99, 4, 5, -99, 7}
condition := func(x float64) bool { return x == -99 }
fmt.Println(sliceop.Find(nil, slice, condition))

// Output:
// [2 5]
}

// An example of taking a sub-slice defined by indices
func ExampleTake() {
slice := []float64{1, 2, -99, 4, 5, -99, 7}
indices := []int{2, 5}
fmt.Println(sliceop.Take(nil, slice, indices))

// Output:
// [-99 -99]
}
Loading