Is Golang's ... syntax really just varargs? - go

Well, I have a simple example, although it may seem pointless, but these examples have deviated greatly from my cognition.
Can anyone tell me what happened.
I pass the elements in []int to the parameter args with the ... syntax, but when I change the formal parameter args, the []int actual parameter is changed.
I modified the ordering of args , but the ordering of []int is also affected:
package main
import (
"fmt"
)
func bubbleSort(args ...int) {
for i := 0; i < len(args); i++ {
for j := 0; j < len(args)-1; j++ {
if args[j] > args[j+1] {
args[j], args[j+1] = args[j+1], args[j]
}
}
}
}
func main() {
isle := []int{3, 6, 1, 2, 5}
bubbleSort(isle...)
fmt.Printf("%v\n", isle)
}
// [1 2 3 5 6]
What if I pass in a single parameter? Apparently []int is not affected by args:
package main
import (
"fmt"
)
func bubbleSort(args ...int) {
for i := 0; i < len(args); i++ {
for j := 0; j < len(args)-1; j++ {
if args[j] > args[j+1] {
args[j], args[j+1] = args[j+1], args[j]
}
}
}
}
func main() {
isle := []int{3, 6, 1, 2, 5}
bubbleSort(isle[0], isle[1], isle[2], isle[3], isle[4])
fmt.Printf("%v\n", isle)
}
// [3 6 1 2 5]

Golang is not about "variable safety" as Rust. So you shoud never think that slice sent to function will not be corrupted.
If you want to use variadic functions but send slices, copy it:
func myUnsafeFunc(args ...int) {
// it will change args
}
func wantToSaveSliceFunc() {
myLovingSlice := []int{1,2,3}
myUnsafeFunc(append([]int(nil), slice...)...)
}
https://freshman.tech/snippets/go/copy-slices/
It's not confusing if you remember that spread operator ... is just a shugar .
Do not use it in your own API without serious reasone (as in fmt.Printf(...) for example) prefer use of explicit x []type notation over x ...type:
func myUnsafeFunc(args []int) {
// it will change args
}
func wantToSaveSliceFunc() {
myLovingSlice := []int{1,2,3}
myUnsafeFunc(append([]int(nil), slice...))
}
it still requires copying of slice, but it's much clear by semantics

Related

variadic function in golang

package main
import (
"fmt"
)
type ISum interface {
sum() int
}
type SumImpl struct {
Num int
}
func (s SumImpl) sum() int {
return s.Num
}
func main() {
nums := []int{1, 2}
variadicExample1(nums...)
impl1 := SumImpl{Num: 1}
impl2 := SumImpl{Num: 2}
variadicExample2(impl1, impl2)
impls := []SumImpl{
{
Num: 1,
},
{
Num: 2,
},
}
variadicExample2(impls...)
}
func variadicExample1(nums ...int) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}
func variadicExample2(nums ...ISum) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num.sum()
}
fmt.Println(total)
}
I have a question while using variable functions in go language.
When passing a struct that implements an interface as an argument, individual declarations are possible, but can you tell me why it is not possible when passing it through ...?
An error occurs in the code below.
variadicExample2(impls...)
I read this
How to pass an interface argument to a variadic function in Golang?
var impls []ISum
impls = append(impls, impl1)
impls = append(impls, impl1)
variadicExample2(impls...)
I found that the above code is possible.
A SumImpl slice is not a ISum slice. One is a slice of structs, and the other is a slice of interfaces. That's why you cannot pass it to a function that requires a []ISum (i.e. ...ISUm).
But you can do this:
impls := []ISum{
SumImpl{
Num: 1,
},
SumImpl{
Num: 2,
},
}

Change slice content and capacity inside a function in-place

I am trying to learn Go, so here is my very simple function for removing adjacent duplicates from slice for exercise from the book by Donovan & Kernighan.
Here is the code: https://play.golang.org/p/avHc1ixfck
package main
import "fmt"
func main() {
a := []int{0, 1, 1, 3, 3, 3}
removeDup(a)
fmt.Println(a)
}
func removeDup(s []int) {
n := len(s)
tmp := make([]int, 0, n)
tmp = append(tmp, s[0])
j := 1
for i := 1; i < n; i++ {
if s[i] != s[i-1] {
tmp = append(tmp, s[i])
j++
}
}
s = s[:len(tmp)]
copy(s, tmp)
}
It should print out [0 1 3] - and I checked, actually tmp at the end of the function it has desired form. However, the result is [0 1 3 3 3 3]. I guess there is something with copy function.
Can I somehow replace input slice s with the temp or trim it to desired length?
Option 1
Return a new slice as suggested by #zerkms.
https://play.golang.org/p/uGJiD3WApS
package main
import "fmt"
func main() {
a := []int{0, 1, 1, 3, 3, 3}
a = removeDup(a)
fmt.Println(a)
}
func removeDup(s []int) []int {
n := len(s)
tmp := make([]int, 0, n)
tmp = append(tmp, s[0])
for i := 1; i < n; i++ {
if s[i] != s[i-1] {
tmp = append(tmp, s[i])
}
}
return tmp
}
Option 2
Use pointers for pass-by-reference.
The same thing in effect as that of option1.
https://play.golang.org/p/80bE5Qkuuj
package main
import "fmt"
func main() {
a := []int{0, 1, 1, 3, 3, 3}
removeDup(&a)
fmt.Println(a)
}
func removeDup(sp *[]int) {
s := *sp
n := len(s)
tmp := make([]int, 0, n)
tmp = append(tmp, s[0])
for i := 1; i < n; i++ {
if s[i] != s[i-1] {
tmp = append(tmp, s[i])
}
}
*sp = tmp
}
Also, refer to following SO thread:
Does Go have no real way to shrink a slice? Is that an issue?
Here's two more slightly different ways to achieve what you want using sets and named types. The cool thing about named types is that you can create interfaces around them and can help with the readability of lots of code.
package main
import "fmt"
func main() {
// returning a list
a := []int{0, 1, 1, 3, 3, 3}
clean := removeDup(a)
fmt.Println(clean)
// creating and using a named type
nA := &newArrType{0, 1, 1, 3, 3, 3}
nA.removeDup2()
fmt.Println(nA)
// or... casting your orginal array to the named type
nB := newArrType(a)
nB.removeDup2()
fmt.Println(nB)
}
// using a set
// order is not kept, but a set is returned
func removeDup(s []int) (newArr []int) {
set := make(map[int]struct{})
for _, n := range s {
set[n] = struct{}{}
}
newArr = make([]int, 0, len(set))
for k := range set {
newArr = append(newArr, k)
}
return
}
// using named a typed
type newArrType []int
func (a *newArrType) removeDup2() {
x := *a
for i := range x {
f := i + 1
if f < len(x) {
if x[i] == x[f] {
x = x[:f+copy(x[f:], x[f+1:])]
}
}
}
// check the last 2 indexes
if x[len(x)-2] == x[len(x)-1] {
x = x[:len(x)-1+copy(x[len(x)-1:], x[len(x)-1+1:])]
}
*a = x
}

Reverse Slice of strings [duplicate]

This question already has answers here:
How do I reverse a slice in go?
(6 answers)
Closed 11 months ago.
I don't understand what is wrong with the below implementation, I had a look at sort.StringSlice and it looks the same.
type RevStr []string
func(s RevStr) Len() int { return len(s) }
func(s RevStr) Less(i, j int) bool { return s[i] < s[j] }
func(s RevStr) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func Reverse(input string) string {
rs := RevStr(strings.Split(input, " "))
sort.Reverse(rs)
return strings.Join(rs, " ")
}
sort.Reverse doesn't sort the data, but rather returns a new sort.Interface that will sort the data in reverse order. So you don't really need your own type:
func Reverse(input string) string {
s := strings.Split(input, " ")
sort.Sort(sort.Reverse(sort.StringSlice(s)))
return strings.Join(s, " ")
}
Playground: http://play.golang.org/p/w49FDCEHo3.
EDIT: If you just need to reverse a slice of strings, just do:
func reverse(ss []string) {
last := len(ss) - 1
for i := 0; i < len(ss)/2; i++ {
ss[i], ss[last-i] = ss[last-i], ss[i]
}
}
Playground: http://play.golang.org/p/UptIRFV_SI
Nothing is wrong with your RevStr type (though you could just use sort.StringSlice). You're not calling sort.Sort on the reversed implementation:
https://golang.org/pkg/sort/#example_Reverse
package main
import (
"fmt"
"sort"
)
func main() {
s := []int{5, 2, 6, 3, 1, 4} // unsorted
sort.Sort(sort.Reverse(sort.IntSlice(s)))
fmt.Println(s)
}
Although #Ainar-G has provided a way to reverse a slice of strings, I think it's nicer to use two variables in for loop to reverse. But it's only my personal opinion, a matter of style :)
func reverse(s []string) []string {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
Playground link with example of usage: http://play.golang.org/p/v1Cy61NFv1
A one-liner solution (using a lambda):
Given:
myStrings := []string{"apple", "banana", "cherry"}
Sort (in reverse order) with:
sort.Slice(myStrings, func(i, j int) bool { return myStrings[i] > myStrings[j]})
Playground Example:
https://play.golang.org/p/WZabAZTizHG
More simple way, without using built-in sorting feature :
func reverse(s []string) []string {
for i := len(s) - 1; i >= 0; i-- {
result = append(result, s[i])
}
return s
}
func reverseStr(data []string) []string {
m := len(data) - 1
var out = []string{}
for i := m; i >= 0; i-- {
out = append(out, data[i])
}
return out
}

Short way to apply a function to all elements in a list in golang

Suppose I would like to apply a function to every element in a list, and then put the resulting values in another list so I can immediately use them. In python, I would do something like this:
list = [1,2,3]
str = ', '.join(multiply(x, 2) for x in list)
In Go, I do something like this:
list := []int{1,2,3}
list2 := []int
for _,x := range list {
list2 := append(list2, multiply(x, 2))
}
str := strings.Join(list2, ", ")
Is it possible to do this in a shorter way?
I would do exactly as you did, with a few tweaks to fix typos
import (
"fmt"
"strconv"
"strings"
)
func main() {
list := []int{1,2,3}
var list2 []string
for _, x := range list {
list2 = append(list2, strconv.Itoa(x * 2)) // note the = instead of :=
}
str := strings.Join(list2, ", ")
fmt.Println(str)
}
This is an old question, but was the top hit in my Google search, and I found information that I believe will be helpful to the OP and anyone else who arrives here, looking for the same thing.
There is a shorter way, although you have to write the map function yourself.
In go, func is a type, which allows you to write a function that accepts as input the subject slice and a function, and which iterates over that slice, applying that function.
See the Map function near the bottom of this Go by Example page : https://gobyexample.com/collection-functions
I've included it here for reference:
func Map(vs []string, f func(string) string) []string {
vsm := make([]string, len(vs))
for i, v := range vs {
vsm[i] = f(v)
}
return vsm
}
You then call it like so:
fmt.Println(Map(strs, strings.ToUpper))
So, yes: The shorter way you are looking for exists, although it is not built into the language itself.
I've created a small utility package with Mapand Filter methods now that generics have been introduced in 1.18 :)
https://pkg.go.dev/github.com/sa-/slicefunk
Example usage
package main
import (
"fmt"
sf "github.com/sa-/slicefunk"
)
func main() {
original := []int{1, 2, 3, 4, 5}
newArray := sf.Map(original, func(item int) int { return item + 1 })
newArray = sf.Map(newArray, func(item int) int { return item * 3 })
newArray = sf.Filter(newArray, func(item int) bool { return item%2 == 0 })
fmt.Println(newArray)
}
With go1.18+ you can write a much cleaner generic Map function:
func Map[T, V any](ts []T, fn func(T) V) []V {
result := make([]V, len(ts))
for i, t := range ts {
result[i] = fn(t)
}
return result
}
Usage, e.g:
input := []int{4, 5, 3}
outputInts := Map(input, func(item int) int { return item + 1 })
outputStrings := Map(input, func(item int) string { return fmt.Sprintf("Item:%d", item) })
Found a way to define a generic map array function
func Map(t interface{}, f func(interface{}) interface{} ) []interface{} {
switch reflect.TypeOf(t).Kind() {
case reflect.Slice:
s := reflect.ValueOf(t)
arr := make([]interface{}, s.Len())
for i := 0; i < s.Len(); i++ {
arr[i] = f(s.Index(i).Interface())
}
return arr
}
return nil
}
origin := []int{4,5,3}
newArray := Map(origin, func(item interface{}) interface{} { return item.(int) + 1})
You can use lo's Map in order to quickly apply a function to all elements. For example, in order to multiply by 2 and convert to string, you can use:
l := lo.Map[int, string]([]int{1, 2, 3, 4}, func(x int, _ int) string { return strconv.Itoa(x * 2) })
Then you can convert back to a comma delimited string like so:
strings.Join(l, ",")

Convenience function to convert slice to tuple?

In Python, one can write code like this, to assign multiple values from a list:
(a, b, c, d) = [1,2,3,4]
Is there a similar set of Go library function for slices? That is, I can do this:
http://play.golang.org/p/DY1Bi5omm1
package main
func get3(s []interface{}) (
a interface{},
b interface{},
c interface{},
rest []interface{}) {
return s[0],s[1],s[2],s[4:]
}
func main() {
s := make([]interface{},5);
for i :=0 ; i < 5; i++ { s[i] = i}
a,b,c,_ := get3(s)
print(a.(int))
print(b.(int))
print(c.(int))
}
Is there a standard gophery way to do this?
And is there a way around the interface{} ugliness?
I don't think you can, not in an idiomatic/clean way at least. You CAN do multiple assignments, but you will have to pass individual values either directly or with a closure:
package main
import (
"fmt"
)
func valuesFromList(list[]int,startFrom int) func() int {
i:=startFrom
return func() int {
ret := list[i]
i++
return ret
}
}
func main () {
list := []int{0,1,2,3,4,5,6,7,8,9}
yield := valuesFromList(list,5)
//This works
a,b,c := yield(),yield(),yield()
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
//This also works
d,e,f := list[0],list[1],list[2]
fmt.Println(d)
fmt.Println(e)
fmt.Println(f)
//This won't work
//g,h,i:= list[7:9]
}
Not like that; you would need dynamic typing or parametric polymorphism, which are not available in Go. The closest I can think about is by fiddling with reflect, like this: http://play.golang.org/p/-K4jh2nZjq
// src is supposed to be []T.
// dst are supposed to be &T, except the last one, which must be a 'rest' &[]T (or nil for discarding).
// There must not be more dst vars than elements in src.
func extract(src interface{}, dst ...interface{}) {
srcV := reflect.ValueOf(src)
// Iterate over dst vars until we run out of them.
i := 0
for i = 0; i < len(dst)-1; i++ {
reflect.Indirect(reflect.ValueOf(dst[i])).Set(srcV.Index(i))
}
// Now, the rest.
restDst := dst[i]
if restDst == nil {
return
}
restV := reflect.ValueOf(restDst)
indirectRest := reflect.Indirect(restV)
l := srcV.Len() - i
indirectRest.Set(reflect.MakeSlice(restV.Type().Elem(), 0, l))
for ; i < srcV.Len(); i++ {
itemV := srcV.Index(i)
indirectRest.Set(reflect.Append(indirectRest, itemV))
}
return
}
Which then you call like:
sl := []int{1, 2, 3, 4, 5, 6} // int or any other type
var a, b, c int
var rest []int
extract(sl, &a, &b, &c, &rest)
So the ugliness doesn't get out the function.
But note that all that happens at runtime, so it's not safe nor efficient and definitely is not idiomatic Go.

Resources