Optional parameters: Difference between revisions

→‎{{header|Go}}: add functional options technique
(→‎{{header|Go}}: add functional options technique)
Line 899:
 
=={{header|Go}}==
Go does not have optional parameters so we list the idiomatic alternatives and some less idiomatic ways of achieving an effect similar to optional parameters.
The most idiomatic way to write this particular sorting function would be a single function that required all three parameters. Wherever practical in Go, the zero value is used as a default, and that seems meaningful in this situation. Calling t.sort(nil, 0, false) to "take the defaults" would make sense. This approach is probably closest to "positional parameters" mentioned in the task description.
 
===Idiomatic (non-solutions)===
In the spirit of the task though, another solution would be to pass a struct with the three "parameters" as fields. While Go does not have named function parameters, it ''does'' have named fields in struct literals. Given,
 
'''Zero values'''
 
The most idiomatic way to write this particular sorting function would be a single function that required all three parameters. Wherever practical in Go, the zero value is used as a default, and that seems meaningful in this situation. Given a table t with method <tt>(table) sort(less func(cell, cell) bool, column int, reverse bool)</tt>, calling <tt>t.sort(nil, 0, false)</tt> to "take the defaults" would make sense. This approach is probably closest to "positional parameters" mentioned in the task description. Note an idiomatic way of specifying an ordering in Go is to provide a "less" function, a function that takes two values and returns true if the the first is "less than" the second in whatever sense specifies the ordering.
 
'''Struct'''
 
As the number of optional parameters grows, at some point it can be easier to pass a struct containing all of the "parameters". An advantage with this is that you can write a constructor function that sets defaults other than zero values. This is also idiomatic.
 
Here is a partial example, partial because it doesn't really have the feel yet of "optional parameters." Note the call to do the reverse sort takes three lines of code, one to construct the parameter struct, one to set the option, and one more to make the call.
 
<lang go>type cell string
 
type spec struct {
less func(cell, cell) bool
column int
reverse bool
}
 
func newSpec() (s spec) {
// initialize any defaults
return
}
 
// sort with all defaults
t.sort(newSpec())
 
// reverse sort
s := newSpec
s.reverse = true
t.sort(s)</lang>
 
===Struct literal with keyed elements===
 
A solution providing more the feel of optional parameters is to pass a struct literal. Go allows a
struct literal to be initialized with named fields but does not require all fields to be specified and does not require them to be specified in order. Thus passing a struct literal can provide very much the feel of optional named parameters. Given,
<lang go>type spec struct {
ordering func(cell, cell) bool
Line 911 ⟶ 947:
Structs in Go are values and are copied when passed as parameters. The result of having a single struct parameter is that the three fields are pushed on the stack, just about like they would if they were separate parameters. The effect is named parameters with unmentioned parameters defaulting to their zero value.
 
While the effect is close to that of optional parameters, Go idioms have evolved to make this technique quite non-idiomatic. The very popular tool <tt>go vet</tt> issues a warning if a struct literal only initializes a partial set of elements. Popular code grading services on the internet run go vet and will give your code a lower grade for using this technique.
A complete program to demonstrate:
 
Nevertheless, a complete program to demonstrate:
<lang go>package main
 
Line 1,035 ⟶ 1,073:
["pail" "food"]
</pre>
 
===Functional options===
A technique that gets a nod of approval from the idiom police is sometimes termed "functional options." This technique involves a bit of tricky machinery though and so has not really gained wide popularity. It makes use of Go's variadic arguments and uses functions to initialize a parameter struct. A full solution:
 
<lang go>package main
 
import (
"fmt"
"sort"
)
 
type cell string
type row []cell
type table struct {
rows []row
column int
less func(cell, cell) bool
}
 
func (c cell) String() string {
return fmt.Sprintf("%q", string(c))
}
 
func (t table) printRows(heading string) {
fmt.Println("--", heading)
for _, row := range t.rows {
fmt.Println(row)
}
fmt.Println()
}
 
// sort.Interface
func (t table) Len() int { return len(t.rows) }
func (t table) Swap(i, j int) { t.rows[i], t.rows[j] = t.rows[j], t.rows[i] }
func (t table) Less(i, j int) bool {
return t.less(t.rows[i][t.column], t.rows[j][t.column])
}
 
// struct implements named parameter-like capability
type spec struct {
ordering func(cell, cell) bool
column int
reverse bool
}
 
// A defined option type is not really needed by the technique, but has
// a nice advantage for documentation. If this type is exported, then
// the the Go documentation tool go doc will organize all of the option
// functions together under the type. (Go doc will see them as constructors
// for the type.)
type Option func(*spec)
 
func ordering(o func(cell, cell) bool) Option {
return func(s *spec) { s.ordering = o }
}
 
func column(c int) Option {
return func(s *spec) { s.column = c }
}
 
func reverse() Option {
return func(s *spec) { s.reverse = true }
}
 
func (t *table) sort(options ...Option) {
var s spec
for _, o := range options {
o(&s)
}
// set up column and comparison function for sort
t.column = s.column
switch {
case s.ordering != nil:
t.less = s.ordering
case s.reverse:
t.less = func(a, b cell) bool { return a > b }
default:
t.less = func(a, b cell) bool { return a < b }
}
 
// sort
sort.Sort(t)
 
// reverse if necessary
if s.ordering == nil || !s.reverse {
return
}
last := len(t.rows) - 1
for i := last / 2; i >= 0; i-- {
t.rows[i], t.rows[last-i] = t.rows[last-i], t.rows[i]
}
}
 
func main() {
t := table{rows: []row{
{"pail", "food"},
{"pillbox", "nurse maids"},
{"suitcase", "airedales"},
{"bathtub", "chocolate"},
{"schooner", "ice cream sodas"},
}}
 
t.printRows("song")
// no parameters
t.sort()
t.printRows("sorted on first column")
 
// "named parameter" reverse.
t.sort(reverse())
t.printRows("reverse sorted on first column")
 
// "named parameters" column and ordering
byLen := func(a, b cell) bool { return len(a) > len(b) }
t.sort(column(1), ordering(byLen))
t.printRows("sorted by descending string length on second column")
}</lang>
Output same as previous solution.
 
=={{header|Groovy}}==
1,707

edits