I would like to choose a part of the generated document randomly based on the given weights, something like this pseudo-code:
{{prob 50}}
This will appear with probability 50%.
{{prob 30}}
This will appear with probability 30%.
{{prob 20}}
You got the idea.
{{endprob}}
The best things that has come to my mind so far is:
{{choose . "template1" 50 "template2" 30 "template3" 20}}
where choose
is my function that belongs to FuncMap
. The current template is passed to itself as e.g. .T
and templateN
are the associated templates. The function would choose the template, look it up in .T
and render with .
. Another similar option would be to pass templateN
as part of .
directly.
I wonder if there's a more elegant / less hackish way? I guess, it's not possible to create custom actions in text/template
, is it?
The standard template packages do not support custom actions.
You can get close to your proposed {{prob}}/{{endprob}}
action using a function map and the built-in {{if}}
action.
Convert the weights to non-overlapping ranges in [0.0,1.0). Use a random number in [0.0,1.0) to select an alternative using an if action in the template.
var t = template.Must(template.New("").
Funcs(template.FuncMap{"rand": rand.Float64}).
Parse(`
{{$r := rand}}
{{if le $r 0.5}}
This will appear with probability 50%.
{{else if le $r 0.8}}
This will appear with probability 30%.
{{else}}
You got the idea.
{{end}}`))
Here's an alternative that allows you to specify weights in the template instead of ranges:
type randPicker struct {
n float64 // random number in [0.0,1.0)
t float64 // total of weights so far
}
func (rp *randPicker) Weight(w float64) bool {
rp.t += w
return rp.t <= rp.n
}
func newRandPicker() *randPicker {
return &randPicker{n: rand.Float64()}
}
var t = template.Must(template.New("").
Funcs(template.FuncMap{"rp": newRandPicker}).
Parse(`
{{$rp := rp}}
{{if rp.Weight 0.50}}
This will appear with probability 50%.
{{else if rp.Weight 0.30}}
This will appear with probability 30%.
{{else}}
You got the idea
{{end}}`))