package main import ( "errors" "fmt" "math" ) type Step struct { Max float64 Rate float64 } type Rule interface { Compute(quantity float64) (float64, error) } type VolumeRule []Step func (s VolumeRule) Compute(quantity float64) (float64, error) { for _, step := range s { if quantity < step.Max { return quantity * step.Rate, nil } } return 0, errors.New("quantity too huge") } type PiecewiseLinearRule []Step func (s PiecewiseLinearRule) Compute(quantity float64) (float64, error) { total := 0.0 prev_max_qty := 0.0 for _, step := range s { effective_qty := math.Min(quantity, step.Max) total += step.Rate * (effective_qty - prev_max_qty) if quantity < step.Max { break } prev_max_qty = step.Max } return total, nil } var rates = map[string]Rule{ "weight": PiecewiseLinearRule{{10, 1}, {15, 2}, {math.Inf(1), 10}}, // Alternately we can be more explicit: "crew": PiecewiseLinearRule{{Max: math.Inf(1), Rate: 5}}, "volume": VolumeRule{{3, 2}, {7, 5}, {1000, 21}}, } type Shipment map[string]float64 func Freight(s Shipment, field string) (float64, error) { return rates[field].Compute(s[field]) } func main() { fmt.Println(Freight(Shipment{"weight": 4.5, "crew": 5, "volume": 37}, "crew")) fmt.Println(Freight(Shipment{"weight": 4.5, "crew": 5, "volume": 37}, "volume")) fmt.Println(Freight(Shipment{"weight": 20, "crew": 5, "volume": 37}, "weight")) fmt.Println(Freight(Shipment{"weight": 20, "crew": 5, "volume": 37000}, "volume")) }