golang return multiple values issue - function

I was wondering why this is valid go code:
func FindUserInfo(id string) (Info, bool) {
it, present := all[id]
return it, present
}
but this isn't
func FindUserInfo(id string) (Info, bool) {
return all[id]
}
is there a way to avoid the temporary variables?

To elaborate on my comment, the Effective Go mentions that the multi-value assignment from accessing a map key is called the "comma ok" pattern.
Sometimes you need to distinguish a missing entry from a zero value. Is there an entry for "UTC" or is that the empty string because it's not in the map at all? You can discriminate with a form of multiple assignment.
var seconds int
var ok bool
seconds, ok = timeZone[tz]
For obvious reasons this is called the “comma ok” idiom. In this example, if tz is present, seconds will be set appropriately and ok will be true; if not, seconds will be set to zero and ok will be false.
Playground demonstrating this
We can see that this differs from calling a regular function where the compiler would tell you that something is wrong:
package main
import "fmt"
func multiValueReturn() (int, int) {
return 0, 0
}
func main() {
fmt.Println(multiValueReturn)
asgn1, _ := multiValueReturn()
asgn2 := multiValueReturn()
}
On the playground this will output
# command-line-arguments
/tmp/sandbox592492597/main.go:14: multiple-value multiValueReturn() in single-value context
This gives us a hint that it may be something the compiler is doing. Searching the source code for "commaOk" gives us a few places to look, including types.unpack
At the time of writing this it this the method's godoc reads:
// unpack takes a getter get and a number of operands n. If n == 1, unpack
// calls the incoming getter for the first operand. If that operand is
// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a
// function call, or a comma-ok expression and allowCommaOk is set, the result
// is a new getter and operand count providing access to the function results,
// or comma-ok values, respectively. The third result value reports if it
// is indeed the comma-ok case. In all other cases, the incoming getter and
// operand count are returned unchanged, and the third result value is false.
//
// In other words, if there's exactly one operand that - after type-checking
// by calling get - stands for multiple operands, the resulting getter provides
// access to those operands instead.
//
// If the returned getter is called at most once for a given operand index i
// (including i == 0), that operand is guaranteed to cause only one call of
// the incoming getter with that i.
//
The key bits of this being that this method appears to determine whether or not something is actually a "comma ok" case.
Digging into that method tells us that it will check to see if the mode of the operands is indexing a map or if the mode is set to commaok (where this is defined does give us many hints on when it's used, but searching the source for assignments to commaok we can see it's used when getting a value from a channel and type assertions). Remember the bolded bit for later!
if x0.mode == mapindex || x0.mode == commaok {
// comma-ok value
if allowCommaOk {
a := [2]Type{x0.typ, Typ[UntypedBool]}
return func(x *operand, i int) {
x.mode = value
x.expr = x0.expr
x.typ = a[i]
}, 2, true
}
x0.mode = value
}
allowCommaOk is a parameter to the function. Checking out where unpack is called in that file we can see that all callers pass false as an argument. Searching the rest of the repository leads us to assignments.go in the Checker.initVars() method.
l := len(lhs)
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
Since it seems that we can only use the "comma ok" pattern to get two return values when doing a multi-value assignment this seems like the right place to look! In the above code the length of the left hand side is checked, and when unpack is called the allowCommaOk parameter is the result of l == 2 && !returnPos.IsValid(). The !returnPos.IsValid() is somewhat confusing here as that would mean that the position has no file or line information associated with it, but we'll just ignore that.
Further down in that method we've got:
var x operand
if commaOk {
var a [2]Type
for i := range a {
get(&x, i)
a[i] = check.initVar(lhs[i], &x, returnPos.IsValid())
}
check.recordCommaOkTypes(rhs[0], a)
return
}
So what does all of this tell us?
Since the unpack method takes an allowCommaOk parameter that's hardcoded to false everywhere except in assignment.go's Checker.initVars() method, we can probably assume that you will only ever get two values when doing an assignment and have two variables on the left-hand side.
The unpack method will determine whether or not you actually do get an ok value in return by checking if you are indexing a slice, grabbing a value from a channel, or doing a type assertion
Since you can only get the ok value when doing an assignment it looks like in your specific case you will always need to use variables

You may save a couple of key strokes by using named returns:
func FindUserInfo(id string) (i Info, ok bool) {
i, ok = all[id]
return
}
But apart from that, I don't think what you want is possible.

Simply put: the reason why your second example isn't valid Go code is because the language specification says so. ;)
Indexing a map only yields a secondary value in an assignment to two variables. Return statement is not an assignment.
An index expression on a map a of type map[K]V used in an assignment or initialization of the special form
v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]
yields an additional untyped boolean value. The value of ok is true if the key x is present in the map, and false otherwise.
Furthermore, indexing a map is not a "single call to a multi-valued function", which is one of the three ways to return values from a function (the second one, the other two not being relevant here):
There are three ways to return values from a function with a result type:
The return value or values may be explicitly listed in the "return" statement. Each expression must be single-valued and assignable to the corresponding element of the function's result type.
The expression list in the "return" statement may be a single call to a multi-valued function. The effect is as if each value returned from that function were assigned to a temporary variable with the type of the respective value, followed by a "return" statement listing these variables, at which point the rules of the previous case apply.
The expression list may be empty if the function's result type specifies names for its result parameters. The result parameters act as ordinary local variables and the function may assign values to them as necessary. The "return" statement returns the values of these variables.
As for your actual question: the only way to avoid temporary variables would be using non-temporary variables, but usually that would be quite unwise - and probably not much of an optimization even when safe.
So, why doesn't the language specification allow this kind of special use of map indexing (or type assertion or channel receive, both of which can also utilize the "comma ok" idiom) in return statements? That's a good question. My guess: to keep the language specification simple.

I'm no Go expert but I believe you are getting compile time error when you are trying to return the array i.e. return all[id]. The reason could be because the functions return type is specially mentioned as (Info, bool) and when you are doing return all[id] it can't map the return type of all[id] to (Info, bool).
However the solution mentioned above, the variables being returned i and ok are the same that are mentioned in the return type of the function (i Info, ok bool) and hence the compiler knows what it's returning as opposed to just doing (i Info, ok bool).

By default, maps in golang return a single value when accessing a key
https://blog.golang.org/go-maps-in-action
Hence, return all[id] won't compile for a function that expects 2 return values.

Related

How can I write a recursion function with a vector parameter?

I have a function that takes a vector as a parameter, scan this vector and generates a random word. It's expected from me that the generated words' letters are different from each other. So, I want to check it with a simple if-else condition inside the same function. If all letters are different, function returns this word. If not, I need to use the same function which I am already inside while using conditions. But first parameter that I used in the main function doesn't work when I attempt to use it for the second time. Here the generateaRandomWord(vector a) function:
vector<string> currentVector;
string generateaRandomWord(vector<string> a) {
currentVector = a;
string randomWord;
int randomNumber = rand() % currentVector.size();
randomWord = currentVector.at(randomNumber);
if (hasUniqueChars(randomWord)) {
return randomWord;
}
else {
generateaRandomWord(currentVector);
}
}
I thought that it is a good idea to keep a vector (currentVector) outside of the function. So, for the first time I use the function this vector will be defined and I will be able to use it if using recursion is necessary. But that didn't work either.
The main problem you have is that your recursive case doesn't return anything -- it throws away the returned value from the recursive call, then falls off the end of the function (returning garbage -- undefined behvaior). You need to actually return the value returned by the recursive call:
return generateaRandomWord(currentVector);

lua not modifying function arguments

I've been learning lua and can't seem to make a simple implementation of this binary tree work...
function createTree(tree, max)
if max > 0 then
tree = {data = max, left = {}, right = {}}
createTree(tree.left, max - 1)
createTree(tree.right, max - 1)
end
end
function printTree(tree)
if tree then
print(tree.data)
printTree(tree.left)
printTree(tree.right)
end
end
tree = {}
createTree(tree, 3)
printTree(tree)
the program just returns nil after execution. I've searched around the web to understand how argument passing works in lua (if it is by reference or by value) and found out that some types are passed by reference (like tables and functions) while others by value. Still, I made the global variable "tree" a table before passing it to the "createTree" function, and I even initialized "left" and "right" to be empty tables inside of "createTree" for the same purpose. What am I doing wrong?
It is probably necessary to initialize not by a new table, but only to set its values.
function createTree(tree, max)
if max > 0 then
tree.data = max
tree.left = {}
tree.right = {}
createTree(tree.left, max - 1)
createTree(tree.right, max - 1)
end
end
in Lua, arguments are passed by value. Assigning to an argument does not change the original variable.
Try this:
function createTree(max)
if max == 0 then
return nil
else
return {data = max, left = createTree(max-1), right = createTree(max-1)}
end
end
It is safe to think that for the most of the cases lua passes arguments by value. But for any object other than a number (numbers aren't objects actually), the "value" is actually a pointer to the said object.
When you do something like a={1,2,3} or b="asda" the values on the right are allocated somewhere dynamically, and a and b only get addresses of those. Thus, when you pass a to the function fun(a), the pointer is copied to a new variable inside function, but the a itself is unaffected:
function fun(p)
--p stores address of the same object, but `p` is not `a`
p[1]=3--by using the address you can
p[4]=1--alter the contents of the object
p[2]=nil--this will be seen outside
q={}
p={}--here you assign address of another object to the pointer
p=q--(here too)
end
Functions are also represented by pointers to them, you can use debug library to tinker with function object (change upvalues for example), this may affect how function executes, but, once again, you can not change where external references are pointing.
Strings are immutable objects, you can pass them around, there is a library that does stuff to them, but all the functions in that library return new string. So once, again external variable b from b="asda" would not be affected if you tried to do something with "asda" string inside the function.

Scala: How to write a generic check function that evaluates any function that returns boolean?

I'm struggling a bit with this: I need a function that takes any function
of type fun(Any*) : Boolean as parameter, evaluates the function and returns true or
false, depending on the success of the function evaluation.
Essentially, what I need is a function type that allows any number and any type of parameter but the function must return Boolean.
Which would allow me to write functions like:
def checkLenght(str : String, length : Int) : Boolean ={
if (str.lenght == length)}
or
def ceckAB(a : Int, b : Int) : Boolean = {
if(a < b && a >= 23 && b < 42) }
so that, for example
eval(checkLenght(abc, 3)) //returns true
eval(ceckAB(4,1)) // returns false
I thought, a function type of:
type CheckFunction = (Any*) => Boolean
may does the trick but I struggle with writing the generic eval function.
Any advise?
Thank you
Solution:
The function requires
1) Another function of return type Boolean: "(func : => Boolean)"
2) Return type Boolean ": Boolean"
3) Returns the value of the passed function-parameter: " = func"
Altogether the function is:
def eval(func : => Boolean) : Boolean = func
It amazes me over again how simple simple things are in Scala.
As pointed out by the comments, this is a rather unusual function with no obvious
sense. Just a word about the underlying reasons.
Motivation:
There were a lot of question about the underlying motivation, so here a short
summary why such a function is needed.
Essentially, there are two reasons.
First one is about moving the failure handling away from the function itself
into a handler function. This preserves the purity of the check function and even allows
re-usage of generic checks.
Second, it's all about "pluggable failure handling". This means, the eval function only
tells if a failure happened (or not). In case of a failure, a handler is called through an interface. The implementation of the handler can be swapped using profiles as required.
Why?
Swapping profiles means, I code my checks and functions as usual but by switching the
profile, I switch the handler which means I can chose between full-stop, console print out, email alert, SNMP notification, push message... you name it. To do so, I need to decouple the check function from its evaluation and from its handling. That's the motivation for such a rather strange looking eval function.
And for the sake of completeness, I've already implemented all that stuff but was I facing the limitation of only handling trivial checks i.e. check(Boolean*) which is neat but often I would prefer to write a function to do more sophisticated checks.
Solved
The function is defined by returning the value of the passed function:
def eval(func : => Boolean) : Boolean = {func}
I can't say that I really understand your motivations for wanting to do what you want to do, but I guess that's beside the point. Maybe the eval function will check something before invoking the supplied function and not invoke that other function (like a fast fail) given some certain condition. Maybe you do some post checking after invoking the function and change the result based on something else. Either way, I suppose you could accomplish something similar to what you want with code looking like this:
def main(args: Array[String]) {
val str = "hello world"
println(eval(checkLength(str, 3)))
println(eval(intsEqual(1,1)))
}
def eval(func: => Boolean):Boolean = {
//Do whetever you want before invoking func, maybe
//not even invoke it if some other condition is present
val fres = func
//Maybe change something here before returning based on post conditions
fres
}
def checkLength(s:String, len:Int) = s.length() == len
def intsEqual(a:Int, b:Int) = a == b
If you really want the eval function to be able to support any function that takes any types of args and returns a Boolean, then using a by-name function like this, and then leveraging closure inside the by-name function to pass any params along to whatever actual function you want to invoke. A better way to demonstrate this is as follows:
def checkMyString(str:String, len:Int) = {
eval(str.length == len)
}
It's probably hard to see that the check str.length == len is not invoked unless eval decides to invoke it until you expand it to it's true form:
def checkMyString(str:String, len:Int) = {
def check = {
str.length == len
}
eval(check)
}
Here, the nested function check has access to str and len due to closure, and this will allow you to get around the requirement that eval must be able to invoke a function with any params that returns a Boolean.
This is just one way to solve your problem, and it might not even be suitable given your needs, but I just wanted to throw it out there.
If your input functions only have 2 arguments, like your two examples, you can write a semi generic function take takes all functions with two arguments of any type:
def eval[A,B](func: (A,B) => Boolean, arg1: A, arg2: B) = {
func(arg1, arg2)
}
def checkLength(str: String, length: Int) : Boolean = {
str.length == length
}
eval(checkLength, "ham", 4)
res0: Boolean = false
But if you want to support functions with more arguments, you would have to write one eval function for three arguments, four arguments, etc
Maybe there is a better way that can handle all cases?

Best way to cache results of method with multiple parameters - Object as key in Dictionary?

At the beginning of a method I want to check if the method is called with these exact parameters before, and if so, return the result that was returned back then.
At first, with one parameter, I used a Dictionary, but now I need to check 3 parameters (a String, an Object and a boolean).
I tried making a custom Object like so:
var cacheKey:Object = { identifier:identifier, type:type, someBoolean:someBoolean };
//if key already exists, return it (not working)
if (resultCache[cacheKey]) return resultCache[cacheKey];
//else: create result ...
//and save it in the cache
resultCache[cacheKey] = result;
But this doesn't work, because the seccond time the function is called, the new cacheKey is not the same object as the first, even though it's properties are the same.
So my question is: is there a datatype that will check the properties of the object used as key for a matching key?
And what else is my best option? Create a cache for the keys as well? :/
Note there are two aspects to the technical solution: equality comparison and indexing.
The Cliff Notes version:
It's easy to do custom equality comparison
In order to perform indexing, you need to know more than whether one object is equal to another -- you need to know which is object is "bigger" than the other.
If all of your properties are primitives you should squash them into a single string and use an Object to keep track of them (NOT a Dictionary).
If you need to compare some of the individual properties for reference equality you're going to have a write a function to determine which set of properties is bigger than the other, and then make your own collection class that uses the output of the comparison function to implement its own a binary search tree based indexing.
If the number of unique sets of arguments is in the several hundreds or less AND you do need reference comparison for your Object argument, just use an Array and the some method to do a naive comparison to all cached keys. Only you know how expensive your actual method is, so it's up to you to decide what lookup cost (which depends on the number of unique arguments provided to the function) is acceptable.
Equality comparison
To address equality comparison it is easy enough to write some code to compare objects for the values of their properties, rather than for reference equality. The following function enforces strict set comparison, so that both objects must contain exactly the same properties (no additional properties on either object allowed) with the same values:
public static propsEqual(obj1:Object, obj2:Object):Boolean {
for(key1:* in obj1) {
if(obj2[key1] === undefined)
return false;
if(obj2[key1] != obj2[key1])
return false;
}
for(key2:* in obj2)
if(obj1[key2] === undefined)
return false;
return true;
}
You could speed it up by eliminating the second for loop with the tradeoff that {A:1, B:2} will be deemed equal to {A:1, B:2, C:'An extra property'}.
Indexing
The problem with this in your case is that you lose the indexing that a Dictionary provides for reference equality or that an Object provides for string keys. You would have to compare each new set of function arguments to the entire list of previously seen arguments, such as using Array.some. I use the field currentArgs and the method to avoid generating a new closure every time.
private var cachedArgs:Array = [];
private var currentArgs:Object;
function yourMethod(stringArg:String, objArg:Object, boolArg:Boolean):* {
currentArgs = { stringArg:stringArg, objArg:objArg, boolArg:boolArg };
var iveSeenThisBefore:Boolean = cachedArgs.some(compareToCurrent);
if(!iveSeenThisBefore)
cachedArgs.push(currentArgs);
}
function compareToCurrent(obj:Object):Boolean {
return someUtil.propsEqual(obj, currentArgs);
}
This means comparison will be O(n) time, where n is the ever increasing number of unique sets of function arguments.
If all the arguments to your function are primitive, see the very similar question In AS3, where do you draw the line between Dictionary and ArrayCollection?. The title doesn't sound very similar but the solution in the accepted answer (yes I wrote it) addresses the exact same techinical issue -- using multiple primitive values as a single compound key. The basic gist in your case would be:
private var cachedArgs:Object = {};
function yourMethod(stringArg:String, objArg:Object, boolArg:Boolean):* {
var argKey:String = stringArg + objArg.toString() + (boolArg ? 'T' : 'F');
if(cachedArgs[argKey] === undefined)
cachedArgs[argKey] = _yourMethod(stringArg, objArg, boolArg);
return cachedArgs[argKey];
}
private function _yourMethod(stringArg:String, objArg:Object, boolArg:Boolean):* {
// Do stuff
return something;
}
If you really need to determine which reference is "bigger" than another (as the Dictionary does internally) you're going to have to wade into some ugly stuff, since Adobe has not yet provided any API to retrieve the "value" / "address" of a reference. The best thing I've found so far is this interesting hack: How can I get an instance's "memory location" in ActionScript?. Without doing a bunch of performance tests I don't know if using this hack to compare references will kill the advantages gained by binary search tree indexnig. Naturally it would depend on the number of keys.

Define default values for function arguments

In the Lua wiki I found a way to define default values for missing arguments:
function myfunction(a,b,c)
b = b or 7
c = c or 5
print (a,b,c)
end
Is that the only way? The PHP style myfunction (a,b=7,c=5) does not seem to work. Not that the Lua way doesn't work, I am just wondering if this is the only way to do it.
If you want named arguments and default values like PHP or Python, you can call your function with a table constructor:
myfunction{a,b=3,c=2}
(This is seen in many places in Lua, such as the advanced forms of LuaSocket's protocol modules and constructors in IUPLua.)
The function itself could have a signature like this:
function myfunction(t)
setmetatable(t,{__index={b=7, c=5}})
local a, b, c =
t[1] or t.a,
t[2] or t.b,
t[3] or t.c
-- function continues down here...
end
Any values missing from the table of parameters will be taken from the __index table in its metatable (see the documentation on metatables).
Of course, more advanced parameter styles are possible using table constructors and functions- you can write whatever you need. For example, here is a function that constructs a function that takes named-or-positional argument tables from a table defining the parameter names and default values and a function taking a regular argument list.
As a non-language-level feature, such calls can be changed to provide new behaviors and semantics:
Variables could be made to accept more than one name
Positional variables and keyword variables can be interspersed - and defining both can give precedence to either (or cause an error)
Keyword-only positionless variables can be made, as well as nameless position-only ones
The fairly-verbose table construction could be done by parsing a string
The argument list could be used verbatim if the function is called with something other than 1 table
Some useful functions for writing argument translators are unpack (moving to table.unpack in 5.2), setfenv (deprecated in 5.2 with the new _ENV construction), and select (which returns a single value from a given argument list, or the length of the list with '#').
In my opinion there isn't another way. That's just the Lua mentality: no frills, and except for some syntactic sugar, no redundant ways of doing simple things.
Technically, there's b = b == nil and 7 or b (which should be used in the case where false is a valid value as false or 7 evaluates to 7), but that's probably not what you're looking for.
The only way i've found so far that makes any sense is to do something like this:
function new(params)
params = params or {}
options = {
name = "Object name"
}
for k,v in pairs(params) do options[k] = v end
some_var = options.name
end
new({ name = "test" })
new()
If your function expects neither Boolean false nor nil to be passed as parameter values, your suggested approach is fine:
function test1(param)
local default = 10
param = param or default
return param
end
--[[
test1(): [10]
test1(nil): [10]
test1(true): [true]
test1(false): [10]
]]
If your function allows Boolean false, but not nil, to be passed as the parameter value, you can check for the presence of nil, as suggested by Stuart P. Bentley, as long as the default value is not Boolean false:
function test2(param)
local default = 10
param = (param == nil and default) or param
return param
end
--[[
test2(): [10]
test2(nil): [10]
test2(true): [true]
test2(false): [false]
]]
The above approach breaks when the default value is Boolean false:
function test3(param)
local default = false
param = (param == nil and default) or param
return param
end
--[[
test3(): [nil]
test3(nil): [nil]
test3(true): [true]
test3(false): [false]
]]
Interestingly, reversing the order of the conditional checks does allow Boolean false to be the default value, and is nominally more performant:
function test4(param)
local default = false
param = param or (param == nil and default)
return param
end
--[[
test4(): [false]
test4(nil): [false]
test4(true): [true]
test4(false): [false]
]]
This approach works for reasons that seem counter-intuitive until further examination, upon which they are discovered to be kind of clever.
If you want default parameters for functions that do allow nil values to be passed, you'll need to do something even uglier, like using variadic parameters:
function test5(...)
local argN = select('#', ...)
local default = false
local param = default
if argN > 0 then
local args = {...}
param = args[1]
end
return param
end
--[[
test5(): [false]
test5(nil): [nil]
test5(true): [true]
test5(false): [false]
]]
Of course, variadic parameters completely thwart auto-completion and linting of function parameters in functions that use them.
Short answer is that it's simplest and best way . in lua , variables by default equal with nil . this means if we don't pass argument to lua functions ,the argument is exits but is nil and lua programmers uses of this lua attribute for set the default value .
also it's not a way for set default value but you can use following function
this function create a error is you don't pass values to arguments
function myFn(arg1 , arg2)
err = arg1 and arg2
if not err then error("argument") end
-- or
if not arg1 and arg2 then error("msg") end
but it's not a good way and better is don't use of this function
and in diagrams shows optional argument in [,arg]
function args(a1 [,a2])
-- some
end
function args ( a1 [,a2[,a3]])
-- some
end
As always, "Lua gives you the power, you build the mechanisms". The first distinction to make here is that between named parameters and the commonly used parameter list.
The parameter list
Assuming all your args are given in the parameter list as follows, they will all be initialized. At this point, you can't distinguish between "wasn't passed" and "was passed as nil" - both will simply be nil. Your options for setting defaults are:
Using the or operator if you expect a truthy value (not nil or false). Defaulting to something even if false is given might be a feature in this case.
Using an explicit nil check param == nil, used either as if param == nil then param = default end or the typical Lua ternary construct param == nil and default or param.
If you find yourself frequently repeating the patterns from point (2), you might want to declare a function:
function default(value, default_value)
if value == nil then return default_value end
return value
end
(whether to use global or local scope for this function is another issue I won't get into here).
I've included all three ways the following example:
function f(x, y, z, w)
x = x or 1
y = y == nil and 2 or y
if z == nil then z == 3 end
w = default(w, 4
print(x, y, z, w)
end
f()
f(1)
f(1, 2)
f(1, 2, 3)
f(1, 2, 3, 4)
note that this also allows omitting arguments inbetween; trailing nil arguments will also be treated as absent:
f(nil)
f(nil, 2, 3)
f(nil, 2, nil, 4)
f(1, 2, 3, nil)
Varargs
A lesser known feature of Lua is the ability to actually determine how many arguments were passed, including the ability to distinguish between explicitly passed nil arguments and "no argument" through the select function. Let's rewrite our function using this:
function f(...)
local n_args = select("#", ...) -- number of arguments passed
local x, y, z, w = ...
if n_args < 4 then w = 4 end
if n_args < 3 then z = 3 end
if n_args < 2 then y = 2 end
if n_args < 1 then x = 1 end
print(x, y, z, w)
end
f() -- prints "1 2 3 4"
f(nil) -- prints "nil 2 3 4"
f(1, nil) -- prints "1 nil 3 4"
f(1, nil, 3) -- prints "1 nil 3 4"
f(nil, nil, nil, nil) -- prints 4x nil
Caveat: (1) the argument list got dragged into the function, hurting readability (2) this is rather cumbersome to write manually, and should probably be abstracted away, perhaps time using a wrapper function wrap_defaults({1, 2, 3, 4}, f) that supplies the defaults as appropriate. Implementation of this is left up to the reader as an exercise (hint: the straightforward way would first collect the args into a garbage table, then unpack that after setting the defaults).
Table calls
Lua provides syntactic sugar for calling functions with a single table as the only argument: f{...} is equivalent to f({...}). Furthermore, {f(...)} can be used to capture a vararg returned by f (caveat: if f returns nils, the table will have holes in it's list part).
Tables also allow implementing named "arguments" as table fields: Tables allow mixing a list and a hash part, making f{1, named_arg = 2} perfectly valid Lua.
In terms of limitations, the advantage of table call is that it only leaves a single argument - the table - on the stack rather than multiple arguments. For recursive functions, this allows hitting the stack overflow later. Since PUC Lua drastically increased the stack limit to ~1M this isn't much of an issue anymore; LuaJIT still has a stack limit of ~65k however, and PUC Lua 5.1 is even lower at around 15k.
In terms of performance & memory consumption, the table call is obviously worse: It requires Lua to build a garbage table, which will then waste memory until the GC gets rid of it. Garbage parameter tables should therefore probably not be used in hotspots where plenty of calls happen. Indexing a hashmap is also obviously slower than getting values straight off the stack.
That said, let's examine the ways to implement defaults for tables:
Unpacking / Destructuring
unpack (table.unpack in later versions (5.2+)) can be used to convert a table into a vararg, which can be treated like a parameter list; note however that in Lua the list part can't have trailing nil values, not allowing you to distinguish "no value" and nil. Unpacking / destructuring to locals also helps performance since it gets rid of repeated table indexing.
function f(params)
local x, y, z, w = unpack(params)
-- use same code as if x, y, z, w were regular params
end
f{1, 2, nil}
if you use named fields, you'll have to explicitly destructure those:
function f(params)
local x, y, z, w = params.x, params.y, params.z, params.w
-- use same code as if x, y, z, w were regular params
end
f{x = 1, w = 4}
mix & match is possible:
function f(params)
local x, y, z = unpack(params)
local w = params.w
-- use same code as if x, y, z, w were regular params
end
f{1, 2, w = 4}
Metatables
The __index metatable field can be used to set a table which is indexed with name if params.name is nil, providing defaults for nil values. One major drawback of setting a metatable on a passed table is that the passed table's metatable will be lost, perhaps leading to unexpected behavior on the caller's end. You could use getmetatable and setmetatable to restore the metatable after you're done operating with the params, but that would be rather dirty, hence I would recommend against it.
Bad
function f(params)
setmetatable(params, {__index = {x = 1, y = 2, z = 3, w = 4}})
-- use params.[xyzw], possibly unpacking / destructuring
end
f{x = 1}
in addition to the presumably garbage params table, this will create (1) a garbage metatable and (2) a garbage default table every time the function is called. This is pretty bad. Since the metatable is constant, simply drag it out of the function, making it an upvalue:
Okay
local defaults_metatable = {__index = {x = 1, y = 2, z = 3, w = 4}}
function f(params)
setmetatable(params, defaults_metatable)
-- use params.[xyzw], possibly unpacking / destructuring
end
Avoiding metatables
If you want a default table without the hackyness of metatables, consider once again writing yourself a helper function to complete a table with default values:
local function complete(params, defaults)
for param, default in pairs(defaults) do
if params[param] == nil then
params[param] = default
end
end
end
this will change the params table, properly setting the defaults; use as params = complete(params, defaults). Again, remember to drag the defaults table out of the function.