Loop over a function and store as list - lua

I have the following function that clicks a checkbox with splash:
local get_dimensions = splash:jsfunc([[
function () {
for i=1, 5 do
var rect = document.querySelector(string.format('checkbox[number="%d"]'), i)
.getClientRects()[0];
return {[rect.left]: rect.top};
end
}
]])
However, I cannot store a list from the loop into a single variable like rect, so how do I store a list into a variable, and when I return it, it should return a list of values?
Something similar to python, ie.:
def stuff():
rect = []
for i in range(5):
rect.append([...])
return rect

Let's take at your code.
You call splash:jsfunc. This function converts JavaScript into a Lua callable. It takes a single argument. A string that defines a JavaScript function.
yourString = [[
function () {
for i=1, 5 do
var rect = document.querySelector(string.format('checkbox[number="%d"]'), i).getClientRects()[0];
return {[rect.left]: rect.top};
end
}
]]
This looks like some weird mix of Lua and JavaScript.
This is how a JavaScript for loop looks like
for (let i = 0; i < 10; i++) {
// some code
}
This is how a Lua numeric for loop looks like:
Also what's the point of using return in a loop without any condition? This will be executed after the first cycle with i = 1. That's probably not what you want.
So fix the loop so it is JavaScript. Create a Object or Array and return that after the loop is complete.

Related

calling a function with a table constructor?

i was learning on how to create and use a class on a youtube video and i came across this code
cc = {calo = 0, goal = 1500}
function cc:new(t)
t = t or {}
setmetatable(t,self)
self.__index = self
return t
end
a = cc:new{goal = 100}
i dont understand this part a = cc:new{goal = 100} this is the first time where i see a function being called with anything other then (). i have 2 guesses on what this does maybe it replaces the parameter of cc:new function with {goal = 100} or maybe the function is called and t table is assigned to the variable then assigning the table with {goal = 100}? pls correct me if im wrong
first, {goal = 100} is just an argument.
second, cc:new{goal = 100} is equal to cc:new({goal = 100})
this is a syntactic sugar, you can call a function without brackets if theres only one argument and its type is string or table literal
example:
function foo(x)
print(x)
return foo
end
foo "Hello" "World"
this will output "Hello" and "World"
if you want to call a function without brackets and using multiple arguments, the function you gonna call must return another function for the next argument.
the another function is not always the original one
it may be recursive

How to modify a functions internal variables at runtime and pass it to another function?

Functions in Dart are first-class objects, allowing you to pass them to other objects or functions.
void main() {
var shout = (msg) => ' ${msg.toUpperCase()} ';
print(shout("yo"));
}
This made me wonder if there was a way to modify a function a run time, just like an object, prior to passing it to something else. For example:
Function add(int input) {
return add + 2;
}
If I wanted to make the function a generic addition function, then I would do:
Function add(int input, int increment) {
return add + increment;
}
But then the problem would be that the object I am passing the function to would need to specify the increment. I would like to pass the add function to another object, with the increment specified at run time, and declared within the function body so that the increment cannot be changed by the recipient of the function object.
The answer seems to be to use a lexical closure.
From here: https://dart.dev/guides/language/language-tour#built-in-types
A closure is a function object that has access to variables in its
lexical scope, even when the function is used outside of its original
scope.
Functions can close over variables defined in surrounding scopes. In
the following example, makeAdder() captures the variable addBy.
Wherever the returned function goes, it remembers addBy.
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(int addBy) {
return (int i) => addBy + i;
}
void main() {
// Create a function that adds 2.
var add2 = makeAdder(2);
// Create a function that adds 4.
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
In the above cases, we pass 2 or 4 into the makeAdder function. The makeAdder function uses the parameter to create and return a function object that can be passed to other objects.
You most likely don't need to modify a closure, just the ability to create customized closures.
The latter is simple:
int Function(int) makeAdder(int increment) => (int value) => value + increment;
...
foo(makeAdder(1)); // Adds 1.
foo(makeAdder(4)); // Adds 2.
You can't change which variables a closure is referencing, but you can change their values ... if you an access the variable. For local variables, that's actually hard.
Mutating state which makes an existing closure change behavior can sometimes be appropriate, but those functions should be very precise about how they change and where they are being used. For a function like add which is used for its behavior, changing the behavior is rarely a good idea. It's better to replace the closure in the specific places that need to change behavior, and not risk changing the behavior in other places which happen to depend on the same closure. Otherwise it becomes very important to control where the closure actually flows.
If you still want to change the behavior of an existing global, you need to change a variable that it depends on.
Globals are easy:
int increment = 1;
int globalAdder(int value) => value + increment;
...
foo(globalAdd); // Adds 1.
increment = 2;
foo(globalAdd); // Adds 2.
I really can't recommend mutating global variables. It scales rather badly. You have no control over anything.
Another option is to use an instance variable to hold the modifiable value.
class MakeAdder {
int increment = 1;
int instanceAdd(int value) => value + increment;
}
...
var makeAdder = MakeAdder();
var adder = makeAdder.instanceAdd;
...
foo(adder); // Adds 1.
makeAdder.increment = 2;
foo(adder); // Adds 2.
That gives you much more control over who can access the increment variable. You can create multiple independent mutaable adders without them stepping on each other's toes.
To modify a local variable, you need someone to give you access to it, from inside the function where the variable is visible.
int Function(int) makeAdder(void Function(void Function(int)) setIncrementCallback) {
var increment = 1;
setIncrementCallback((v) {
increment = v;
});
return (value) => value + increment;
}
...
void Function(int) setIncrement;
int Function(int) localAdd = makeAdder((inc) { setIncrement = inc; });
...
foo(localAdd); // Adds 1.
setIncrement(2);
foo(localAdd); // Adds 2.
This is one way of passing back a way to modify the local increment variable.
It's almost always far too complicated an approach for what it gives you, I'd go with the instance variable instead.
Often, the instance variable will actually represent something in your model, some state which can meaningfully change, and then it becomes predictable and understandable when and how the state of the entire model changes, including the functions referring to that model.
Using partial function application
You can use a partial function application to bind arguments to functions.
If you have something like:
int add(int input, int increment) => input + increment;
and want to pass it to another function that expects to supply fewer arguments:
int foo(int Function(int input) applyIncrement) => applyIncrement(10);
then you could do:
foo((input) => add(input, 2); // `increment` is fixed to 2
foo((input) => add(input, 4); // `increment` is fixed to 4
Using callable objects
Another approach would be to make a callable object:
class Adder {
int increment = 0;
int call(int input) => input + increment;
}
which could be used with the same foo function above:
var adder = Adder()..increment = 2;
print(foo(adder)); // Prints: 12
adder.increment = 4;
print(foo(adder)); // Prints: 14

store lua functions in queue to be executed later

I am using functions from a lua-based library and I would like to store those different functions with varying parameter counts in a queue to be executed later on when I need to. Here is a similar question and solution for javascript.
Below is an example that works, but conditionals are done manually for each parameter count. Is there a cleaner way to do this in lua? Keep in mind I cannot modify the library's function implementation since I dont have access to their code.
Action = {}
function Action:new(func, ...)
newObj = {
func = func or nil,
args = {...} or {}
}
self.__index = self
return setmetatable(newObj, self)
end
function Action:execute()
if #self.args == 0 then
self.func()
elseif #self.args == 1 then
self.func(self.args[1])
elseif #self.args == 2 then
self.func(self.args[1], self.args[2])
-- and so on
end
theQueue = {
Action:new(aFunc, param),
Action:new(aDifferentFunc, param1, param2)
}
for _, action in ipairs(theQueue) do
action:execute()
end
You can simply use table.unpack to convert your args table back to a parameter list.
self.func(table.unpack(self.args))
newObj should be local btw.
This looks like a perfect case for anonymous functions:
local theQueue = {
function() aFunc(param) end,
function() aDifferentFunc(param1, param2) end,
}
for _, action in ipairs(theQueue) do
action()
end

is there aliasing in lua similar to ruby

Can you alias a function (not in a class) in LUA in a similar way to Ruby? In ruby you would do something like this:
alias new_name_for_method method()
def method()
new_name_for_method() # Call original method then do custom code
i = 12 # New code
end
I'm asking because I'm developing for a program that uses LUA scripting and I need to override a function that is declared in a default file.
In Lua, functions are values, treated like any other value (number, string, table, etc.) You can refer to a function value via as many variables as you like.
In your case:
local oldmethod = method
function method(...)
oldmethod(...)
i = 12 -- new code
end
keep in mind that
function method() end
is shorthand for:
method = function() end
function() end just creates a function value, which we assign to the variable method. We could turn around and store that same value in a dozen other variables, or assign a string or number to the method variable. In Lua, variables do not have type, only values do.
More illustration:
print("Hello, World")
donut = print
donut("Hello, World")
t = { foo = { bar = donut } }
t.foo.bar("Hello, World")
assert(t.foo.bar == print) -- same value
FYI, when wrapping a function, if you want its old behavior to be unaffected for now and forever, even if its signature changes, you need to be forward all arguments and return values.
For a pre-hook (new code invoked before the old), this is trivial:
local oldmethod = method
function method(...)
i = 12 -- new code
return oldmethod(...)
end
A post-hook (new code invoked after the old) is a bit more expensive; Lua supports multiple return values and we have to store them all, which requires creating a table:
local oldmethod = method
function method(...)
local return_values = { oldmethod(...) }
i = 12 -- new code
return unpack(return_values)
end
In lua, you can simply override a variable by creating a new function or variable with the same name.
function name_to_override()
print('hi')
end
If you still want to be able to call the old function:
local old_function = name_to_override
function name_to_override()
old_function()
print('hi')
end

using concatenation in getUrl during loop?

I would like to write Actionscript loop that involves "getURL". However, from what I can see getURL does not allow concatenation of variable names?
I have variables textholder0, textholder1, textholder2 that have movieclip names as values and link0, link1, link2 that have website addresses as values.
I can use this["textholder" + 0].onRelease but getURL("link"+ 0) gives "undefined"
textholder0.onRelease = function()
{
getURL(link0);
}
textholder1.onRelease = function()
{
getURL(link1);
}
textholder2.onRelease = function()
{
getURL(link2);
}
Any way to do this so I can create a loop for the above?
Here is a test. Unfortunately, it still gives me "undefined/" for the URL. To keep it simple I created three movie clips, with instances textholder0, textholder1, textholder2. Put a loop on the main timeline.
var links:Array = ["http://www.google.ca", "http://www.google.com", "http://www.google.ru"];
for(var i:Number=0; i<links.length; i++){
this["textholder" + i].linkURL = links[i];
this["textholder" + i].onRelease = function() {
getURL(linkURL);
}
}
Here is output from debugger window
Variable _level0.links = [object #1, class 'Array'] [
0:"http://www.google.ca",
1:"http://www.google.com",
2:"http://www.google.ru" ]
Variable _level0.i = 3
Movie Clip: Target="_level0.textholder0"
Variable _level0.textholder0.linkURL = "http://www.google.ca"
Variable _level0.textholder0.onRelease = [function 'onRelease']
Movie Clip: Target="_level0.textholder1"
Variable _level0.textholder1.linkURL = "http://www.google.com"
Variable _level0.textholder1.onRelease = [function 'onRelease']
Movie Clip: Target="_level0.textholder2"
Variable _level0.textholder2.linkURL = "http://www.google.ru"
Variable _level0.textholder2.onRelease = [function 'onRelease']
I am starting to think that you can not use onRelease within a loop at all.
getURL("link"+ 0) will try to go to a URL "link0", since "link"+ 0 will be concatenated to the string "link0", and not get the value of link0. But you can try doing this:
getURL(this["link" + 0]);
The difference, and the mechanism of the bracket notation, is that you can reference a property of an object in two ways - using dot notation, like this.link0, or the bracket notation, this["link0"]. But it has to be expressed as an object property, just saying "link" + 0 anywhere, like in getURL("link"+ 0) won't give a reference to link0.
ok, so I think the problem with the loop here is that it was incrementing "i" variable before any of the buttons were clicked.
http://www.senocular.com/flash/tutorials/faq/#loopfunctions
Senocular.com says "you need to define a new, unique variable to represent that value at the time of function creation and have the function reference that value"
So the loop goes as following
var links:Array = ["http://www.google.ca", "http://www.google.com", "http://www.google.ru"];
var curr_button;
for(var i=0; i<=links.length; i++){
curr_button = this["textholder"+i];
//note creation of an extra variable "num" below to store the temp number
curr_button.num = i;
curr_button.onRelease = function() {
getURL(links[this.num]);
}
}

Resources