Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

variable number of arguments

 [1/17] from: gchiu:compkarori at: 26-Nov-2000 10:45


Can a function have a variable number of arguments? -- Graham Chiu

 [2/17] from: al:bri:xtra at: 26-Nov-2000 11:10


Graham wrote:
> Can a function have a variable number of arguments?
No. But you can simulate it, by using 'any-type! function specifiers and passing unset! as arguments. Better is to use refinements. Andrew Martin ICQ: 26227169 http://members.nbci.com/AndrewMartin/

 [3/17] from: lmecir:mbox:vol:cz at: 26-Nov-2000 3:08


Hi,
> Graham wrote: > > Can a function have a variable number of arguments? > > No. But you can simulate it, by using 'any-type! function specifiers and > passing unset! as arguments. Better is to use refinements. > > Andrew Martin
yes, a function can have a variable number of arguments. Do is such a function, as e.g. in: take-n: func [n /local spec] [ spec: copy [] for i 1 n 1 [ append spec to word! append copy "a" to string! i ] func spec reduce [:reduce append reduce [to lit-word! append copy "take" to string! n] spec] ] do take-n 4 1 2 3 4 == [take4 1 2 3 4] , where Do took 5 arguments. I call such functions VNA functions. The problem is, that there is currently no possibility to define VNA functions in Rebol, which is a pity, because it would allow us to create some nice dialects in Rebol (a message-passing dialect me and Elan tried to define some time ago can serve as an example). Fortunately, there is a way, how to emulate that. A function taking a block as its argument can behave as if the elements of the block were the arguments. As an example you can have a look at Any and All functions, or a lot of other nice examples. Regards Ladislav

 [4/17] from: al:bri:xtra at: 26-Nov-2000 16:18


Ladislav Mecir wrote:
> Andrew Martin wrote: > > Graham Chiu wrote: > > > Can a function have a variable number of arguments? > > No. But you can simulate it, by using 'any-type! function specifiers and
passing unset! as arguments. Better is to use refinements.
> Yes, a function can have a variable number of arguments. Do is such a
function, as e.g. in:
> take-n: func [n /local spec] [ > spec: copy [] > for i 1 n 1 [ > append spec to word! append copy "a" to string! i > ] > func spec reduce [:reduce append reduce [to lit-word! append copy
take to string! n] spec]
> ] > do take-n 4 1 2 3 4 > == [take4 1 2 3 4] > > , where Do took 5 arguments.
Actually, 'do took only one argument, the value returned from 'take-n (which had the argument "4"), which happened to be a function that is evaluated by 'do that could take the next four arguments "1 2 3 4". So the answer to the question:
> Can a function have a variable number of arguments?
is still: No.
>> help do
USAGE: DO value /args arg /next DESCRIPTION: Evaluates a block, file, URL, function, word, or any other value. DO is a native value. ARGUMENTS: value -- Normally a file name, URL, or block (Type: any) REFINEMENTS: /args -- If value is a script, this will set its system/script/args arg -- Args passed to a script. Normally a string. (Type: any) /next -- Do next expression only. Return block with result and new position. Note that 'do only has one argument. Andrew Martin ICQ: 26227169 http://members.nbci.com/AndrewMartin/

 [5/17] from: lmecir:mbox:vol:cz at: 26-Nov-2000 12:09


Hi,
> Ladislav Mecir wrote: > > Andrew Martin wrote: > > > Graham Chiu wrote: > > > > > Can a function have a variable number of arguments? > > > > No. But you can simulate it, by using 'any-type! function specifiers
and
> passing unset! as arguments. Better is to use refinements. > > > Yes, a function can have a variable number of arguments. Do is such a > function, as e.g. in: > >
take-n: func [n /local spec] [ spec: copy [] for i 1 n 1 [ append spec to word! append copy "a" to string! i ] func spec reduce [:reduce append reduce [to lit-word! append copy "take" to string! n] spec] ]
> > do take-n 4 1 2 3 4 > > == [take4 1 2 3 4] > > > > , where Do took 5 arguments. > > Actually, 'do took only one argument, the value returned from 'take-n
(which
> had the argument "4"), which happened to be a function that is evaluated
by
> 'do that could take the next four arguments "1 2 3 4". >
[snip] Think for the second time, please. Let's make sure, how many arguments we have to take: make-sure1: func [ { This function takes just one argument } f ] [ ; evaluate the argument f ] make-sure1 take-n 4 1 2 3 4 The result: ** Script Error: f is missing its a1 argument ** Where: make-sure1 ** Near: f make-sure2: func [ { This function takes five arguments } f a2 a3 a4 a5 ] [ ; evaluate f f a2 a3 a4 a5 ] make-sure2 take-n 4 1 2 3 4 The result: == [take4 1 2 3 4] Conclusion: Do had to take five arguments to yield the correct result. Best regards Ladislav

 [6/17] from: tim:johnsons-web at: 26-Nov-2000 8:29


Hello: I'm not sure where this thread started... but alternatively, one could have a function with a block as argument. With rebol's type? and length? native values, then the code block could really be designed to process any number of "pseudo-arguments" in the block. JMTCW tj

 [7/17] from: rryost:home at: 26-Nov-2000 12:05


As a rank amateur, couldn't a function handle a variable number of arguments if they were "passed" in a single block? Russell [rryost--home--com]

 [8/17] from: al:bri:xtra at: 27-Nov-2000 8:33


Russell Yost pointed out:
> As a rank amateur, couldn't a function handle a variable number of arguments if they were "passed" in a single block?
Of course. That's the best way of passing any number of arguments. You can also pass the multiple arguments as a string of characters as well if that makes more sense (parsing a file for example. Andrew Martin Still thinking about 'do...

 [9/17] from: al:bri:xtra at: 27-Nov-2000 12:27


GC> Can a function have a variable number of arguments? AM> No. But you can simulate it, by using 'any-type! function specifiers and passing unset! as arguments. Better is to use refinements. LM> Yes, a function can have a variable number of arguments. Do is such a function, as e.g. in: take-n: func [n /local spec] [ spec: copy [] for i 1 n 1 [ append spec to word! append copy "a" to string! i ] func spec reduce [ :reduce append reduce [ to lit-word! append copy "take"to string! n ] spec ] ] LM> do take-n 4 1 2 3 4 LM> == [take4 1 2 3 4] LM> , where Do took 5 arguments. AM> Actually, 'do took only one argument, the value returned from 'take-n which had the argument "4"), which happened to be a function that is evaluated by 'do that could take the next four arguments "1 2 3 4". LM> Think for the second time, please. Let's make sure, how many arguments we have to take: make-sure1: func [ {This function takes just one argument} f ][ ; evaluate the argument f ] make-sure1 take-n 4 1 2 3 4 The result: ** Script Error: f is missing its a1 argument ** Where: make-sure1 ** Near: f make-sure2: func [ {This function takes five arguments} f a2 a3 a4 a5 ][ ; evaluate f f a2 a3 a4 a5 ] make-sure2 take-n 4 1 2 3 4 The result: == [take4 1 2 3 4] LM>Conclusion: Do had to take five arguments to yield the correct result. AM>Actually, I disagree. 'Do only took one argument. The function returned by 'take-n takes the four arguments. Rebol functions are a bit more active than in conventional languages. Take for example, this function: fget: func [:a][probe :a] It doesn't do much, not even _evaluating_ it's 'a argument. Here's a small session at Rebol console:
>> fget: func [:a][probe :a] >> fget 1 + 2
1 == 3 Why did "1" get printed? And why was the result of 3 printed _after_ the "1"? Here's another function: feval: func [a][probe :a] Note that I've dropped one colon out in the "function spec". Therefore 'a is evaluated. Here's the Rebol console session:
>> feval: func [a][probe :a] >> feval 1 + 2
3 == 3 Note that both cases, the line: probe :a never evaluates 'a. So where does 'a get evaluated? Effectively here: func [a] More exactly, 'a is evaluated inside the brackets after "func". Or perhaps better to say that an evaluated argument in a function processes or evaluates the input stream of values until a natural stopping point is reached. In other words, the input stream reduces down to a value that is not a func or prefix operator (effectively a function) and isn't a value followed by a binary operator value. The input stream of values is also terminated by the presence of a right square bracket. Because "continuations", IIRC, aren't in Rebol, the function argument evaluation mechanism can't "leap out" of the function body and resume processing the "upper" or "outer" level input stream of values. So the error message: ** Script Error: f is missing its a1 argument ** Where: make-sure1 ** Near: f means what it says, and 'do only has one argument. I hope that helps! Andrew Martin

 [10/17] from: larry:ecotope at: 26-Nov-2000 18:03


Hi Andrew, Ladislav This example may help resolve the question in a direct fashion. We can use do/next to identify the first expression in a block.
>> x: 1 y: 2 z: 3
== 3
>> do/next [do x y z]
== [1 [y z]] This shows that the inner do consumed only one argument.
>> take2: func [x y] ["hello"] >> do/next [take2 x y z]
== ["hello" [z]] This shows the function take2 consumed (as expected) two arguments. Cheers -Larry

 [11/17] from: lmecir:mbox:vol:cz at: 27-Nov-2000 9:35


> GC> Can a function have a variable number of arguments? > > AM> No. But you can simulate it, by using 'any-type! function specifiers
and passing unset! as arguments. Better is to use refinements.
> LM> Yes, a function can have a variable number of arguments. Do is such a
function, as e.g. in:
> take-n: func [n /local spec] [ > spec: copy []
<<quoted lines omitted: 12>>
> LM> , where Do took 5 arguments. > AM> Actually, 'do took only one argument, the value returned from 'take-n
which had the argument "4"), which happened to be a function that is evaluated by 'do that could take the next four arguments "1 2 3 4".
> LM> Think for the second time, please. Let's make sure, how many arguments
we have to take:
> make-sure1: func [ > {This function takes just one argument}
<<quoted lines omitted: 20>>
> LM>Conclusion: Do had to take five arguments to yield the correct result. > AM>Actually, I disagree. 'Do only took one argument. The function returned
by 'take-n takes the four arguments. Rebol functions are a bit more active than in conventional languages. Take for example, this function:
> fget: func [:a][probe :a] > It doesn't do much, not even _evaluating_ it's 'a argument. Here's a
small session at Rebol console:
> >> fget: func [:a][probe :a] > >> fget 1 + 2 > 1 > == 3 > > Why did "1" get printed? And why was the result of 3 printed _after_ the
1 ? LM> I think, that the explanation of the above can be pretty simple: function Fget asks for its argument to be fetched, which means, that no 1 + 2 calculation is allowed *before* supplying an argument to Fget. That is why the evaluation of the addition is *postponed* here until Fget is evaluated. Let's make sure: make-sure3: func [:a] [2 * probe :a] make-sure3 1 + 2 1 == 4 AM>
> Here's another function: > feval: func [a][probe :a] > Note that I've dropped one colon out in the "function spec". Therefore
'a is evaluated. Here's the Rebol console session:
> >> feval: func [a][probe :a] > >> feval 1 + 2
<<quoted lines omitted: 5>>
> func [a] > More exactly, 'a is evaluated inside the brackets after "func". Or
perhaps better to say that an evaluated argument in a function processes or evaluates the input stream of values until a natural stopping point is reached. In other words, the input stream reduces down to a value that is not a func or prefix operator (effectively a function) and isn't a value followed by a binary operator value. The input stream of values is also terminated by the presence of a right square bracket. Because continuations , IIRC, aren't in Rebol, the function argument evaluation mechanism can't "leap out" of the function body and resume processing the upper or "outer" level input stream of values. LM> Here, as opposed to your previous example, the evaluation of the Feval's argument is allowed *before* Feval gets it, that'a all. No "inside brackets" evaluation is needed to explain the difference. AM>
> So the error message: > ** Script Error: f is missing its a1 argument
<<quoted lines omitted: 4>>
> Andrew Martin > -><-
LM> It doesn't help, because you didn't succeed to explain, why you are'nt able to write a Rebol function similar to Make-sure4: make-sure4: func [ {This function takes five arguments} f a2 a3 a4 a5 ][ ; evaluate f f :a2 :a3 :a4 :a5 ] , but able to take any function (not only a function having at most four arguments) and evaluate it correctly. FYI my explanation is, that if such a hypothetical function had to evaluate its first argument correctly, it should have supplied it all necessary arguments, but that is not possible, because the only Rebol function capable of doing that is currently the Do function. This subject has something in common with Rebol orthogonality, where we see, that Rebol, using itself a function capable of taking a variable number of arguments, doesn't allow a user to define such a function routinely too. The interesting thing about that is, that such functions can be added to Rebol without *any* difficulty, because such functions basically do the same as the functions taking a block as their only argument. Thanks for the interesting discussion on the subject, I hope, that it wasn't boring for anybody. Regards Ladislav

 [12/17] from: lmecir:mbox:vol:cz at: 27-Nov-2000 9:57


Hi Larry, you *are* correct, of course, but your list is not complete.
> Hi Andrew, Ladislav > This example may help resolve the question in a direct fashion. We can use
<<quoted lines omitted: 10>>
> Cheers > -Larry
take-n: func [n /local spec] [ spec: copy [] for i 1 n 1 [ append spec to word! append copy "a" to string! i ] func spec reduce [:reduce append reduce [to lit-word! append copy "take" to string! n] spec] ] do/next [do take-n 4 1 2 3 4 5] == [[take4 1 2 3 4] [5]] shows, that Do took 5 arguments here, IMO. Regards Ladislav

 [13/17] from: al:bri:xtra at: 28-Nov-2000 20:52


Ladislav wrote:
> shows, that Do took 5 arguments here, IMO.
No. 'do took only one argument. Andrew Martin ICQ: 26227169 http://members.nbci.com/AndrewMartin/

 [14/17] from: al:bri:xtra at: 28-Nov-2000 21:29


> > >> fget: func [:a][probe :a] > > >> fget 1 + 2 > > 1 > > == 3 > > > > Why did "1" get printed? And why was the result of 3 printed _after_ the
1 ?
> LM> I think, that the explanation of the above can be pretty simple:
function Fget asks for its argument to be fetched, which means, that no 1 + 2 calculation is allowed *before* supplying an argument to Fget. That is why the evaluation of the addition is *postponed* here until Fget is evaluated. Let's make sure:
> make-sure3: func [:a] [2 * probe :a] > make-sure3 1 + 2 > 1 > == 4
While that argument seems to fit, it doesn't fit the fact that there's no do involved? Functions don't need to ask "do" to evaluate themselves, they actively get their own arguments, when they're evaluated.
> AM> > > Here's another function: > > feval: func [a][probe :a] > > Note that I've dropped one colon out in the "function spec".
Therefore 'a is evaluated. Here's the Rebol console session:
> > > > >> feval: func [a][probe :a]
<<quoted lines omitted: 5>>
> > probe :a > > never evaluates 'a. So where does 'a get evaluated? Effectively
here:
> > func [a] > > More exactly, 'a is evaluated inside the brackets after "func". Or
perhaps better to say that an evaluated argument in a function processes or evaluates the input stream of values until a natural stopping point is reached. In other words, the input stream reduces down to a value that is not a func or prefix operator (effectively a function) and isn't a value followed by a binary operator value. The input stream of values is also terminated by the presence of a right square bracket. Because continuations , IIRC, aren't in Rebol, the function argument evaluation mechanism can't "leap out" of the function body and resume processing the upper or "outer" level input stream of values.
> LM> Here, as opposed to your previous example, the evaluation of the
Feval's argument is allowed *before* Feval gets it, that'a all. No "inside brackets" evaluation is needed to explain the difference. No, the function just evaluates it's own arguments as specfied by it's function spec. There's no need for the function to ask an external evaluation system to do it.
> AM> > > So the error message:
<<quoted lines omitted: 7>>
> > I hope that helps! > LM> It doesn't help, because you didn't succeed to explain, why you aren't
able to write a Rebol function similar to Make-sure4:
> make-sure4: func [ > {This function takes five arguments}
<<quoted lines omitted: 4>>
> ] > , but able to take any function (not only a function having at most four
arguments) and evaluate it correctly. You can if you delay evaluation until the function is in the same context as it's arguments:
>> do func [a b c][print [a + 10 b + 20 c + 30]] 1 2 3
11 22 33 which is effectively what your function 'take-n simplifies down to.
> FYI my explanation is, that if such a hypothetical function had to
evaluate its first argument correctly, it should have supplied it all necessary arguments, but that is not possible, because the only Rebol function capable of doing that is currently the Do function. 'do doesn't do that. It only seems to.
> This subject has something in common with Rebol orthogonality, where we
see, that Rebol, using itself a function capable of taking a variable number of arguments, doesn't allow a user to define such a function routinely too. The interesting thing about that is, that such functions can be added to Rebol without *any* difficulty, because such functions basically do the same as the functions taking a block as their only argument. Here's how 'do works for the example above. Rebol evaluates 'do. 'do requires one arguement and evaluates it's one argument, the 'func word (which happens to be next at the "instruction pointer"). 'func evaluates it's two arguments, the spec and body blocks, and returns itself as a function! datatype. (The input "instruction pointer" is now at the start of the "1 2 3" sequence. 'do sees the function! datatype and evaluates the function! datatype again. The function checks out its spec "[a b c]" and evaluates the "1 2 3" in succession (advancing the "instruction pointer" successively) and assigns the values to "a b c" then evaluates it's function body (print "11 22 33"), returning the last result in the block to 'do, which is unset!, which the console won't display.
> Thanks for the interesting discussion on the subject, I hope, that it
wasn't boring for anybody. It's been interesting. I had to really think about it. Reminded me of programming in Intel 8085 machine code and Motorola 6809 assembler, but this was a lot easier. Andrew Martin Rebol assembler! ICQ: 26227169 http://members.nbci.com/AndrewMartin/

 [15/17] from: lmecir:mbox:vol:cz at: 29-Nov-2000 9:47


Andrew Martin, AKA Rebol assembler! wrote: It's been interesting. I had to really think about it. Reminded me of programming in Intel 8085 machine code and Motorola 6809 assembler, but this was a lot easier. Envying Andrew I dare to write a lengthy essay on the subject. Task #1: Write a Rebol function Eval-1 taking a Rebol function as its first argument. Eval-1 shall check its first argument to make sure, that it is a one-argument-taking Rebol function. Next it shall evaluate it. The number of Eval-1's arguments shall be as low as possible to accomplish the requirements above. The first argument shall be passed normally (i.e. neither unevaluated nor fetched). My Eval-1 implementation is below The Line. (Don't look there, if you want to try to do it on your own first.) Task #2: Write a Rebol function Eval-2 taking a Rebol function as its first argument. Eval-2 shall check its first argument to make sure, that it is a one-argument-or-two-arguments-taking Rebol function. Next, it shall evaluate it. The first argument shall be passed normally (i.e. neither unevaluated nor fetched), moreover, you can suppose, that it uses the normal argument passing too. The number of Eval-2's arguments shall be as low as possible. Again, my implementation is below The Line. The Line -------------------------------------------------------------------------- ; uncomment the line below, if you don't have defined the Nargs function before ;do http://www.sweb.cz/LMecir/highfun.r eval-1: func [ [catch] function [any-function!] arg [any-type!] ] [ if (nargs :function) <> 1 [ throw make error! {Incorrect function!} ] function get/any 'arg ] Let's test it: f1: func [x] [x] f2: func [x [any-type!]] [type? get/any 'x] f3: does [1]
>> eval-1 :f1 1
== 1
>> eval-1 :f2 :f1
== function!
>> eval-1 :f3
** User Error: Incorrect function! ** Near: eval-1 :f3 eval-2: func [ [catch] function [any-function!] args [block!] ] [ if all [ (nargs :function) <> 1 (nargs :function) <> 2 ] [ throw make error! {Incorrect function!} ] do append copy [function] args ] Well, let's have a look: f4: func [x y] [reduce [x y]] eval-2 :f1 [1] eval-2 :f2 [:f1] eval-2 :f3 [1 2] eval-2 :f4 [1 2] (tried your implementation?) When I wrote my Eval-2's implementation, I was told, that its interface was ugly and that the interface of the Do native looked much better, as in: do :f4 1 2 That really looks better. I realized, that the only way, how to mimic the behaviour of the Do native is to allow Eval-2 to take three arguments or two arguments, depending on the Function argument, which is something I shall describe in the next part. To be continued... Ladislav

 [16/17] from: lmecir:mbox:vol:cz at: 30-Nov-2000 21:25


For many people the meaning of: The function took N arguments. may be unclear. To be as precise as possible, here is my definition: args-taken?: function [ {How many arguments a function took?} f [any-function!] args [block!] ] [test] [ test: make block! 1 + length? args append test :f repeat i length? args [ append/only test to paren! reduce [ :pick 'args i ] ] test: do/next test if empty? second test [ print {Warning! To obtain the complete result you should supply more arguments.} ] (length? args) - (length? second test) ] Let's test the above definition:
>> args-taken? :list-dir []
feedback.r license.html nntp.r notes.html rebdoc.r rebol.exe rebol.r setup.html user.r Warning! To obtain the complete result you should supply more arguments. == 0 Here the List-dir function took 0 arguments, but it might have done so just because we didn't supply more. To make sure, we will watch what happens, if we supply more arguments:
>> args-taken? :list-dir [%.]
feedback.r license.html nntp.r notes.html rebdoc.r rebol.exe rebol.r setup.html user.r Warning! To obtain the complete result you should supply more arguments. == 1
>> args-taken? :list-dir [%. %.]
feedback.r license.html nntp.r notes.html rebdoc.r rebol.exe rebol.r setup.html user.r == 1 Now a more complicated example:
>> args-taken? :- [1 2 3]
== 1 The above result is telling us, that the - op used as a prefix operator normally takes just one argument, even though Rebol is saying:
>> help -
USAGE: value1 - value2 DESCRIPTION: Returns the second value subtracted from the first. - is an op value. ARGUMENTS: value1 -- (Type: number pair char money date time tuple) value2 -- (Type: number pair char money date time tuple)
>> args-taken? :do [:- 1 2 3]
== 1 Here we see, what the majority expected: Do took one argument.
>> args-taken? :do reduce [:- 1 2 3]
== 3 Wow! Ladislav (preferring Rebol to assembler)

 [17/17] from: lmecir:mbox:vol:cz at: 3-Dec-2000 16:32


Hi all, I promised you to introduce some Variable Number Of Arguments taking functions, but am not sure, if it is worth the effort, because to design such a dialect is a time-consuming work. That is why I am tempted to resign for the time being and return to it later eventually. If you are missing my explanation why args-taken? :do reduce [:- 1 2 3] yields 3, while args-taken? :- [1 2 3] yields 1, let me know. Regards Ladislav

Notes
  • Quoted lines have been omitted from some messages.
    View the message alone to see the lines that have been omitted