# Can a function be made to accept a variable amount of inputs?

I have a function that takes two inputs and processes them for a single output. What I need is one that can take a varying number of inputs. and process them to a single output. Is this possible in Mathematica?

 DatasetAverage[inputData_, inputData2_] := Block[{dataAvg, v, w},
v = Length[inputData];
w = Length[inputData2];
If [v != w,
Print["DatasetAverage: Data sample sizes do not match"]];
dataAvg = Table[Mean[{inputData[[i]], inputData2[[i]]}], {i, 1, Length[inputData2]}]
]

-
You may want to wait for other answers before accepting. I am sure the regulars here can offer more refined answers. –  Michael Wijaya Jun 9 '12 at 1:44
I discussed multiple-arg functions briefly in my book, which may add a little more info to the excellent answers you received here. –  Leonid Shifrin Jun 9 '12 at 9:58
thanks @Leonid Shifrin I will review it! –  R Hall Jun 10 '12 at 17:02

Yes, for both named patterns and pure functions.

## Pure functions

You can see that inherently they accept multiple arguments but discard those that are not used:

{#} &[1, 2, 3]  (* out: {1} *)


The object ##, (SlotSequence), represents all arguments wrapped in Sequence, e.g. Sequence[1, 2, 3]. (Internally it doesn't use Sequence but it behaves similarly in most places.)

{##} &[1, 2, 3]  (* out: {1, 2, 3} *)


You can combine # and ##, including their numbered forms:

{"first" -> #, "rest" -> {##2}, "all" -> {##}} &[1, 2, 3]

{"first" -> 1, "rest" -> {2, 3}, "all" -> {1, 2, 3}}


## Named patterns

Using Blank*:

_ (Blank)
__ (BlankSequence)
___ (BlankNullSequence):

f[a_, b__] := {"first" -> a, "rest" -> {b}}

f[1, 2, 3] (* out: {"first" -> 1, "rest" -> {2, 3}} *)


__ requires an argument to be present while ___ does not:

f[1] (* out: f[1] *)

g[a_, b___] := {"first" -> a, "rest" -> {b}}

g[1] (* out: {"first" -> 1, "rest" -> {}} *)


Multiple variable length named patterns can be given and will by default be matched shortest first:

h[a_, b__, c__] := {"a" -> a, "b" -> {b}, "c" -> {c}}

h[1, 2, 3, 4, 5]  (* out: {"a" -> 1, "b" -> {2}, "c" -> {3, 4, 5}} *)


This can be controlled with Shortest and Longest:

i[a_, Longest[b__], c__] := {"a" -> a, "b" -> {b}, "c" -> {c}}

i[1, 2, 3, 4, 5] (* out: {"a" -> 1, "b" -> {2, 3, 4}, "c" -> {5}} *)


The Blank* functions are not the only way to create a variable length pattern. You can also use Repeated (..) or RepeatedNull (...):

j[x : _Real ..] := {x}

j[1.1, 1.2, 1.3]  (* out: {1.1, 1.2, 1.3} *)


These methods can be used in powerful ways such as destructuring.

## Optional arguments

In addition to the variable length methods above one can make use of Optional  parameters with or without the use of Default values. A basic example:

k[a_, b_: 3, c_: 5] := {a, b, c}

k[1]
k[1, 2]
k[1, 2, 3]

{1, 3, 5}

{1, 2, 5}

{1, 2, 3}


In the example above there are two optional parameters. By default they are filled in sequential order, meaning that in k[1, 2] the 2 is bound to b. This also can be controlled with Shortest and Longest as noted in the section above.

In a limited way optional arguments can also be used for pure functions:

Default can be used to set the default values for a given function, rather than specifying them as part of each function definition, but it must be used before they are defined, and it cannot be used to change them for existing definitions. (Why does Default behave like this?)

Default[m, 1] = 1;
Default[m, 2] = 3;
Default[m, 3] = 5;

m[a_., b_., c_.] := {a, b, c}

m[]
m[2]
m[2, 4]
m[2, 4, 6]

{1, 3, 5}

{2, 3, 5}

{2, 4, 5}

{2, 4, 6}


Also see:

Related Q&A's of a more advanced nature:

-
Thanks very much! Looks like I will be re-writing some code to take advantage of these features! –  R Hall Jun 9 '12 at 2:42
I saw the pure functions and instantly knew who wrote the answer... –  Brett Champion Jun 9 '12 at 3:02
@Brett I hope that's a good thing. :-) –  Mr.Wizard Jun 9 '12 at 7:15
@Mr.Wizard, how would you call on the last argument using Pure functions? The context I have in mind is that I am going to display the product of a variable number of matrices and I want to refer to the last matrix in my code. It looks like Length[{##}] will give me the number of inputs but I am not sure how to access ##n where n is this number of inputs. –  Ben Allgeier Jul 31 '14 at 5:05
@BenAllgeier Does Last[{##}] do what you want or do I misunderstand? –  Mr.Wizard Jul 31 '14 at 5:36

Just one supplementary remark regarding conditions on the argument list:

Functions with a variable number of arguments can be very useful, and patterns like x__ allow you to define them easily.

But while the number of arguments may not be fixed, there may still be restrictions or limits on the allowed number. This can be enforced by using a condition as shown here:

f[x__] := Total@Apply[Times, Partition[{x}, 2], 2] /; EvenQ[Length[{x}]]


In this example, f is intended to form pairs from the arguments, multiply the pairs and add them. That's why there is a condition appended to the definition with /; - it insures that the definition is not used unless an even number of arguments is supplied.

If the condition isn't met, the function returns unevaluated - unless you provide another definition for a function with the same name with a different condition.

-
Related question: mathematica.stackexchange.com/q/1835/121 –  Mr.Wizard Jun 9 '12 at 3:39

This may be close to what you want:

datasetAverage[inputDataList_] :=
If[
Equal @@ Map[Length, inputDataList] == False,
Print["DatasetAverage: Data sample sizes do not match"],
Mean[inputDataList]
]


Some sample inputs:

In[10]:= datasetAverage[{{1,2,3},{4,5,6},{7,8,9}}]
Out[10]= {4,5,6}

datasetAverage[{{1,2,3},{4,5,6,7}}]
DatasetAverage: Data sample sizes do not match


Edit

My earlier version of datasetAverage was defined using BlankSequence, but it did not make use of the fact that it can take an unspecified number of parameters. It is now rewritten using Blank.

Here is one version which uses BlankSequence:

datasetAverage2[inputDataList__] :=
If[
Equal @@ Map[Length, {inputDataList}] == False,
Print["DatasetAverage: Data sample sizes do not match"],
Mean[{inputDataList}]
]


The only difference is that I now enclose every occurrence of inputDataList in parentheses.

In[9]:= datasetAverage2[{1,2,3},{4,5,6},{7,8,9}]
Out[9]= {4,5,6}

-
Thank you! What allows for the multiple inputs? –  R Hall Jun 9 '12 at 1:38
@RHall I used the BlankSequence pattern, which stands for any sequence of one or more expressions. So now I can feed in a sequence of datasets. –  Michael Wijaya Jun 9 '12 at 1:40
Thanks very much! –  R Hall Jun 9 '12 at 1:43
I'm rather confused by your edit. Why did you convert this to a single argument function? –  Mr.Wizard Jun 9 '12 at 3:38
@Mr.Wizard I mixed up two ideas when I wrote up the answer. The original function does not actually require the use of BlankSequence. In fact, it fails if I evaluate datasetAverage[{1,2,3},{4,5,6}]. –  Michael Wijaya Jun 9 '12 at 3:48

Mean already does what your DataSetAverage is expected to do. From docs Mean > More Information:

that is, Mean map-threads over sub-lists in its argument.

So

Mean[{{a, b, c}, {u, v, w}}]


gives

and

Mean[{{a, b, c}, {u, v, w, x}}]


throws the error

So, you can use something like

ClearAll[dtAvrg];
dtAvrg[lst__List] :=  Check[Mean[{lst}], "DatasetAverage: Data sample sizes do not match",  Mean::rectt]


examples:

 dtAvrg[{a, b, c}, {u, v, w}, {x, y, z}]


returns

and

 dtAvrg[{1, 2, 3}, {4, 5, 6}, {7, 8, 9}]


gives {4,5,6}.

For lists with unequal lengths,

dtAvrg[{a, b, c}, {u, v, w}, {x, y, z, t}]


returns

Mean::rectt: Rectangular array expected at position 1 in Mean[{{a,b,c},{u,v,w},{x,y,z,t}}].
"DatasetAverage: Data sample sizes do not match"


If you like you can Quiet the error message and display your message only:

 Quiet@dtAvrg[{a, b, c}, {x, y, z, t}]
(* gives:  "DatasetAverage: Data sample sizes do not match" *)

-
Great tip Thank you! –  R Hall Jun 9 '12 at 10:54
+1 Your method is much cleaner than mine. It is good to know about Check too. –  Michael Wijaya Jun 9 '12 at 17:27
@MichaelWijaya, thank you for the vote. –  kguler Jun 9 '12 at 17:36