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?
1 Answer 1
Style comments
- Don't panic. Return the incorrect value of
n
in your error usingfmt.Errorf
. - Use gofmt on your code. (Have your favorite editor call it on-save.)
- There is no better way to convert a bool to an int than what you have.
- Replace
var u = ...
byu := ...
. - No need to use named returned parameters in Rn.
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 implementsR
, so you could probably have an interface only for this. Let's call this interfaceLaw
.- Your
Continuouser
should simply be calledContinuous
orContinuousLaw
(because it describes what a continuous law does). In its definition, replace theR() float64
byLaw
. - Your
Continuous
should be calledMultiDrawer
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.
Explore related questions
See similar questions with these tags.