Go variadic function argument passing - go

I am trying to understand, What is difference between 1st and 2nd passing argument in function. In both case methods are functional and compiles.
1)
generateReport(capacities...)
func generateReport(capacities ...float64) {
for i, cap := range capacities {
fmt.Printf("Plant %d capacity %.0f\n", i, cap)
}
}
2)
generateReport(plantCapacities)
func generateReport(capacities []float64) {
for i, cap := range capacities {
fmt.Printf("Plant %d capacity %.0f\n", i, cap)
}
}
Have found few good samples
1) GolangBot - Variadic Function
2) Golang.org - Passing arguments as #Himanshu mentioned.

According to Golang language specification
If f is variadic with a final parameter p of type ...T, then within f
the type of p is equivalent to type []T. If f is invoked with no
actual arguments for p, the value passed to p is nil. Otherwise, the
value passed is a new slice of type []T with a new underlying array
whose successive elements are the actual arguments, which all must be
assignable to T. The length and capacity of the slice is therefore the
number of arguments bound to p and may differ for each call site.
variadic functions are used to handle multiple trailing arguments. It can be used to pass slice arguments.
func main(){
capacities := []float64{1, 2, 3, 4}
generateReport(capacities...)
}
func generateReport(capacities ...float64) {
for i, cap := range capacities {
fmt.Printf("Plant %d capacity %.0f\n", i, cap)
}
}
Variadic functions can also be called in usual way with individual arguments. It works like spread operator in java script which can take multiple arguments. For eg:-
func main(){
generateReport(1,2,3,4)
}
func generateReport(capacities ...float64) {
for i, cap := range capacities {
fmt.Printf("Plant %d capacity %.0f\n", i, cap)
}
}

Related

invalid operation: cannot index T when modeling slices of arbitrary dimensions

I am try to do a matrix subtraction with unknow size of matrix. here is the code:
type ArrayLike interface {
[]interface{} | [][]interface{} | [][][]interface{} | [][][][]interface{}
}
func subMatrix[T ArrayLike](A, B T, shape []int) interface{} {
dim := shape[0]
if len(shape) == 1 {
retObj := make([]interface{}, dim)
for i := 0; i < dim; i++ {
Av := A[i].(float64)
Bv := B[i].(float64)
retObj[i] = Av - Bv
}
return retObj
} else {
retObj := make([]interface{}, dim)
for i := 0; i < dim; i++ {
retObj[i] = subMatrix(Av[i], Bv[i], shape[1:])
}
return retObj
}
}
It complains about
invalid operation: cannot index A (variable of type T constrained by []interface{}|[][]interface{}|[][][]interface{}|[][][][]interface{})compilerNonIndexableOperand
Does anyone know how to do this job?
You can't do this, not with generics and not with interface{}/any alone. The main issue is that you can't statically model slices with arbitrary dimensions — i.e. arbitrary types. Let's go in order:
The error message you got is because you can't index a type parameter with a union constraint like that. Specs, Index expressions:
For a of type parameter type P:
[...]
The element types of all types in P's type set must be
identical. [...]
The element types of ArrayLike's type set are not identical. The element type of []interface{} is interface{}, the one of [][]interface{} is []interface{} and so on.
There is a placebo solution to the indexing error, outlined here, basically it involves changing the type of the arguments A and B to a slice []T. Then you can index A[i] and B[i].
However this isn't enough. At this point, there's no way to pass correct arguments to the recursive call. The type of the expression A[i] is now T, but subMatrix wants []T.
Even dropping type parameters and declaring the args A and B as any doesn't work, because on the recursive call you still want to index them. In order to index, you have to assert any to something that's indexable, but what would that be? At each recursion the types would have one less dimension, so a statically typed assertion would eventually panic.
The only way to solve this is (probably?) with reflection, but honestly I don't see a practical utility to do that.
You can, and should, write a generic function for each matrix dimension:
import "golang.org/x/exp/constraints"
type Number interface {
constraints.Integer | constraints.Float
}
func Sub2DMatrix[T Number](a, b [][]T) {
for i := range a {
for j := range a[i] {
a[i][j] -= b[i][j]
}
}
}

What's the difference between a generic slice argument and an argument constrained to slice types?

Consider the experimental package slices. The package is experimental, so I understand the signatures may change; I'm using it to illustrate the issue.
Consider the signatures of two functions from this package, slices.Contains and slices.Grow:
func Contains[E comparable](s []E, v E) bool
func Grow[S ~[]E, E any](s S, n int) S
The first argument to Contains has type []E (slice of Es) with E constrained by comparable (types that are comparable).
The first argument to Grow instead has type S (just S), with S constrained by ~[]E (types whose underlying type is a slice of E)
However it looks like there isn't any practical difference between what operations are allowed inside functions with such type params. If we declare some fake funcs with the same type parameters, we can see that both compile just fine:
As expected, in both functions we can len/cap, append, range, allocate with make, and index with [ ].
func fakeContains[E comparable](s []E, v E) {
fmt.Println(len(s), cap(s))
var e E
fmt.Println(append(s, e))
fmt.Println(make([]E, 4))
for _, x := range s {
fmt.Println(x)
}
fmt.Println(s[0])
fmt.Println(reflect.TypeOf(s).Kind())
}
func fakeGrow[S ~[]E, E any](s S, n int) {
fmt.Println(len(s), cap(s))
var e E
fmt.Println(append(s, e))
fmt.Println(make(S, 4))
for _, x := range s {
fmt.Println(x)
}
fmt.Println(s[0])
fmt.Println(reflect.TypeOf(s).Kind())
}
Even reflect.TypeOf(s).Kind() gives reflect.Slice in all cases.
The functions can also be tested with different types, and all compile:
// compiles just fine
func main() {
type MyUint64 uint64
type MyUint64Slice []uint64
foo := []uint64{0, 1, 2}
fakeContains(foo, 0)
fakeGrow(foo, 5)
bar := []MyUint64{3, 4, 5}
fakeContains(bar, 0)
fakeGrow(bar, 5)
baz := MyUint64Slice{6, 7, 8}
fakeContains(baz, 0)
fakeGrow(baz, 5)
}
The only actual difference in my understanding is that in slices.Grow the argument s S is not a slice. It's just constrained to slice types. And as a matter of fact reflect.TypeOf(s) gives a different output when the arg is an instance of type MyUint64Slice []uint64:
Contains with arg s []E gives reflect.TypeOf(s) -> []uint64
Grow with arg s S gives reflect.TypeOf(s) -> main.MyUint64Slice
However it's not immediately apparent to me what's the practical difference between the two.
Playground with the code: https://gotipplay.golang.org/p/zg2dGtSJwuI
Question
Are these two declarations equivalent in practice? If not, when should I choose one over the other?
It matters if you have to return a slice of the same (possibly named) type as the argument.
If you do not have to return a slice (just some other info e.g. a bool to report if the value is contained), you do not need to use a type parameter that itself constraints to a slice, you may use a type parameter for the element only.
If you have to return a slice of the same type as the input, you must use a type parameter that itself constraints to a slice (e.g. ~[]E).
To demonstrate, let's see these 2 implementations of Grow():
func Grow[S ~[]E, E any](s S, n int) S {
return append(s, make(S, n)...)[:len(s)]
}
func Grow2[E any](s []E, n int) []E {
return append(s, make([]E, n)...)[:len(s)]
}
If you pass a slice of a custom type having a slice as its underlying type, Grow() can return a value of the same type. Grow2() cannot: it can only return a value of an unnamed slice type: []E.
And the demonstration:
x := []int{1}
x2 := Grow(x, 10)
fmt.Printf("x2 %T len=%d cap=%d\n", x2, len(x2), cap(x2))
x3 := Grow2(x, 10)
fmt.Printf("x3 %T len=%d cap=%d\n", x3, len(x3), cap(x3))
type ints []int
y := ints{1}
y2 := Grow(y, 10)
fmt.Printf("y2 %T len=%d cap=%d\n", y2, len(y2), cap(y2))
y3 := Grow2(y, 10)
fmt.Printf("y3 %T len=%d cap=%d\n", y3, len(y3), cap(y3))
Output (try it on the Go Playground):
x2 []int len=1 cap=12
x3 []int len=1 cap=12
y2 main.ints len=1 cap=12
y3 []int len=1 cap=12
As you can see Grow2(y, 10) receives a value of type main.ints and yet it returns a value of type []int. This is not what we would want from it.

Go generics in methods [duplicate]

This question already has answers here:
How to create generic method in Go? (method must have no type parameters)
(2 answers)
Is there a way to map an array of objects in golang?
(1 answer)
Closed last year.
Thanks to the new 1.18 beta release of Go, we can use new Generic feature. First thing I tried to do is to implement Map / Filter / Reduce to any slice.
So with a Generic Slice Type defined as :
type Slice[T any] []T
I can implements my two first methods Map and Filter as follows:
func (s Slice[T]) Map(callback func(item T, indice int, arr Slice[T]) T) Slice[T] {
temp := make([]T, 0)
for i, v := range s {
temp = append(temp, callback(v, i, s))
}
return temp
}
func (s Slice[T]) Filter(callback func(item T, indice int, arr Slice[T]) bool) Slice[T] {
temp := make([]T, 0)
for i, v := range s {
if callback(v, i, s) {
temp = append(temp, v)
}
}
return temp
}
This works fine as follows:
s := Slice[int]{1, 2, 3}
mapped := s.Map(func(item int, indice int, arr Slice[int]) int {
return item*2 + len(arr) + indice
})
fmt.Println(mapped) // Print : [5 8 11]
filtered := s.Filter(func(item int, _ int, _ Slice[int] ) bool {
return item%2==0
})
fmt.Println(filtered) // Print : [2]
Now, I'd like to implements my "Reduce" function, in the same way the Javascript Reduce works... In the Javascript version, we pass a callback with the accumulator, the current element, the index of the current element in the array and the array itself. It should return the accumulator; and a second argument: the initial value.
So, the slice should be generic over T, but the accumulator could be another generic value
I tried to code something like:
func (s Slice[T]) Reduce[V any](callback func(accumulator V, current T) V, initial V) V {
...
}
With this signature, based on a slice of T, my method will take a callback and an initial value of another generic type V. The callback accumulator is from this second generic V (and will return V) based on each current element of type T.
Unfortunately this code produces:
syntax error: method must have no type parameters
It seems we can't use any other type parameter except one declared on the receiver.
Is there any solution for this use case?

How to pass an accumulator to a recursive func?

(I'm new to Go.)
I am working on this leetcode problem: https://leetcode.com/problems/pascals-triangle/
package main
import "fmt"
func main() {
arrRes := [][]int{}
gen(5, arrRes)
fmt.Println(arrRes)
}
func gen(numRows int, arrRes [][]int) {
build(numRows, 0, arrRes)
}
func build(n int, level int, arrRes [][]int) {
if(n == level) {
return
}
arr := []int{}
if level == 0 {
arr = append(arr, 1)
} else if level == 1 {
arr = append(arr, 1, 1)
} else {
// get it out
tmp := arrRes[level-1]
arr = comb(tmp)
}
arrRes = append(arrRes, arr)
build(n, level+1, arrRes)
}
func comb(arr []int) []int{
// arr type init
tmpArr := []int{1}
for i:=1; i<len(arr); i++ {
sum := arr[i-1] + arr[i]
tmpArr = append(tmpArr, sum)
}
// go use val, not ref
tmpArr = append(tmpArr, 1)
return tmpArr;
}
I want to define an accumulated variable arrRes := [][]int{} and keep passing into the recursive function. I think Go is pass-by-value instead of pass-by-reference. Is there a way to keep this pattern?
I've got two alternative methods:
passing a global var.
pass a 2D array into the func then return the new 2D array.
https://github.com/kenpeter/go_tri/blob/master/tri_global.go
https://github.com/kenpeter/go_tri/blob/master/tri.go
A slice is (basically) three things: a length, a capacity, and a pointer to an underlying array. Everything in Go is pass-by-value, so when you pass a slice to a function you are passing its current length, current capacity, and the memory address of the pointer. Changes made to length and capacity inside the function are made to a copy, and will not affect the length and capacity of the slice that was passed as an argument in the function call.
Printing a slice doesn't print its underlying array, it prints the part of the underlying array that is visible in the slice (which could be none of it if len = 0), based on (1) the pointer to the first element in the underlying array that's supposed to be visible to the slice; and (2) the length in the slice variable.
If you are modifying the length or capacity of a slice inside a function and you want those changes to be visible outside the function, you can either return the slice to update the context outside the function, like append does:
numbers := append(numbers, 27)
Or you can pass in a pointer to a slice:
func ChangeNumbersLenOrCap(numbers *[]int) {
// make your changes, no return value required
}
For your program, it looks like you could get away with a pointer to a slice of int slices:
var arrRes *[][]int
...because you're not modifying the int slice across another function boundary. Some programs would need a pointer to a slice of pointers to int slices:
var arrRes *[]*[]int
Here are some simple edits to get you started:
arrRes := [][]int{}
gen(5, &arrRes)
fmt.Println(arrRes)
}
func gen(numRows int, arrRes *[][]int) {
// ...
func build(n int, level int, arrRes *[][]int) {
// ...
tmp := *arrRes[level-1]
// ...
*arrRes = append(*arrRes, arr)
build(n, level+1, arrRes)

Go Golang - embedding types and "len/range"

package m
type M map[int]int
// have methods on M
// can use len and range on M
package n
// need methods of M
type N struct { M }
// methods available
// BUT cannot use len or range on N
// if " type N M " - I lose the methods on M
Need the methods of M and len/range functionality in a different package. How can this be done ?
Ignoring packages (they don't matter in this scenario), you need to specify a valid type for the builtins len and range:
type M map[int]int
func (m *M) SayHi() {
fmt.Println("Hi!")
}
type N struct{ M }
func main() {
var foo N
fmt.Println(len(foo.M))
for k, v := range foo.M {
fmt.Printf("%d: %d\n", k, v)
}
foo.SayHi()
}
foo.SayHi() works because SayHi is promoted to struct N.
However, len and range are not methods on M, they are builtins that expect specific types. Embedding does not change the type, it promotes methods from the embedded field to the container struct.
You can read more about the details in the Go spec and Effective Go.

Resources