5
\$\begingroup\$

I am currently discovering Golang, and I try to implement some random variable generation functions similar to R functions.

To avoid code repetition, I'd like to use inheritance, which is quite complex with Go at first sight.

Each variable implements a x.R() method, which generate a random value following x distribution.

I'd like to implement x.Rn(n int), which simply returns an array of n random values. It is therefore the same for any random variable, as it simply calls x.R() n times.

So far, my implementation is as following:

// Continuouser Implements a continous law
type Continuouser interface{
    R() float64
    D(value float64) float64
    P(value float64) float64
}
// Continuous A Continuous law
type Continuous struct{
    Continuouser
}
// Rn Generate n draws of a continuous random variable
func (c Continuous) Rn(n int) (res []float64){
    if (n < 0){
        panic(errors.New("n must be positive"))
    }
    res = make([]float64,n)
    for i := 0; i < n; i++{
        res[i] = c.R()
    }
    return
}

And here is how I implement a law :

// NewBern Generate a new variable following a Bern(p) law
func NewBern(p float64) (x *Bern){
    x = &Bern{Discrete{},p,1.-p}
    // Discrete.discreter self reference X
    x.Discrete = Discrete{x}
    return
}

// R Generate a random value following the same Bernouilli distribution as x
func (x Bern) R() int64 {
    var u = NewStdUnif()
    // maybe there is a more idomatic way link int(u.R()<p)
    if u.R() < x.p {
        return 1
    }
    return  0
}

So, x does embed an anonymous continuous struct, which in turn embeds a reference to x, that implements the method R().

You can have access to the full repo here on GitHub.

It does work and avoid code redundancy, but is that the idiomatic Golang way?

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Style comments

On your main question

What you have works, but consider if one caller of your library does:

cont := Continuous{}
cont.Rn(42)

If you can't guess, try it; it's a good way to learn =) Anyway, you don't want that to happen. So you can do this several ways. Before I'm going into two idiomatic options, let's talk about your interface definitions:

  • Rn only needs something that implements R, so you could probably have an interface only for this. Let's call this interface Law.
  • Your Continuouser should simply be called Continuous or ContinuousLaw (because it describes what a continuous law does). In its definition, replace the R() float64 by Law.
  • Your Continuous should be called MultiDrawer or something. The only reason you're defining it is because you want to draw multiple values; so name it accordingly.

Option 1:

func Rn(law Law, n int) []float64

It's very simple; what you're saying here is "give me a Law, I can give you n random values from it".

Option 2:

interface MultiDrawer {
    Rn(n int) []float64
}

struct multiDrawer { // unexported: you don't want callers to touch this directly
    Law
}

func NewMultiDrawer(law Law) MultiDrawer {
    return &multiDrawer(law)
}

func (md *multiDrawer) Rn(n int) []float64 {
    // your code using md.R()
}

You're saying something similar here: "give me a Law and I give you a MultiDrawer". It's better if you want to have a concept of a MultiDrawer variable that you'll later pass around or reuse.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.