patterngoMinor
Random distributions in Go with inheritance
Viewed 0 times
randomdistributionswithinheritance
Problem
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
I'd like to implement
So far, my implementation is as following:
And here is how I implement a law :
So,
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?
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?
Solution
Style comments
On your main question
What you have works, but consider if one caller of your library does:
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:
Option 1:
It's very simple; what you're saying here is "give me a
Option 2:
You're saying something similar here: "give me a
- Don't panic. Return the incorrect value of
nin 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:
Rnonly needs something that implementsR, so you could probably have an interface only for this. Let's call this interfaceLaw.
- Your
Continuousershould simply be calledContinuousorContinuousLaw(because it describes what a continuous law does). In its definition, replace theR() float64byLaw.
- Your
Continuousshould be calledMultiDraweror 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) []float64It'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.Code Snippets
cont := Continuous{}
cont.Rn(42)func Rn(law Law, n int) []float64interface 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()
}Context
StackExchange Code Review Q#125751, answer score: 3
Revisions (0)
No revisions yet.