Discussion:
Passing Parameters by Reference
Virgil Smith
2004-06-11 21:57:20 UTC
Permalink
Lua forces copy semantics for simple value types (nil, boolean, number,
string, light userdata) and reference semantics for complex types (function,
table, userdata).

I have a colleague using Lua in a project that has a strong need for passing
parameters by reference (problem set is the multivariate optimization of a
complex system). However, since Lua is incapable of this construct he is
forced to pass parameters by name (or to add a level of indirection to every
variable that he might ever wish to pass to a function by reference).

Basically passing a value by reference (other than a
table/function/userdata) requires one of the following (AFAIK).


function SomeFunction(Tbl, Key) do
Tbl[Key] = Tbl[Key] + 2
end

...or...

function SomeFunction(ParamA) do
Param[1] = Param[1] + 2 -- Note that the ONLY reason for
end -- ParamA to be a table is so that
-- the 1 and only value it contains
-- can be changed



1. Does anyone know of any pure Lua way of accomplishing "passing by
reference" that is more elegant and maintainable than the above methods?



2. Does anyone have a feel for how hard it would be to modify the Lua
sources to overcome this limitation of Lua?



3. Anyone have a good suggestion for a good Lua replacement for those that
mostly like Lua, but come across "poor fits" in certain projects?




=====================================================
Virgil Smith EMAIL: ***@Nomadics.com
Nomadics Inc HTTP: www.Nomadics.com
1024 S. Innovation Way PHONE: 405-372-9535
Stillwater, OK 74074, USA FAX: 405-372-9537
=====================================================
Jamie Webb
2004-06-12 01:29:31 UTC
Permalink
Post by Virgil Smith
Lua forces copy semantics for simple value types (nil, boolean, number,
string, light userdata) and reference semantics for complex types
(function, table, userdata).
I have a colleague using Lua in a project that has a strong need for
passing parameters by reference (problem set is the multivariate
optimization of a complex system).
Since Lua allows multiple return values, you can always just use the idiom:

a = f(a)

regardless of how many parameters you need to change. There is never an actual
need for passing parameters by reference. It just might be notationally
convenient in some cases, but arguably doing so would just make your code
harder read.

-- Jamie Webb
Virgil Smith
2004-06-14 15:39:15 UTC
Permalink
Post by Jamie Webb
Since Lua allows multiple return values,
a = f(a)
The case at hand is a multivariate optimization problem (and several similar
issues).
a = f(a)
is not a valid solution, as it does not allow the creation of a function
that performs the optimization in a general fashion.

Consider the following

function Optimize(f, v, min, max) do
-- f is the function to be optimized (minimized)
-- v is the variable quantity, i.e. the position
-- along the "search" axis
-- min is a lower boundary for v
-- max is an upper boundary for v
......
end

Note that f is not passed any arguments by Optimize as Optimize should have
no dependence on the number of parameters upon which f operates nor upon
which parameter is currently being operated upon. So f will operate either
on globals or on member variables of an object.

Anyone who thinks that a function such as Optimize should be re-implemented
for every object(or class) of which a target function "f" is a member needs
to look up the term "generic programming" and buy a book on it.


As I stated before my colleague is currently "passing by name" when passing
by reference is called for. This means that all functions (such as
"Optimize" above) must accept a table/userdata and the NAME of the variable
quantity. This allows an operation such as....

v = v + delta

can be written as...

tbl[v] = tbl[v] + delta


Frankly he's just tired of having to write extra code because Lua is missing
a key feature of almost all popular programming languages (i.e. the ability
to pass by reference).
Mark Hamburg
2004-06-14 16:19:09 UTC
Permalink
Post by Virgil Smith
Frankly he's just tired of having to write extra code because Lua is missing
a key feature of almost all popular programming languages (i.e. the ability
to pass by reference).
Not implemented in Java. Not in Lisp. Not in Smalltalk...

But really, the answer is multiple-value return. If you need to change
multiple values, then you pass them in and receive them back. That's how you
get the same thing as Pascal VAR parameters and C++ references. It gets a
little ugly if one is modifying an entry within a table rather than a
variable, but it's quite doable. For example, it's a little crude and
possibly more error prone to have to write:

table[ index ] = adjust( table[ index ], other_param )

Rather than:

adjust( table[ index ], other_param )

But you also get code that's easier to understand with respect to exceptions
and side-effects.

Perhaps with a more detailed example in some other language, the list can
better analyze what your friend needs to do.

Mark

P.S. As a language that addresses these things via syntactic sugar, perhaps
Lua needs something like:

let x be table[ index ] in
x = adjust( x, other_param )
end

This introduces an alternative name for an assignment path. I would argue
that it should probably also capture the values of table and index so that x
continues to refer to the same thing. In other words, it translates to:

do
local _temp1, _temp2 = table, index
_temp1[ _temp2 ] = adjust( _temp1[ _temp2 ], other_param )
end
Jamie Webb
2004-06-14 16:45:41 UTC
Permalink
Post by Virgil Smith
Post by Jamie Webb
Since Lua allows multiple return values,
a = f(a)
The case at hand is a multivariate optimization problem (and several
similar issues).
a = f(a)
is not a valid solution, as it does not allow the creation of a function
that performs the optimization in a general fashion.
Playing around with globals in that manner is terrible style. Suppose we want
to minimise a binary function f w.r.t. the first parameter:

p1 = Optimize(function(x) return f(x, p2) end, p1, min, max)

Now Optimize is always passed a function in a single variable, regardless of
the actual implementation of the function to minimise. This is actually
generic, as opposed to assuming that the function operates on table members.
If you really want to use table members:

p1 = Optimize(function(x) p1 = x return f() end, p1, min, max)
Post by Virgil Smith
Frankly he's just tired of having to write extra code because Lua is
missing a key feature of almost all popular programming languages (i.e. the
ability to pass by reference).
Most popular languages have goto as well. Doesn't mean it's a good thing.
Value semantics are widely recognised as resulting in cleaner code.

-- Jamie Webb
Virgil Smith
2004-06-14 17:43:25 UTC
Permalink
Suppose we want to minimise a binary function f
...
p1 = Optimize(function(x) return f(x, p2) end, p1, min, max)
...
p1 = Optimize(function(x) p1 = x return f() end, p1, min, max)
Yeah!! A meaningful suggestion. Thanks!
I had originally considered this, but tossed the idea because its a lot of
coding and runtime effort to hand code a translation function and call it on
every iteration.

However, this usage of closures is more generic than my previously mentioned
"passing by name" method since it can actually cope with "private data
members" that might not otherwise be reachable by Optimize (or similar
functions).

It would be very nice if this construct could be reduced to a macro or a
reusable construction function, but I don't see how it can in Lua as calling
a function to create the workaround function would suffer from the original
pass by reference problem.

------

Thank you all for your reading time and thoughts on what is a trivial detail
(just one that has been annoyingly repetitive for my coworker).
Doug Rogers
2004-06-14 18:42:42 UTC
Permalink
Post by Virgil Smith
function Optimize(f, v, min, max) do
-- f is the function to be optimized (minimized)
-- v is the variable quantity, i.e. the position
-- along the "search" axis
-- min is a lower boundary for v
-- max is an upper boundary for v
......
end
Hi Virgil. What do you use for the error function?

I did a minimization project in Ada (83!) a few years back using
Levenberg-Marquardt minimization. While Ada does indeed provide for passing
values by reference, I have to agree with the peanut gallery and say that I
don't think that's the fundamental problem here.

With Ada generic packages it is possible to wrap all the state information
into a single package and have the function being minimized gain local access
to all the state information. I had default generic partial derivative
function, but for one of my models I required a special-purpose one. It was
very codey; it would be very simple and concise in Lua.

This could be done by wrapping the entire problem into a single table that
holds not just the function but also its nonvariant state information. The
variant (v) would then be perturbed according to whatever method you are
using - L-M or some form of steepest descent, whatever.

The problem would be speed. In my L-M minimization, the minimizer itself did
not consume too much CPU time. At most it performed a few 8x8 matrix
inversions (I linked in some downloaded Ada code to do Singular Value
Decomposition). The function(s) being minimized consumed the vast majority of
the CPU time.

In Lua, though, each reference to state information comes at the cost of a
table lookup. This cost is small compared to other scripting languages, but
it would be huge in number-crunching algorithms in Lua. You simply cannot
avoid that without butchering the code so much that you might as well call it
a different language.

It may be that -- heaven forbid! -- Lua is not the appropriate language for
your friend's problem. I would sooner cut off my own fingers than type the
stuff that your friend is typing! :)

But being a fan of Lua, I guess I'd recommend finding ways to hide the
time-critical calculations in C code behind proxy tables/userdata. It's not
as much work as you might think. It took me a few days to get up to speed,
but now I feel very comfortable writing mixed C/Lua applications. I was
surprised how easily it all came together.

For example, you may be able to write the minimizer completely in Lua. Then
use a wrapper to prep the data to go into C, FORTRAN, whatever, to do the
real work of running the function being minimized. This would allow your
friend to sit at an interpreter and inspect the results, then make trial runs
repeatedly. But any changes to the function would have to happen in C.

Or you may find it more useful to provide C functions for vector and matrix
calculations. Then you could write the forward function entirely in Lua. This
would require more legwork up front, but would be time well spent because
such routines are very general purpose and can serve you for lots of
projects. I've written implementations of Vector and Matrix, below, but right
now it's all in Lua, so not-so-fast.

-- Vector module provides length(), dot(), outer(), '+', '-', any of which
-- could be optimized in C. The elements of the vector could be completely
-- hidden behind (light)userdata, requiring a single table lookup per
-- operation on the vector (rather than the value).
-- Vector.norm2 = function(v) return math.sqrt(v:dot(v)) end
-- Matrix module provides similar.
-- Matrix.numerical_partial(f, delta) return function... end

function minimize(problem, vstart)
-- 'problem' is table containing:
-- :f(v) function taking Vector object, returning Vector
-- :dfdv(v) partial derivative function taking Vector returning Matrix
-- .vmin [optional] lower bounds for elements of v, Vector
-- .vmax [optional] upper bounds for elements of v, Vector
-- .fmin [optional] lower bounds for return values of f, Vector
-- .fmax [optional] upper bounds for return values of f, Vector
-- Other elements of 'problem' can be set as desired.
-- Parameter
-- vstart initial value for v, a Vector object
-- Returns a table with the best fit data, norm, and function results.
local v = problem.vstart:copy()
local fstart = problem:f(v)
local best = {}
best.norm = fstart:norm2()
best.v = vstart
best.f = fstart

while (some search-technique-specific criteria are still met)
pick new value for v
recalculate error function
end

return best
end -- minimize()

fxy = function(x, y) return (x * x * y) - (2 * x * y) - (y * y) end
fv = function(f, v) return f(v[1], v[2]) end
p = {
f=fv,
dfdv=Matrix.numerical_partial(fv, 0.001),
vmin={ -1.0, -1.0 },
vmax={ 20.0, 20.0 },
}

best = minimize(p, { 8.2, 2.5 })
print(best.v:tostring())

I *like* the idea of passing a table to the minimizer/optimizer. I think it
provides a great way to distinguish between what is and what is not part of
the problem space. The method (granted, of smaller scope) of using multiple
return values doesn't always lead to more readable code.

But Lua is flexible enough to find a way that is pleasing to your own eye.

Doug
--
--__-__-____------_--_-_-_-___-___-____-_--_-___--____
Doug Rogers - ICI - V:703.893.2007x220 www.innocon.com
-_-_--_------____-_-_-___-_--___-_-___-_-_---_--_-__-_
Jose Marin
2004-06-15 11:40:01 UTC
Permalink
Hello.

I've serached in the archives but haven't found the
answer.
It's possible to calc the memory used to a table and
it's contents, somehow like the 'sizeof' operator?

______________________________________________________________________

Participe da pesquisa global sobre o Yahoo! Mail:
http://br.surveys.yahoo.com/global_mail_survey_br

Loading...