3
$\begingroup$

Let's say I now have a set of variables {x,y,z},I define functions in terms of x,y, and z,I can do that

f[x_, y_, z_] := x + y + z

The downside of this definition is that I have to type x_,y_,z_ repeatedly

If there are 100 variables, I have to do this f[x1_,x2_,x3_....x100_]:=...,It's inefficient!

I came up with some ideas,this is my code

vars = {x, y, z}

f[x_, y_, z_] := x + y + z

f1 = Function[Evaluate@vars, x + y + z] @@ # &

f1@{1,2,3}

foo[x_] := f @@ (x /. a_Symbol -> a_)

foo@vars

But there are flaws,like this:

enter image description here

so I want to go back to f[x_,y_,z_], using f[vars] is obviously not possible,I want to do it in the most intuitive way

enter image description here

What should I do?

$\endgroup$
11
  • 2
    $\begingroup$ F[Thread[Pattern[Evaluate[vars], _]]] := something $\endgroup$
    – chyanog
    2 days ago
  • 1
    $\begingroup$ I agree with you it is not good idea to pass many variables to a function. The less, the better. This is true in any language and not just Mathematica. It can lead to programming errors. Other languages use a Record or struct to pass many variables in function call in one such record/struct. You can do similar thing in Mathematica. see struct-equivalent-in-mathematica for examples. $\endgroup$
    – Nasser
    2 days ago
  • $\begingroup$ @Nasser But my function really has dozens of variables $\endgroup$ 2 days ago
  • $\begingroup$ f={x,y,z}|->x+y+z. $\endgroup$
    – cvgmt
    2 days ago
  • 3
    $\begingroup$ See if you can change the way you operate on your variables so you can accept a list directly. For instance, in your model case, appropriate variations could be: g[list_] := Total[list] or h[list_] := Plus @@ list. This would modify the function calls to g[{x, y, z}]. It may also have the added advantage that variables in your function call can be grouped by function / logically, alleviating the chance of simple errors and improving readability. $\endgroup$
    – MarcoB
    2 days ago

2 Answers 2

11
$\begingroup$

All of your proposals require you to type the full argument list as well as to reference each argument in your function definition. This isn't any reduction in typing for a single function definition. So, I'm inferring that you actually have several functions that all use the same argument list. It's not difficult to define a pattern expression:

argPattern1 = Sequence[x_, y_, z_]

and then reference it:

f1[argPattern1] := x + y + z;
f2[argPattern1] := x y z
(* f1[1, 2, 3] -> 6 *)

If you'd rather start with a list of argument names, you could do something like this instead:

argNames = {x, y, z};
argPattern2 = Pattern[#, Blank[]] & /@ argNames

Usage:

g1[argPattern2] := 10 (x + y + z);
g2[argPattern2] := (x + y + z)/10
(* g1[{1, 2, 3}] -> 60 *)

Of course, if you started with a list but on occasion need the sequence:

argPattern3 = Sequence @@ argPattern2;
h[argPattern3] := 3 x + y + 2 z
(* h[1, 2, 3] -> 11 *)

Or, having defined a function already that uses the argument list you want, you could manipulate DownValues:

DownValues[k] = ReplacePart[DownValues[f1] /. f1 -> k, {1, 2} -> (x^2 + y^2 + z^2)]
(* k[1, 2, 3] -> 14 *)

Manipulating DownValues can be dicey, so be careful. Also make sure that the raw argument names don't have their own definitions--maybe protect them or do the operations in a With or something else along those lines.

Going a completely different route, you could use Slot and just never deal with naming arguments repeatedly:

q = Function[#1 + #2^#3]
(* q[1, 2, 3] -> 9 *)
$\endgroup$
4
$\begingroup$

While it's unclear which of the following is the desired form, I'll give a general approach and then some specific alternatives:

f[x1_, x2_,..., x100_] := ...    (* coordinates as separate arguments *)
f[{x1_, x2_,..., x100_}] := ...  (* coordinates in a single vector *)

First interpretation

I interpret the exclamation, "It's inefficient!", to mean it's inefficient to type. Thus we seek an efficient way to edit. Some code-construction together with copy and paste is helpful.

First construct our arguments. We can apply our func to get code that is ready to edit and add our definition := .... We can then apply our next function to args and edit the output to construct another function. It seems efficient to me.

ClearAll["x*", func]; (* make the workspace ready *)
args = Table[Pattern[Evaluate@ToExpression["x" <> ToString@n], _],
  {n, 100; 3}]
func @@ args
func @ args
(*
  {x1_, x2_, x3_}
  func[x1_, x2_, x3_]
  func[{x1_, x2_, x3_}]
*)

Alternatively, depending on the desired form, one can copy the output of args, or one can delete the { and } from the output of args. In either case, one gets a cell that can be copied and pasted into a function definition.

So I don't see any inefficiency in constructing the left-hand side of the definition. The formula for right-hand side, in terms of a hundred variables, might be horrible, but that's beside the point, I guess.

Second interpretation

I can see three possible options: We could want the first form (like in @lericr's answer currently accepted), the second form (like in the OP's answer), or both forms to work. In my early days of using Mathematica, I became irritated that functions $f : {\bf R}^3 \rightarrow {\bf R}$ might equally be considered a function of a single point $(x,y,z)$ or a function of three independent variables $x$, $y$, and $z$. It makes a difference to Mathematica, but not to mathematicians. So I got in the habit of doing both, as seen in the examples func1 and func2. The third, which uses an anonymous Function, cannot be polymorphic. The fourth could be made polymorphic in the same way func1 is; or one could use Apply@func4, which operator form of Apply did not exist in the early days.

The setup. We set up the variables inside Hold[]. We also assign values to the variables to check that our definitions do not leak evaluations.

ClearAll["x*", func1, func2, func3, func3vec, func4];
vars = Flatten[
   Hold @@ 
    ToExpression[Table["x" <> ToString@n, {n, 3}], StandardForm, 
     Hold]];
{x1, x2, x3} = Prime@Range@3;

1, 2. Note func1 evaluates the formula when passed the individual coordinate, and has a form for converting func1[vector] to func1[x1, x2, x3]. In func2 it's the reverse.

With[{nvars = Length@vars},
  func1[x_List] /; Length@x == nvars := func1 @@ x;
  Activate@Replace[vars, x_ :> Inactive[Pattern][x, _], 1] /. 
   Hold[v__] :>
    (func1[v] := x1 + x2 + x3);
  ];

func2[x_List] := x . {1, 1, 1}; (* math. short-cut :) *)
func2[x___] := func2[{x}];

3. An anonymous function can't be polymorphic, so different versions are given for each form. One could have func3vec call func3 conditionally, but that hardly seems a solution anyone would prefer.

vars /. Hold[v__] :>
  (func3 = Function[{v}, x1 + x2 + x3])

(*  Function[{x1, x2, x3}, x1 + x2 + x3]  *)

vars /. Hold[v__] :>
  (func3vec =
    Block[{v},
      {v} = #;
      x1 + x2 + x3] &)

(*  Block[{x1, x2, x3}, {x1, x2, x3} = #1; x1 + x2 + x3] &  *)

4. If one can construct the body of the function expr programmatically, then func4 shows a way to make it into a function. The function func4 can be overloaded to handle a vector argument in the same was that func1 was.

expr = vars /. Hold[v__] :> Block[{v}, (* make held expression *)
     x1 + x2 + x3 // Hold[#] &];       (* for body of func4 *)
{Activate@Replace[vars, x_ :> Inactive[Pattern][x, _], 1],
   expr} /.
 {Hold[v__], Hold[e_]} :> (func4[v] := e);

Testing. Since func3 and func4 don't have a vector form, they can be applied to a vector argument with the operator form Apply@f.

Through[{func1, func2, Apply@func3, Apply@func4}[Table[x[n], {n, 3}]]]
(*
{x[1] + x[2] + x[3],
 x[1] + x[2] + x[3],
 x[1] + x[2] + x[3], 
 x[1] + x[2] + x[3]}
*)

Through[{func1, func2, func3, func4}[x[1], x[2], x[3]]]
(* <same as above> *)

func3vec@Table[x[n], {n, 3}] (* for completeness *)
(*  x[1] + x[2] + x[3]  *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.