PDA

View Full Version : Idea about "VAR ARG" parameters



Robert Hodge
04-07-2013, 15:59
In the post regarding blank-delimited concatenation, a sample function was shown that took 16 arguments. 16 was an arbitrary number; we can put up to 32 if we are ambitious enough to type that many, but here is what it looked like:


FUNCTION $ (S01 AS STRING, _
OPTIONAL S02 AS STRING, _
S03 AS STRING, _
S04 AS STRING, _
S05 AS STRING, _
S06 AS STRING, _
S07 AS STRING, _
S08 AS STRING, _
S09 AS STRING, _
S10 AS STRING, _
S11 AS STRING, _
S12 AS STRING, _
S13 AS STRING, _
S14 AS STRING, _
S15 AS STRING, _
S16 AS STRING) AS STRING

DIM I AS LONG, X(16) AS STRING, GAP AS STRING, RESULT AS STRING

IF FUNCTION_CParams >= 01 THEN X(01) = S01
IF FUNCTION_CParams >= 02 THEN X(02) = S02
IF FUNCTION_CParams >= 03 THEN X(03) = S03
IF FUNCTION_CParams >= 04 THEN X(04) = S04
IF FUNCTION_CParams >= 05 THEN X(05) = S05
IF FUNCTION_CParams >= 06 THEN X(06) = S06
IF FUNCTION_CParams >= 07 THEN X(07) = S07
IF FUNCTION_CParams >= 08 THEN X(08) = S08
IF FUNCTION_CParams >= 09 THEN X(09) = S09
IF FUNCTION_CParams >= 10 THEN X(10) = S10
IF FUNCTION_CParams >= 11 THEN X(11) = S11
IF FUNCTION_CParams >= 12 THEN X(12) = S12
IF FUNCTION_CParams >= 13 THEN X(13) = S13
IF FUNCTION_CParams >= 14 THEN X(14) = S14
IF FUNCTION_CParams >= 15 THEN X(15) = S15
IF FUNCTION_CParams >= 16 THEN X(16) = S16

FOR I = 1 TO 16
IF VERIFY (X(I), " ") > 0 THEN
RESULT = RESULT & GAP & X(I)
GAP = " "
END IF
NEXT
RETURN RESULT

END FUNCTION

Obviously, the most burdensome aspect of this function is just handling all the potential optional arguments and putting them into an array.

The C language has long had a way to deal with this concept, using what is called "variable arguments", commonly abbreviated to VAR ARGS. This provides a way to access arguments on the 'stack' without knowing ahead of time what they were.

I believe TB could fairly easily incorporate such a facility. It would be a matter of treating the arguments as a variable-length array of some given type. While a new keyword like VARARGS could be created to address this, there's no reason why OPTIONAL couldn't be used in a new way. The variable number of arguments would effectively be put into an array, whose size could be determined with a call to UBOUND.

Here is the function above rewritten to use these concepts. Note how much shorter it is. Here, S(OPTIONAL) means that S is array of STRING values of varying size, which is determined by the UBOUND function.


FUNCTION $ ( S(OPTIONAL) AS STRING ) AS STRING

DIM I AS LONG, GAP AS STRING, RESULT AS STRING

FOR I = 1 TO UBOUND (S)
IF VERIFY (S(I), " ") > 0 THEN
RESULT = RESULT & GAP & S(I)
GAP = " "
END IF
NEXT
RETURN RESULT

END FUNCTION

ReneMiner
04-07-2013, 19:30
Nice. (to fill 10 chars)

Petr Schreiber
05-07-2013, 00:13
Hi Robert,

interesting concepts as usually. Regarding passing of array with arbitrary dimensions - it is possible right now:



String myArray(3) = "A", "B", "C"

MsgBox 0, Func(myArray)

Function Func( S() As String ) As String

Dim I As Long, GAP As String, RESULT As String

For I = 1 To UBound (S)
If Verify (S(I), " ") > 0 Then
RESULT = RESULT & GAP & S(I)
GAP = " "
End If
Next
Return RESULT

End Function



Petr

Robert Hodge
05-07-2013, 02:07
Hi Robert,

interesting concepts as usually. Regarding passing of array with arbitrary dimensions - it is possible right now:



String myArray(3) = "A", "B", "C"

MsgBox 0, Func(myArray)

Function Func( S() As String ) As String

Dim I As Long, GAP As String, RESULT As String

For I = 1 To UBound (S)
If Verify (S(I), " ") > 0 Then
RESULT = RESULT & GAP & S(I)
GAP = " "
End If
Next
Return RESULT

End Function


Petr

The key difference here is that you would not be passing an array, but individual parameters.

So, you'd have a function header like

Function Func( S(OPTIONAL) As String ) As String

and then call it as x= Func ("ABC", "DEF") rather than an array argument. Basically, when TB saw the combination of

Func( S(OPTIONAL) As String

and

x = Func ("ABC", "DEF")

it would create some "parameter setup code" that did the equivalent of:

DIM temp(2) AS STRING
temp(1) = "ABC"
temp(2) = "DEF"
x = Func (temp)

ReneMiner
05-07-2013, 09:37
I see some problem here:

example

Sub foo(Byval S1(Optional) As String, Byval S2(Optional) As String)

how to determine which ends where? use brackets?

foo(["Hello", "This", "Is", "S1"], ["And", "This", "Is", "S2"]) ???

Robert Hodge
05-07-2013, 15:20
I see some problem here:

example

Sub foo(Byval S1(Optional) As String, Byval S2(Optional) As String)

how to determine which ends where? use brackets?

foo(["Hello", "This", "Is", "S1"], ["And", "This", "Is", "S2"]) ???

That's an excellent question. The short answer is, given the proposed notation like S1(OPTIONAL), there could only be one such parameter in the function foo. For basically the same reason that the current OPTIONAL attribute applies to all parameters that follow it, you could only have one OPTIONAL array as a parameter.

I think your bracket notation is brilliant. What you would probably call this is an "array constructor". How would that work?

First, since the array constructor would construct a temporary, but ordinary (garden-variety) array, the (OPTIONAL) notation would not be needed in the function header, which would now look like this:


Sub foo(Byval S1() As String, Byval S2() As String)

Within the SUB/FUNCTION, array bounds would be determined using UBOUND as usual.

Second, each bracketed expression would get assigned to a temporary array, then passed to the function. In this case,


foo(["Hello", "This", "Is", "S1"], ["And", "This", "Is", "S2"])

would be turned into the equivalent of this:


DIM temp1(4), temp2(4) AS STRING

temp1(1) = "Hello"
temp1(2) = "This"
temp1(3) = "Is"
temp1(4) = "S1"

temp2(1) = "And"
temp2(2) = "This"
temp2(3) = "Is"
temp2(4) = "S2"

foo (temp1, temp2)

The advantages of the "array constructor" syntax is that (a) it doesn't require new SUB/FUNCTION parameter syntax, (b) doesn't require coordination between the SUB/FUNCTION header declaration and the call to the function (that is, the code generation process is much less complex), existing functions that already take standard array arguments could be used with array-constructor arguments without having to rewrite the function argument(s) as NAME(OPTIONAL), and (d) it offers the ability to have more than one variable-length 'literal array' to be passed, as you suggest in your example code.

The main difficulty I see is in setting up the temp arrays to pass parameters. You showed your sample 'foo' function as taking its parameters BYVAL, and that's fine. If for some reason you wanted to pass a bunch of variables BYREF, it might be trickier. I have confidence that Eros could figure that out, but it would be a little harder to implement.

Only other issue to verify is that brackets aren't used anywhere else in TB. I don't think they are, but maybe some syntax is 'lurking around' somewhere that I'm not aware of. Eros would have to confirm that.

Bracketed array constructors? I like.

Charles Pegge
05-07-2013, 15:49
A form of ellipsis (...) could be the answer:

function foo(s ... as string)
'receive params as s(1),s(2) etc.
end function

foo "cabbage","king","shoe","" 'using empty string as end marker

Robert Hodge
05-07-2013, 17:12
A form of ellipsis (...) could be the answer:

function foo(s ... as string)
'receive params as s(1),s(2) etc.
end function

foo "cabbage","king","shoe","" 'using empty string as end marker

The ... ellipsis notation is used in C/C++, and it's certainly possible. That would be an alternative to my OPTIONAL notation. Here, any of these would basically say the same thing:


FUNCTION FOO ( S(OPTIONAL) AS STRING) AS STRING
FUNCTION FOO ( S ... AS STRING) AS STRING
FUNCTION FOO ( S(...) AS STRING) AS STRING

etc. The specific syntax isn't that critical; you'd just have to decide on something that wouldn't break the language or the lexical scanner, and agree to it, then implement it.

But, the point Rene brings up is a stronger one. If you are going to alter the calling sequence based on the function header, it complicates the language. Consider the following examples, where I will use the S(OPTIONAL) notation, just to pick one:


X = F1("ONE", "TWO")
Y = F2("ONE", "TWO")

FUNCTION F1 ( S(OPTIONAL) AS STRING) AS STRING
...
END FUNCTION

FUNCTION F2 (S1 AS STRING, S2 AS STRING) AS STRING
...
END FUNCTION

The problem comes in when the calls are issued. By all appearances, the calls to F1 and F2 look "identical", but because F1 takes an OPTIONAL ARRAY, TB has to create a temporary STRING array, insert the values into it, then call the function, like this:


DIM temp(2) AS STRING
temp(1) = "ONE"
temp(2) = "TWO"
x = F1(temp)

whereas the call the F2 is made just like it looks: Y = F2("ONE","TWO").

So, TB can't just look at the call to the SUB/FUNCTION itself - it would have to coordinate the call with the information in the header to decide how to launch the function. That's quite a bit harder. Not that it can't be done, and I am sure Eros could figure this out if sufficiently motivated, but it would be a good bit of work. But - even if this were done - you would STILL have to alter the SUB/FUNCTION header with the new syntax, whether that was S(OPTIONAL) or S ... or whatever. That means any existing functions that took arrays could not be used; you'd have to have one version for arrays, and another for this OPTIONAL/'ellipsis' notation. Again, not that this couldn't be done, but you could not resue any existing code to take advantage of the new feature; you would have to write all new code.

That's why I think Rene's "bracketed array constructor" idea is a better idea - better than mine even. It's easier to implement, and can be used for more things, and can be applied to existing function code that accepts array parameters.

ReneMiner
05-07-2013, 17:55
If the brackets are used for something already - (was it some matrix-stuff for multi-dimensional arrays?) - one certainly could use those curly braces also:


foo({"This","Is","S1"},{"Here", "Is", "S2"})

and it should enable this way so for those too lazy to count theirselves:


Dim A() as String = {"Hello", "I", "Am", "Element", "Five"}
' which equals

Dim A() as String
Array Assign A() = "Hello", "I", "Am", "Element", "Five"


btw. that should work with numeral "array-assignements" also then.