[kntouskos@sini2] > global test do={ local a ({});set ($a->"$1") 1;put $a;set a;put $a}
[kntouskos@sini2] > $test a
a=1
[kntouskos@sini2] > $test b
a=1;b=1
[kntouskos@sini2] > $test c
a=1;b=1;c=1
Have a look at the variable test after each run and you see that it will be updated. So you are actually editing the test variable/script for each run.
You are adding new kv pair to the variable value within the script.
I’m expecting that when using a local variable that the value will be empty in the next run or when I unset it.
I’m not at any point modifying “test” function.
Running on the prompt, behavior is as expected.
[kntouskos@sini2] > local a ({});set ($a->"a") 1;put $a;set ($a->"b") 1;put $a;set ($a->"hello") 1;put $a;set a;put $a;local a ({});set ($a->"whatsallthisthen") 1;put $a
a=1
a=1;b=1
a=1;b=1;hello=1
whatsallthisthen=1
[kntouskos@sini2] > local a ({});set ($a->"a") 1;put $a;set ($a->"b") 1;put $a;set ($a->"hello") 1;put $a;set a;put $a;local a ({});set ($a->"whatsallthisthen") 1;put $a
a=1
a=1;b=1
a=1;b=1;hello=1
whatsallthisthen=1
Once it get’s wrapped into a function it breaks.
[kntouskos@sini2] > global test do={local a ({});set ($a->"a") 1;put $a;set ($a->"b") 1;put $a;set ($a->"hello") 1;put $a;set a;put $a;local a ({});set ($a->"whatsallthisthen") 1;put $a }
[kntouskos@sini2] > $test
a=1
a=1;b=1
a=1;b=1;hello=1
whatsallthisthen=1
[kntouskos@sini2] > $test #second run
a=1;b=1;hello=1
a=1;b=1;hello=1
a=1;b=1;hello=1
whatsallthisthen=1
Is this normal behavior?
What I’m actually trying to do is somethng like this.
I have a function that returns a dictionary based on preset values, depending on what’s requested. Not all entries have the same keys present.
For example, a directory listing with name, phones, addresses and other info.
Bob has a name, address and mobile number, Tom doesn’t have an address but has a birthday. Running this returns bad results after the second run.
function :
global main do={
local dict ({})
#populate dict with values based on given name
if ($1="bob") do={
set ($dict->"name") "bob"
set ($dict->"address") "maple st."
set ($dict->"mobile") "5551234"
}
if ($1="tom") do={
set ($dict->"name") "tom"
set ($dict->"mobile") "main ave."
set ($dict->"birthday") "01011970"
}
return $dict
}
execution :
[kntouskos@sini2] > #tom has a bday
[kntouskos@sini2] > foreach key,value in=[$main tom] do={ put ("$key is $value") }
address is maple st.
birthday is 01011970
mobile is main ave.
name is tom
[kntouskos@sini2] > #bob doesnt have a bday
[kntouskos@sini2] > foreach key,value in=[$main bob] do={ put ("$key is $value") }
address is maple st.
birthday is 01011970
mobile is 5551234
name is bob
edit:
I’ve already fixed my script by using two arrays instead of a dictionary and looping over the “key” array in the foreach loop and using the index to grab the value from the “value” array, but using a dictionary looks much more elegant. Plus this left me dumbfound, so I really want to know whats up and why it’s happening.
I’m using a local variable to hold a dictionary. I’m expecting that local variable to be empty next time around, not hold values from previous runs.
I’m not interested in “doing” something. I’ve already done it. What I want is to understand why a local variable retains it’s values, if this is intended (and why) or if this is a bug.
If you really really need a script, consider this from my previous post.
I have a global function that acts as a directory.
Within it I have names, emails, phones and maybe other info. Not all entries need to have the same keys.
Each time I request something I expect to get the proper values.
It’s not about if the script is optimal, makes sense, can be written in a different way or whatever, it’s about not producing the expected results and why.
script:
global main do={
local dict ({})
#populate dict with values based on given name
if ($1="bob") do={
set ($dict->"name") "bob"
set ($dict->"address") "maple st."
set ($dict->"mobile") "5551234"
}
if ($1="tom") do={
set ($dict->"name") "tom"
set ($dict->"mobile") "666"
set ($dict->"birthday") "01011970"
}
return $dict
}
results:
[kntouskos@sini2] > foreach key,value in=[$main bob] do={ put ("$key is $value") } #requesting dictionary for bob, as this is the first run results are correct.
address is maple st.
mobile is 5551234
name is bob
[kntouskos@sini2] > foreach key,value in=[$main tom] do={ put ("$key is $value") } #requesting dictionary for tom, results are incorrect
address is maple st. #tom has no address key, we're getting bobs
birthday is 01011970
mobile is 666
name is tom
[kntouskos@sini2] > foreach key,value in=[$main bob] do={ put ("$key is $value") } #3rd run, requesting bob again
address is maple st.
birthday is 01011970 #bob has no birdthday, we're getting toms
mobile is 5551234
name is bob
I do understand from your previous post that the function is getting re-written by each iteration, the question is why and should it.
Its not irrelevant. To solve problem its good to have as much information as possible. There even may be an other much better solution that OP has thought of
You can narrow the scope for the local variable to help mitigate the issue:
global main do={
#populate dict with values based on given name
if ($1="bob") do={
local dict ({})
set ($dict->"name") "bob"
set ($dict->"address") "maple st."
set ($dict->"mobile") "5551234"
return $dict
}
if ($1="tom") do={
local dict ({})
set ($dict->"name") "tom"
set ($dict->"mobile") "666"
set ($dict->"birthday") "01011970"
return $dict
}
}
foreach key,value in=[$main bob] do={ put ("$key is $value") }
address is maple st.
mobile is 5551234
name is bob
foreach key,value in=[$main tom] do={ put ("$key is $value") }
birthday is 01011970
mobile is 666
name is tom
It IS irrelevant. The problem is to do with scripting and variables not any other use they may be put to. Answer the question as posed or don’t bother answering at all.
Mikrotik RouterOS script variables are “static”. The value is stored with the function, and as you have seen, this is implemented by rewriting the function whenever the value of a local variable is changed. Assigning an array is done by reference, not by value, which means the variable doesn’t get its own copy of the array but uses the same storage as the original array. This can lead to very counter-intuitive situations, for example:
The interpreter has put the assigned value into the $empty variable, because $a is just another reference to the original array referenced by $empty. The function is rewritten to remember the value, so the next time the function is called, $empty starts with the value it was assigned during the previous execution.
The way around this is to create a new array and assign it to the variable. But you can’t use a literal or the interpreter is going to rewrite it to remember the value. So use a function which returns an array:
Voila, without the array literal, the interpreter has no place to remember the assigned value, and the next invocation of the function starts with an empty array.
Thanks for the recommendation 2frogs, though I believe Emil66’s response takes care of it in a better way.
Great post Emil66. Thanks a lot for the explanation, though tbh I’m not sure I understand exactly why it’s happening. I do understand however that ({}) is not the proper way to initialize an array but rather use [toarray “”].
edit: the question now is, is this a feature, a known limitation or a bug and should it be reported to MT support?
I do understand however that ({}) is not the proper way to initialize an array but rather use [toarray “”].
It is not wrong to initialize with a literal. It just means the interpreter will remember the variable value between executions of the function. Initializing the variable with a fresh empty array from the return value of [:toarray “”] is just a way to use static variables like they aren’t static. And btw., if you use [:toarray ({})], no conversion is necessary and the return value is just a reference to that literal. Then the interpreter once again knows where to remember the array value.