Understanding the doc (why can't I?)

157 views
Skip to first unread message

1955...@gmail.com

unread,
Oct 15, 2018, 10:06:58 AM10/15/18
to golang-nuts
Here's the doc for shuffle in math/random:

func Shuffle(n int, swap func(i, j int))

Shuffle pseudo-randomizes the order of elements using the default Source. n is the number of elements. Shuffle panics if n < 0. swap swaps the elements with indexes i and j.


And here's the example:

package main

import (
    "fmt"
    "math/rand"
    "strings"
)

func main() {
    words := strings.Fields("ink runs from the corners of my mouth")
    rand.Shuffle(len(words), func(i, j int) {
        words[i], words[j] = words[j], words[i]
    })
    fmt.Println(words)

}

Why can't I understand a word of it?
(This is not atypical, by the way - and not just for go - so if someone can educate me, I might suddenly start prospering in the software game)

Issues (for me):
  • Shuffle doesn't seem to swap anything (the thing you want shuffled isn't even an argument)
  • As I read it the example, it should only swap two words (index i and j) but it seems to shuffle the everything
  • What's this bloody "func" thing???
  • Why doesn't "swap" appear in the example?
Yours sincerely,
Confused

Jan Mercl

unread,
Oct 15, 2018, 10:18:50 AM10/15/18
to 1955...@gmail.com, golang-nuts



On Mon, Oct 15, 2018 at 4:06 PM <1955...@gmail.com> wrote:

> Issues (for me):
>
> Shuffle doesn't seem to swap anything (the thing you want shuffled isn't even an argument)

It shuffle things by calling the function literal passed to rand.Shufle. Above it is `func(i, j int) { words[i], words[j] = words[j], words[i] }`. the `words` slice is not an argument, it's passed as part of a closure of the anonymous function.

> As I read it the example, it should only swap two words (index i and j) but it seems to shuffle the everything

It swaps only two words. But Shuffle calls the swap function several times with different indices.

> What's this bloody "func" thing???

It's a keyword that introduces a definition of a function type or a named or anonymous function.

> Why doesn't "swap" appear in the example?

It does. 'swap' is the declared name of the function to pass to rand.Shuffle. The actual argument might be, for example, `foo`, `bar` or a function literal (anonymous function) as seen above.

Also, see the source of rand.Shuffle here: https://golang.org/src/math/rand/rand.go?s=7456:7506#L225

--

-j

Chris Hopkins

unread,
Oct 15, 2018, 12:38:13 PM10/15/18
to golang-nuts
I've edited the example very slightly at:

Let's break this down.
Think about how you might shuffle a pack of cards, a simple way is to swap 2 cards around at a time. As long as you exchange enough cards enough times and both cards you chose are randomly picked, eventually you'll have shuffled the pack. Please be happy with that as a concept before going further.

The rand.Shuffle therefore doesn't care about what it has to shuffle. All it "thinks" about is: swap element 2 and 7, swap element 24 and 9 etc. (the numbers aren't important here, just that anything in an array can be shuffled by giving these sorts of instructions). All it needs to know is how long the array it has to shuffle is, and "who" can swap things around. I'll repeat that as it might not be obvious. It doesn't have to know how to swap things, it doesn;t have to know anything about the things. If I told you to shuffle 52 cards by shouting out numbers, the exact same instruction if you were instead shuffling 52 cars, or airplanes, or dogs; it's the number of things in the list and the numbers given as instructions that matter. It therefore only has to know of a function that can do this swap around. So we also pass it what I have called swapper. Who is an agent who you say, swap 2 and 7, 24 and 9, etc and it will do that.
So it can call swapper with i and j set to two values and swapper does the swap.

So how does this play into go:
Well we've declared the initial array as an array of words.
We've now declared swapper as someone who can carry out the function of swapping any 2 items in that array, that is the item at location i gets put at the position j was and the item at location j gets put where i was. I explicitly declared swapper as a func that takes two integers as that's the only thing rand.Shuffle knows or cars about the function. It just calls it a whole bunch of times, doesn't pay attention to anything it does, it just trusts it to do what needs to be done.
So as far as rand.Shuffle goes, it needs how many items are in the array, and a function that it can call with two integer parameters. so we sat to rand.shuffle what the length of the array is, and to use swapper.

And that's it. Don't worry about how rand.Shuffle works (until you want to). Yes it's a good bit of computer science to understand how it works, but it's also good computer science to accept that you don't need to know how things work under the hood either.

If that overly wordy version helps?

Chris

Neil Higgins

unread,
Oct 15, 2018, 6:31:53 PM10/15/18
to Chris Hopkins, golang-nuts
Well, ok. But I would call “Shuffle” a misleading misnomer, because until the user defines a shuffler function (which perversely might not, or might fail to, shuffle anything), it does not shuffle anything.

Thanks for taking the time to answer my question. Neil
--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/ICTwCqa8SgE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

andrey mirtchovski

unread,
Oct 15, 2018, 6:39:40 PM10/15/18
to 1955...@gmail.com, cbeho...@gmail.com, golang-nuts
> Well, ok. But I would call “Shuffle” a misleading misnomer, because until the user defines a shuffler function (which perversely might not, or might fail to, shuffle anything), it does not shuffle anything.

how would you implement shuffle in golang so that it's not a
misleading misnomer? i would presume that you would give Shuffle a
slice and expect it to return it shuffled? but then if the slice is
empty, Shuffle has still not shuffled anything :)

Neil Higgins

unread,
Oct 15, 2018, 6:49:19 PM10/15/18
to Chris Hopkins, golang-nuts
So as well as getting rid of the euphemistic name, the documentation should simply say that it delivers n pairs of random numbers in the relevant range to a user-defined function.

Neil Higgins (iPhone)
higgins...@bigpond.com

Dan Kortschak

unread,
Oct 15, 2018, 8:21:08 PM10/15/18
to Neil Higgins, Chris Hopkins, golang-nuts
But this is not really what it does. You can see from the output of
this code https://play.golang.org/p/88Llo7zHTeK

```
package main

import (
"fmt"
"math/rand"
)

func main() {
rand.Shuffle(10, func(i, j int) {
fmt.Println(i, j)
})
}
```

That `i` is not sampled from a random distribution, but in fact counts
down from the last index.

This is an implementation of the Fisher-Yates shuffle https://golang.or
g/src/math/rand/rand.go?s=7456:7506#L225

The reason that rand.Shuffle is called Shuffle is that that is it's
intention. Just as sort.Sort has the intention of sorting things, but
needn't necessarily (https://play.golang.org/p/VdMuiFfcp6w).

If we want to get metaphysical, nothing that we get the machine todo
intrinsically means anything beyond what meaning we ascribe to it
(under some transformation or set of bases). This is why we name things
and why those names matter.

Neil Higgins

unread,
Oct 15, 2018, 8:40:08 PM10/15/18
to Dan Kortschak, Chris Hopkins, golang-nuts
I would ascribe metaphysicality to the current name, and comprehensibility to a more accurate name. In this case, “shuffling” is just an example of what can be done. Abstraction is all very nice, until any applied meaning is completely lost in mumbo-jumbo.

Neil Higgins (iPhone)
higgins...@bigpond.com

Dan Kortschak

unread,
Oct 15, 2018, 8:56:11 PM10/15/18
to Neil Higgins, Chris Hopkins, golang-nuts
I'm curious. What name would you suggest?

Note that what I said below applies here; shuffling is what is the
intended use of rand.Shuffle. It could conceivably be used for
alternative things, but then C's sprintf can be used for writing to
arbitrary memory, though that is not what is intended. Should sprintf
be renamed to "writememory"?

ApplyFisherYatesIndexPairsToFunc would be the most accurate (beyond
just making the code be the documentation), but that then depends on
the user knowing that Fisher-Yates is a shuffling mechanism in the
common case, and knowing what Fisher-Yates does in the general case.
The current situation leads someone who's looking to shuffle something
to the function, and then provides two examples to show how that works.

There is obviously some assumed common knowledge, but without that
assumption things become untenable.

On Tue, 2018-10-16 at 10:39 +1000, Neil Higgins wrote:
> I would ascribe metaphysicality to the current name, and
> comprehensibility to a more accurate name. In this case, “shuffling”
> is just an example of what can be done. Abstraction is all very nice,
> until any applied meaning is completely lost in mumbo-jumbo.
>
> Neil Higgins (iPhone)
> higgins...@bigpond.com
>
> >
> > On 16 Oct 2018, at 10:20 am, Dan Kortschak <dan.kortschak@adelaide.

Neil Higgins

unread,
Oct 15, 2018, 9:45:25 PM10/15/18
to Dan Kortschak, Chris Hopkins, golang-nuts
Ok, Dan. With what you have told me, I acknowledge that shuffling is what it’s all about, so the metaphysics matches the physics on this case. So the problem is on my side: Probably a deficit in fluency with idiomatic code.

Bakul Shah

unread,
Oct 15, 2018, 10:04:35 PM10/15/18
to Neil Higgins, Dan Kortschak, Chris Hopkins, golang-nuts
On Oct 15, 2018, at 6:44 PM, Neil Higgins <1955...@gmail.com> wrote:
>
> Ok, Dan. With what you have told me, I acknowledge that shuffling is what it’s all about, so the metaphysics matches the physics on this case. So the problem is on my side: Probably a deficit in fluency with idiomatic code.

May be it ought to be called FYShuffle? The strange prefix is indicates the user better read the documentation -- this is not like shuffling cards. Just as various hash functions are typically associated with weird names. e.g FNV hash (Fowler-Noll-Vo hash), murmur hash and so on.

andrey mirtchovski

unread,
Oct 15, 2018, 10:39:40 PM10/15/18
to Bakul Shah, Neil Higgins, kortschak, Chris Hopkins, golang-nuts
> May be it ought to be called FYShuffle?

then we'ld have to rename it if we switched the algorithm (which has
happened once for sort.Sort already). that's not what go is about :)

maybe you're advocating for implementing a Shuffle interface, which
brings us round about to where we are right now :)

Bakul Shah

unread,
Oct 15, 2018, 10:59:04 PM10/15/18
to andrey mirtchovski, Neil Higgins, kortschak, Chris Hopkins, golang-nuts
On Mon, 15 Oct 2018 20:39:11 -0600 andrey mirtchovski <mirtc...@gmail.com> wrote:
> > May be it ought to be called FYShuffle?
>
> then we'ld have to rename it if we switched the algorithm (which has
> happened once for sort.Sort already). that's not what go is about :)

Unlikely :-)

The following is much less obscure.

func Shuffle(slice inteface{})

& might have more more sense. e.g.

var cards []card
...
rand.Shuffle(cards)


The current Shuffle is confusing. May be because it has a
somewhat clumsy interface.

> maybe you're advocating for implementing a Shuffle interface, which
> brings us round about to where we are right now :)

I'll shuffle off now....

1955...@gmail.com

unread,
Oct 15, 2018, 11:06:17 PM10/15/18
to golang-nuts

Oh well! Bakul - thank you for that little bit of affirmation. I feel better now :-)

andrey mirtchovski

unread,
Oct 15, 2018, 11:29:37 PM10/15/18
to Bakul Shah, Neil Higgins, kortschak, Chris Hopkins, golang-nuts
> Unlikely :-)
>
> The following is much less obscure.
>
> func Shuffle(slice inteface{})
>
> & might have more more sense. e.g.
>
> var cards []card
> ...
> rand.Shuffle(cards)

you've now restricted Shuffle to "shuffling" only slices. and it has
to examine interface{} to determine if it's "shuffle-able". calling
Shuffle(nil) in your example would still not do anything (which is one
of the original issues of OP). i posit that the current shuffle makes
good sense given the language. metaphysics notwithstanding.

Dan Kortschak

unread,
Oct 15, 2018, 11:46:49 PM10/15/18
to Bakul Shah, andrey mirtchovski, Neil Higgins, Chris Hopkins, golang-nuts
type Swapper interface {
// Swap swaps the elements i and j.
Swap(i, j int)
}

func Shuffle(s Swapper)

On Mon, 2018-10-15 at 19:58 -0700, Bakul Shah wrote:
> On Mon, 15 Oct 2018 20:39:11 -0600 andrey mirtchovski <mirtchovski@gm

Bakul Shah

unread,
Oct 15, 2018, 11:52:45 PM10/15/18
to andrey mirtchovski, Neil Higgins, kortschak, Chris Hopkins, golang-nuts
On Mon, 15 Oct 2018 21:29:07 -0600 andrey mirtchovski <mirtc...@gmail.com> wrote:
> > Unlikely :-)
> >
> > The following is much less obscure.
> >
> > func Shuffle(slice inteface{})
> >
> > & might have more more sense. e.g.
> >
> > var cards []card
> > ...
> > rand.Shuffle(cards)
>
> you've now restricted Shuffle to "shuffling" only slices. and it has

The current Shuffle is calling its func argument with indices
which make most sense for slices! What other use has it been
put to?

> to examine interface{} to determine if it's "shuffle-able". calling
> Shuffle(nil) in your example would still not do anything (which is one
> of the original issues of OP). i posit that the current shuffle makes
> good sense given the language. metaphysics notwithstanding.

No, the interface will be examined to find a swap interface.
Something like

rv := reflect.ValueOf(slice)
swap = reflect.Swapper(slice)
Shuffle(rv.Len(), swap) // current Shuffle

The same as in Sort.Slice().

All in the name of a little bit of efficiency.

Dan Kortschak

unread,
Oct 16, 2018, 12:30:04 AM10/16/18
to Bakul Shah, andrey mirtchovski, Neil Higgins, Chris Hopkins, golang-nuts
¡left off the Len method!

type Swapper interface {
// Swap swaps the elements i and j.
Swap(i, j int)

// Len returns the number of elements that may be swapped.
Len() int
}

func Shuffle(s Swapper)

roger peppe

unread,
Oct 16, 2018, 3:51:34 AM10/16/18
to kortschak, Bakul Shah, andrey mirtchovski, 1955...@gmail.com, cbeho...@gmail.com, golang-nuts
I agree that rand.Shuffle could have used an interface type that's a
subset of sort.Interface. I'm not sure why it didn't, although of
course it's easy to go from one to the other:

func main() {
x := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
xi := sort.IntSlice(x)
rand.Shuffle(xi.Len(), xi.Swap)
fmt.Println(x)
}

As for passing the slice directly, I think it's better to keep APIs
type-safe, at least to start with.
If we find ourselves writing out the swap function many times, then we
could add a rand.ShuffleSlice function along the same lines as
sort.Slice:

func ShuffleSlice(x interface{}) {
rand.Shuffle(reflect.ValueOf(x).Len(), reflect.Swapper(x))
}

As others have pointed out, this is strictly less powerful than the
current rand.Shuffle. For example, you can't shuffle two slices at
once (not that uncommon if you're storing data column-oriented for
cache friendliness).
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages