1

I've been trying to implement a function which can randomly select an element from any type of slice (like python's random.choice function)

func RandomChoice(a []interface{}, r *rand.Rand) interface{} {
    i := r.Int()%len(a)
    return a[i]
}

However, when I try to pass in a slice of type []float32 into the first argument this error occurs:

cannot use my_array (type []float32) as type []interface {} in function argument

is this a fundemental misuse of interface{}? is there some better way to do this?

kellpossible
  • 663
  • 1
  • 8
  • 19

4 Answers4

5

Re: is there some better way to do this?

IMO there is. The OP approach is inefficient wrt to simple:

var v []T
...

// Select a random element of 'v'
e := v[r.Intn(len(v))]   
...

Note that both of the approaches will panic for len(v) == 0 until a pre-check for this is made.

zzzz
  • 87,403
  • 16
  • 175
  • 139
  • Thanks for the answer! Although I'm not entirely sure I understand the lingo. Are you suggesting that it would be better not to use a function like RandomChoice at all? – kellpossible Feb 28 '13 at 08:42
  • 2
    Yes, that's what I mean. No type conversions/assertions, statically type checked and better run time performance for writing about a dozen characters in-line. Which is perhaps even less typing than `e := RandomChoice(a, r).(T)` anyway. – zzzz Feb 28 '13 at 09:07
2

Using reflection:

func RandomChoice(slice interface{}, r *rand.Rand) interface{} {
    x := reflect.ValueOf(slice)
    return x.Index(r.Intn(x.Len())).Interface()
}
newacct
  • 119,665
  • 29
  • 163
  • 224
1

From the language specification:

Two types are either identical or different.

Two named types are identical if their type names originate in the same TypeSpec. A named and an unnamed type are always different. Two unnamed types are identical if the corresponding type literals are identical, that is, if they have the same literal structure and corresponding components have identical types. In detail:

  • Two array types are identical if they have identical element types and the same array length.
  • Two slice types are identical if they have identical element types.
  • Two struct types are identical if they have the same sequence of fields, and if corresponding fields have the same names, and identical types, and identical tags. Two anonymous fields are considered to have the same name. Lower-case field names from different packages are always different.
  • Two pointer types are identical if they have identical base types.
  • Two function types are identical if they have the same number of parameters and result values, corresponding parameter and result types are identical, and either both functions are variadic or neither is. Parameter and result names are not required to match.
  • Two interface types are identical if they have the same set of methods with the same names and identical function types. Lower-case method names from different packages are always different. The order of the methods is irrelevant.
  • Two map types are identical if they have identical key and value types.
  • Two channel types are identical if they have identical value types and the same direction.

And:

A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:

  • x's type is identical to T.
  • x's type V and T have identical underlying types and at least one of V or T is not a named type.
  • T is an interface type and x implements T.
  • x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a named type.
  • x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
  • x is an untyped constant representable by a value of type T.

Any value may be assigned to the blank identifier.

The combination of these two results that you can't assign a []MyType to an []interface{}.

Community
  • 1
  • 1
Kocka
  • 1,634
  • 2
  • 13
  • 21
  • Thankyou for your answer to part of my question. I also like the quote from Stephen on the link from my answer: "In Go, there is a general rule that syntax should not hide complex/costly operations." – kellpossible Feb 28 '13 at 08:44
0

Well, I can hardly believe after all the searching, that the first listed related question was: Type converting slices of interfaces in go which pretty much contains the answer. Changing RandomChoice to use the InterfaceSlice function described in the answer to that question yields:

func RandomChoice(slice interface{}, r *rand.Rand) interface{} {
    islice := InterfaceSlice(slice)
    i := r.Int()%len(islice)
    return islice[i] 
}

although apparently this answer is not very well performing, because it requires the entire slice to be converted to []interface{}...

Community
  • 1
  • 1
kellpossible
  • 663
  • 1
  • 8
  • 19