Will local variables be independent in recursively called Lotusscript function? - recursion

If I have a recursive function (Lotusscript) and inside the function is a locally declared variable, will each iteration of the call keep the variables independently stored in memory?
For example, I have a counter on a main function that loops 10 times....it calls the recursive function, and in certain circumstances, that will call itself....but passing a different object each time as an argument. The recursive function has its own counter variable declared locally.
Suppose this main function if called, and it calls the recursive function one time, and begins a loop, counting up to ten itself. On the 5th loop, it calls itself. This recursion will end due to setting a global boolean, and now I have three known local variables, the main function, and two from the recursive function.
Will each of these counters be kept track of independently, so that depending on which function I am in it knows where it is in its own ten loops?
I hope I made this clear. I am trying a simple proof-of-concept function but it is really confusing.
Thanks

Yes it will be independant: the local variables are local to each call within the recursion as long as you don‘t use them as parameters as they are byref by default:
Sub RecurseMe( intParameter as Integer )
Dim intCount as Integer
Print "Called with:", intParameter
intParameter = intParameter + 1
intCount = intCount + 1
Print "IntCount: ", intCount
If intParameter < 3 then
Call RecurseMe( intParameter )
End If
Print "Exiting with: ", intParameter
End Sub
Dim intTest as Integer
intTest = 1
Call RecurseMe( intTest )
Print "Final result: ", intTest
Will output:
Called with: 1
IntCount: 1
Called with: 2
IntCount: 1
Exiting with: 3
Exiting with: 3
Finale result: 3
As your see: intCount is always reinitialised in sub, intParameter will even be changed in the calling sub.

Related

time_ns() result not saved by writedlm() in julia

I am working with a program which includes many function calls inside a for loop. For short, it is something like this:
function something()
....
....
timer = zeros(NSTEP);
for it = 1:NSTEP # time steps
tic = time_ns();
Threads.#threads for p in 1:2 # Star parallel of two sigma functions
Threads.lock(l);
Threads.unlock(l);
arg_in_sig[p] = func_sig[p](arg_in_sig[p]);
end
.....
.....
Threads.#threads for p in 1:2
Threads.lock(l)
Threads.unlock(l)
arg_in_vel[p] = func_vel[p](arg_in_vel[p])
end
toc=time_ns();
timer[i] = toc-tic;
end # time loop
writedlm("timer.txt",timer)
return
end
What I am trying to do, is to meassure the time that takes to perform on each loop iteration, saving the result in an output file called "timer.txt". The thing is that it doesn't work.
It saves a file with all zeros on it (except two or three values, which is more confusing).
I made a toy example like:
using DelimitedFiles;
function test()
a=zeros(1000)
for i=1:1000
tic = time_ns();
C = rand(20,20)*rand(20,20);
toc = time_ns();
a[i] = toc-tic;
end
writedlm("aaa.txt",a);
return a;
end
and these actually works (it saves fine!). Is there something to do with the fact that I am implementing Threads.#threads?. What can be happening between writedlm() and time_ns() in my program?
Any help would be much apreciated!
You are iterating over it but try to save by:
timer[i] = toc-tic;
while it should be
timer[it] = toc-tic;
Perhaps you have some i in global scope and hence the code still works.
Additionally locking the thread and immediately unlocking does not seem to make much sense. Moreover, when you iterate over p which happens to be also index of the Vector cell where you save the results there is no need to use the locking mechanism at all (unless you are calling some functions that depend on a global state).

Using caching instead of memoization to speedup a function

While memoization of a function is a good idea, it could cause a program to crash because the program could potentially run out of memory.
Therefore it is NOT A SAFE OPTION to be used in a production program.
Instead I have developed caching with a fixed memory slots below with a soft limit and hard limit. When the cache slots is above the hard limit, it will have the least used slots deleted until the number of slots is reduced to the soft limit.
struct cacheType
softlimit::Int
hardlimit::Int
memory::Dict{Any,Any}
freq::Dict{Any,Int}
cacheType(soft::Int,hard::Int) = new(soft,hard,Dict(),Dict())
end
function tidycache!(c::cacheType)
memory_slots=length(c.memory)
if memory_slots > c.hardlimit
num_to_delete = memory_slots - c.softlimit
# Now sort the freq dictionary into array of key => AccessFrequency
# where the first few items have the lowest AccessFrequency
for item in sort(collect(c.freq),by = x -> x[2])[1:num_to_delete]
delete!(c.freq, item[1])
delete!(c.memory, item[1])
end
end
end
# Fibonacci function
function cachefib!(cache::cacheType,x)
if haskey(cache.memory,x)
# Increment the number of times this key has been accessed
cache.freq[x] += 1
return cache.memory[x]
else
# perform housekeeping and remove cache entries if over the hardlimit
tidycache!(cache)
if x < 3
cache.freq[x] = 1
return cache.memory[x] = 1
else
result = cachefib!(cache,x-2) + cachefib!(cache,x-1)
cache.freq[x] = 1
cache.memory[x] = result
return result
end
end
end
c = cacheType(3,4)
cachefib!(c,3)
cachefib!(c,4)
cachefib!(c,5)
cachefib!(c,6)
cachefib!(c,4)
println("c.memory is ",c.memory)
println("c.freq is ",c.freq)
I think this would be most useful in a production environment than just using memorization with no limits of memory consumption which could result in a program crashing.
In Python language, they have
#functools.lru_cache(maxsize=128, typed=False)
Decorator to wrap a function with a memoizing callable that saves up to the maxsize most recent calls. It can save time when an expensive or I/O bound function is periodically called with the same arguments.
Since a dictionary is used to cache results, the positional and keyword arguments to the function must be hashable.
Is there an equivalent in Julia language?
There is LRUCache.jl, which provides an LRU type which basically acts like a Dict. Unfortunately, this doesn't seem to work with the Memoize.jl package, but you can use my answer to your other question:
using LRUCache
const fibmem = LRU{Int,Int}(3) # store only 3 values
function fib(n)
get!(fibmem, n) do
n < 3 ? 1 : fib(n-1) + fib(n-2)
end
end

Efficient-yet-terse way to add all the arrays in a composite type?

I came up with 2 ways to add all the arrays in a pair of composite types. The first way (add_structs_1) takes 4 seconds to run and the second way (add_structs_2) takes 0.15 seconds. But the second way requires a lot more code...I have to explicitly mention each field in the composite type. Is there a way to get the efficiency of add_structs_2, without explicitly listing each field?
type SampleStruct
a::Vector{Float64}
k::Matrix{Float64}
e_axis::Vector{Float64}
e_dev::Vector{Float64}
e_scale::Vector{Float64}
end
function add_structs_1(tgt::SampleStruct, src::SampleStruct)
for n in names(SampleStruct)
for i in 1:length(tgt.(n))
tgt.(n)[i] += src.(n)[i]
end
end
end
function add_structs_2(tgt::SampleStruct, src::SampleStruct)
for i in 1:length(tgt.a)
tgt.a[i] += src.a[i]
end
for i in 1:length(tgt.k)
tgt.k[i] += src.k[i]
end
for i in 1:length(tgt.e_axis)
tgt.e_axis[i] += src.e_axis[i]
end
for i in 1:length(tgt.e_dev)
tgt.e_dev[i] += src.e_dev[i]
end
for i in 1:length(tgt.e_scale)
tgt.e_scale[i] += src.e_scale[i]
end
end
function time_add_structs(f::Function)
src = SampleStruct(ones(3), ones(3,3), [1.], [1.], [1.])
tgt = SampleStruct(ones(3), ones(3,3), [1.], [1.], [1.])
#time for i in 1:1000000
f(tgt, src)
end
end
time_add_structs(add_structs_1)
time_add_structs(add_structs_1)
time_add_structs(add_structs_2)
time_add_structs(add_structs_2)
time_add_structs(add_structs_3)
time_add_structs(add_structs_3)
A more julian approach to add_structs_1 is to make the inner loop a separate function, this allows the compiler to specialize the function on each type in the SampleStruct and gives quite a speedup.
By profiling the code it was visible that the time to execute names(SampleStruct) were quite significant, and this should be done in each iteration of your benchmark, by making it a global constant some time is gained and the function now looks like:
function add_array(a::AbstractArray,b::AbstractArray)
for i in 1:length(a)
a[i] += b[i]
end
end
const names_in_struct = names(SampleStruct)
function add_structs_3(tgt::SampleStruct, src::SampleStruct)
for n in names_in_struct
add_array(tgt.(n),src.(n))
end
end
The function is now within a factor of four of add_structs_2
The metaprogramming approach is more complicated but gives the same performance as add_structs_2
ex = Any[]
for n in names(SampleStruct)
t = Expr(:.,:tgt, QuoteNode(n))
s = Expr(:.,:src, QuoteNode(n))
e=quote
for i in 1:length($t)
$t[i] += $s[i]
end
end
push!(ex,e)
end
eval(quote function add_structs_4(tgt::SampleStruct, src::SampleStruct)
$(Expr(:block,ex...))
end
end)
Each of those for loops could be replaced with a one-liner, making the long version just this:
function add_structs_3(tgt::SampleStruct, src::SampleStruct)
tgt.a[:] += src.a
tgt.k[:,:] += src.k
tgt.e_axis[:] += src.e_axis
tgt.e_dev[:] += src.e_dev
tgt.e_scale[:] += src.e_scale
end
This is the same length as add_structs_1 but slower because it actually builds a temporary array and then does the assignment. You could also use some metaprogramming to generate the longer code.
An approach that should get all the performance of the best case is to combine Daniel's and Stefan's answers: define addition as a separate function just like in Daniel's solution, instead of iterating over the names list each field manually like in Stefan's answer.

Stack overflow error when do a loop inside another loop

In a classic ASP function, when I do a loop inside another as shown in the code below I have a stack overflow error.
Function shift(x,y)
shift = x
For i = 1 to y
shift = shift*2
Next
End Function
Function translate_url(iVal)
sAlpha = "abcdefghijklmnopqrstuvwxyz-_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
doWrite = False
iBase = 63 'DO NOT CHANGE
For i = 4 to 0 step -1
iPos = (iVal and shift(iBase, i*6))/shift(1, i*6)
If iPos Then doWrite = True
If doWrite Then translate_url = translate_url & Mid(sAlpha, iPos + 1,1)
Next
End Function
arr = Split("1,2,3,4,5,6,7,8,9,0",",")
For Each i In arr
response.Write(translate_url(arr(i)))
next
The error does not occur when I remove the loop outside the function. Eg:
response.Write(translate_url(arr(1)))
return "c".
What I need to do to make the code flows down the array and return the corresponding values ​​according to the function?
VBScript has a dark side. Variables scope is one of them.
When you don't declare a variable, VBScript will do it for you, free of charge or error and give it global scope.
What does it mean? Take a look in the main loop:
For Each i In arr
response.Write(translate_url(arr(i)))
next
The i variable becomes global. When you have this later in the function:
For i = 4 to 0 step -1
'...
Next
It's changing the same i variable. This is causing endless loop of function calls.
To resolve this, declare i locally in each function:
Function shift(x,y)
Dim i
'...
End Function
Function translate_url(iVal)
Dim i
'...
End Function
And it will be different variable and no overflow.
As the EVIL global variable i is used in your top level loop and in the functions shift() and translate_url(), you got what you deserve.
Evidence: Just change your loop to
For Each NoliMeTangere In arr
response.Write translate_url(arr(NoliMeTangere))
next
Remedy: Use "Option Explicit" and Dim all local variables in your Subs/Functions/Methods.

Referring to function name within function definition

Given the following:
wscript.echo "fx(0)=" & fx(0)
Function fx( v1 )
fx = 1 + 2
Wscript.echo "fx=" & fx
End Function
Both "echo" lines print the value 3. Why doesn't the one inside the function cause a syntax error or recursive loop? Same question with following sample:
wscript.echo "fy()=" & fy()
wscript.echo "fy=" & fy
Function fy
fy = 1 + 2
Wscript.echo "fy=" & fy
End Function
All echo lines print 3.
I can't find documentation that describes the behavior when a function definition references it's name as an RVALUE.
TIA.
Inside a VBScript function, the name of the function is a locally scoped variable intended to be equivalent to the return value that comes out of the function. It's no different from any other Dim var = .... To treat it as a function, you need to use call semantics.
Basically, you seem to have gotten used to languages that have first-class functions. VBScript doesn't have them, so there is no ambiguity.
In addition to Plynx, VBScript is somewhat ambigeous. That is why you can use wscript.echo fy as well as wscript.echo fy() for functions without parameters.
However, this doesn't work the other way around inside functions where you use the functionname as variable. If you do this:
Function fy
fy = 1 + 2
Wscript.echo "fy=" & fy()
End Function
You'll get an Out of stack space: 'fy' error.
Using the functionname as a function can actually be usefull, because you can now use the functionname as a variable or as a function call whilest building recursive functions.
For readability, I'll recommend to use parenthesis in a function call. It will make reviewing or debugging easier for you or your co-worker.

Resources