Referring to function name within function definition - recursion

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.

Related

Julia Macro to Save the Overt form of a Function, and Define it

Some of the parameters to a simulation I am writing are functions. When the output is generated, I want to put the definition of these functional parameters in the output. I have in mind a macro that somehow saves the definition as a string, and then defines it. For example, here's what I do now:
borda_score_fn(p) = exp(1/p)
global g_borda_score_fn_string = "exp(1/p)"
And then I write g_borda_score_fn_string to my output. But this is really ugly!
What I would like to do is something like this:
#paramfn borda_score_fn(p) = exp(1/p)
And later be able to both call borda_score_fn(p), and have the form (i.e., "exp(1/p)") available for writing to my output log. (The string form might get stashed in a global dict, actually, they both could.)
I have tried many version of this, but can't get the right set of parses and calls to get it to work. Any help would be appreciated.
This may be a bit different than what you have in mind, but one perhaps "Julian" approach might be to have the function itself return the form string via multiple dispatch, rather than defining a whole new global variable just for that. For example, say we have a type
struct Form end
that we can use for dispatch, then we can write
borda_score_fn(p) = exp(1/p)
borda_score_fn(::Form) = "exp(1/p)"
which can then be retrieved just by calling the function with our type
julia> borda_score_fn(2)
1.6487212707001282
julia> borda_score_fn(Form())
"exp(1/p)"
That might actually be not bad on its own. But, if you want a macro to do both parts at once, then something along the lines of
macro paramfn(e)
name = esc(e.args[1].args[1])
str = string(e.args[2].args[2])
f = esc(e)
quote
$name(::Form) = $str
$f
end
end
would let you write
julia> #paramfn borda_score_fn(p) = exp(1/p)
borda_score_fn (generic function with 2 methods)
julia> borda_score_fn(1)
2.718281828459045
julia> borda_score_fn(Form())
"exp(1 / p)"
For completeness, here's how you can do it in a way more similar to your original approach, but more idiomatically than with a global variable:
julia> module FormOf
export formof, #paramfn
function formof end
macro paramfn(expr)
name = esc(expr.args[1].args[1])
form_str = string(expr.args[2].args[2])
quote
$(esc(expr))
$FormOf.formof(::typeof($name)) = $form_str
$name
end
end
end
Main.FormOf
julia> FormOf.#paramfn borda_score_fn(p) = exp(1/p)
borda_score_fn (generic function with 1 method)
julia> FormOf.formof(borda_score_fn)
"exp(1 / p)"
However, since it defines a new method of FormOf.formof, this only works in global scope:
julia> function bla()
FormOf.#paramfn fn(p) = exp(1/p)
fn(10) + 1
end
ERROR: syntax: Global method definition around REPL[45]:10 needs to be placed at the top level, or use "eval".
Stacktrace:
[1] top-level scope
# REPL[50]:1
#cbk's solution does not have this limitation.

Will local variables be independent in recursively called Lotusscript function?

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.

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.

Ada String Concatenation

I have a function that returns a string for a particular item, and I need to call that function numerous times and combine those strings into one. The combined string is bounded. I've made sure to fill it when space characters when it initializes but I keep getting "length check failed" errors. Is there something basic I'm doing wrong here?
FOR I IN 1..Collection.Size LOOP
Combined_String := combined_string & Tostring(Collection.Book(I));
END LOOP;
Unbounded_String is probably the easiest way to go:
with Ada.Strings.Unbounded;
use Ada.Strings.unbounded;
...
Temp_Unbounded_String : Unbounded_String; -- Is empty by default.
...
for I in 1 .. Collection.Size loop
Append(Temp_Unbounded_String, ToString(Collection.Book(I));
end loop;
If you then need to have the result placed in your fixed length standard string:
declare
Temp_String : constant String := To_String(Temp_Unbounded_String);
begin
-- Beware! If the length of the Temp_String is greater than that of the
-- fixed-length string, a Constraint_Error will be raised. Some verification
-- of source and target string lengths must be performed!
Combined_String(Temp_String'Range) := Temp_String;
end;
Alternatively, you can use the Ada.Strings.Fixed Move() procedure to bring the Unbounded_String into the target fixed-length string:
Ada.Strings.Fixed.Move(To_String(Temp_Unbounded_String), Combined_String);
In this case, if the source string is "too long", by default a Length_Error exception is raised. There are other parameters to Move() that can modify the behavior in that situation, see the provided link on Move for more detail.
In order to assign Combined_String, you must assign the full correct length at once. You can't "build up" a string and assign it that way in Ada.
Without seeing the rest of your code, I think Ada.Strings.Unbounded is probably what you should be using.
I know this is an ancient question, but now that Ada 2012 is out I thought I'd share an idiom I've been finding myself using...
declare
function Concatenate(i: Collection'index)
is
(tostring(Collection(i) &
if (i = Collection'last) then
("")
else
(Concatenate(i+1))
);
s: string := Concatenate(Collection'first);
begin
Put_Line(s);
end;
Typed off the top of my head, so it'll be full of typos; and if you want it to work on empty collections you'll need to tweak the logic (should be obvious).
Ada 2012's expression functions are awesome!
Ada works best when you can use perfectly-sized arrays and strings. This works wonderfully for 99% of string uses, but causes problems any time you need to progressively build a string from something else.
Given that, I'd really like to know why you need that combined string.
If you really need it like that, there are two good ways I know of to do it. The first is to use "unbounded" (dynamically-sized) strings from Ada.Strings.Unbounded, as Dave and Marc C suggested.
The other is to use a bit of functional programming (in this case, recursion) to create your fixed string. Eg:
function Combined_String (String_Collection : in String_Collection_Type) return String is
begin
if String_Collection'length = 1 then
return String_Collection(String_Collection'first);
end if;
return String_Collection(String_Collection'first) &
Combined_String (String_Collection'first + 1 .. String_Collection'last);
end Combined_String;
I don't know what type you used for Collection, so I'm making some guesses. In particular, I'm assuming its an unconstrained array of fixed strings. If it's not, you will need to replace some of the above code with whatever your container uses to return its bounds, access elements, and perform slicing.
From AdaPower.com:
function Next_Line(File : in Ada.Text_IO.File_Type :=
Ada.Text_Io.Standard_Input) return String is
Answer : String(1..256);
Last : Natural;
begin
Ada.Text_IO.Get_Line(File => File,
Item => Answer,
Last => Last);
if Last = Answer'Last then
return Answer & Next_Line(File);
else
return Answer(1..Last);
end if;
end Next_Line;
As you can see, this method builds a string (using Get_Line) of unlimited* length from the file it's reading from. So what you'll need to do, in order to keep what you have is something on the order of:
function Combined_String (String_Collection : in String_Collection_Type)
Return String is
begin
if String_Collection'length = 1 then
Return String_Collection(String_Collection'First).All;
end if;
Recursion:
Declare
Data : String:= String_Collection(String_Collection'First).All;
SubType Constraint is Positive Range
Positive'Succ(String_Collection'First)..String_Collection'Last;
Begin
Return Data & Combined_String( String_Collection(Constraint'Range) );
End Recursion;
end Combined_String;
Assuming that String_Collection is defined as:
Type String_Collection is Array (Positive Range <>) of Access String;
*Actually limited by Integer'Range, IIRC

Resources