3
$\begingroup$

I would like to define a function over a list of variables like

f[a_,b_,c_]:=a+b+c

However, when this list is big it makes uncomfortable to read. Can I somehow define a function in this way:

list={a_,b_,c_};
f[list]:=a+b+c

It will be perfect if the answer code will not request me to know the size of this list. I mean so that bigger list

list={a_,b_,c_,d_} Improved formatting

will not cause me to change the previous definition

f[list]:=a+b+c

For example here I'm trying to define all neccessary functions. Some use already defined functions. But you can see that I have to rewrite all needed variables every time I define new function. It's really annoying to spend the time seeking what variables I could miss, because they all need diferent variables.

(*All functions' definitions:*)
(*Fixed costs*)
F=1.;
(*Variable part of cost function with ponetial nonlinearity*)
c[f_]:=1/f;
(*Dirty quantity*)
Q[L1_,L2_,x1_,x2_,τ_]:=L1*x1+L2*x2*τ;
(*Pure quantity*)
Q$pure[L1_,L2_,x1_,x2_,τ_]:=L1*x1+L2*x2;
(*Cost function*)
V[L1_,L2_,x1_,x2_,τ_,f_]:=c[f]*Q[L1,L2,x1,x2,τ]+F;
(*Derivative of cost function with respect to Q dirty, made by inverse chain rule*)
Der$V[L1_,L2_,x1_,x2_,τ_,f_]:=Derivative[0,0,1,0,0,0][V][L1,L2,x1,x2,τ,f]/Derivative[0,0,1,0,0][Q][L1,L2,x1,x2,τ];
(*wage*)
w[x$HH_,x$HF_,x$FH_,x$FF_,L$H_,L$F_,τ_,f_,a_,m_]:=x$FH/x$HF*(1-ru[x$HF,a,m])/(1-ru[x$FH,a,m])*V[L$H,L$F,x$HH,x$HF,τ,f]/Der$V[L$H,L$F,x$HH,x$HF,τ,f]*Der$V[L$F,L$H,x$FF,x$FH,τ,f]/V[L$F,L$H,x$FF,x$FH,τ,f];
(*Lagrangian multiplier fo Home*)
λ$H[x$HH_,x$HF_,x$FH_,x$FF_,L$H_,L$F_,τ_,f_,a_,m_]:=Derivative[1,0,0][u][x$HH,a,m]*(1-ru[x$HH,a,m])/w[x$HH,x$HF,x$FH,x$FF,L$H,L$F,τ,f,a,m]/Der$V[L$H,L$F,x$HH,x$HF,τ,f];
(*Lagrangian multiplier fo Foreign*)
λ$F[x$HH_,x$HF_,x$FH_,x$FF_,L$H_,L$F_,τ_,f_,a_,m_]:=Derivative[1,0,0][u][x$FF,a,m]*(1-ru[x$FF,a,m])/Der$V[L$F,L$H,x$FF,x$FH,τ,f];
(*Total number of firms in a contry*)
N$[L1_,L2_,x1_,x2_,τ_,f_]:=L1/V[L1,L2,x1,x2,τ,f];

One way I'm trying to solve this problem is this:

vl = {a_, m_, s_, τ_, c_, x$HH_, x$HF_, x$FF_, x$FH_, λ$H_, λ$F_, L$H_, L$F_, w_, δ_};
vle = {a, m, s, τ, c, x$HH, x$HF, x$FF, x$FH, λ$H, λ$F, L$H, L$F, w, δ};
vle1 = {1, 1, 1, 1, 1, 1, 1, x$FF, x$FH, λ$H, λ$F, L$H, L$F, w, 1};
xx[vl] := vle
xx[vle1]

But it doesn't work because I can't make Mathematica think about "vl" in the definition of function "xx" as taken representation of "vl", but without blanks. If I ask

xx[vle1]

it returns

{a, m, s, τ, c, x$HH, x$HF, x$FF, x$FH, λ$H, λ$F, L$H, L$F, w, δ}
$\endgroup$
7
  • $\begingroup$ Look up Apply, __, _List in the Documentation. With knowledge about these three you should be abe to answer your own question. $\endgroup$
    – Sektor
    Commented Apr 25, 2015 at 21:28
  • $\begingroup$ It can be done, but it doesn't look like a good idea. With[{list = {a_, b_, c_}}, f[list] := a + b + c]. Try not to do this. Also, this won't work in every situation. I really can't understand why you can't just inline list. Why don't you explain in more detail what you are trying to achieve? There's very likely a much better solution. $\endgroup$
    – Szabolcs
    Commented Apr 25, 2015 at 21:29
  • $\begingroup$ Sorry for my English. Inline means just write it in a row f[a_,b_,c_]? The reason is that I ahve a lot of function definition. And some functions need already defined functios. Like: p$HH[x$HH_] := Derivative[1, 0, 0][u][x$HH, a, m]/\[Lambda]$H p$HF[x$HF_] := Derivative[1, 0, 0][u][x$HF, a, m]/\[Lambda]$F \[Nu]$H[L$H_, L$F_, x$HH_, x$HF_, \[Tau]_, f_] := (L$H*p$HH[x$HH]*x$HH + L$F*p$HF[x$HF]*x$HF - w*(c*(L$H*x$HH + \[Tau]*L$F*x$HF) + F)) And if I forgot to include any small variable it doesn't work and it is not convinient.... $\endgroup$
    – dart_kaide
    Commented Apr 25, 2015 at 21:33
  • $\begingroup$ wouldn't f[list_]:=Total@list solve it? (ahem, this is what @sektor meant with Apply) $\endgroup$
    – Sos
    Commented Apr 25, 2015 at 21:34
  • $\begingroup$ The summation here is just to simplify things. Ofcourse I will use some complex operations. $\endgroup$
    – dart_kaide
    Commented Apr 25, 2015 at 21:38

2 Answers 2

2
$\begingroup$

This approach seems to work.

Clear@f
list = {a, b, c}
argList := Evaluate@
   Sequence @@ (Pattern[#, Blank[]] & /@ list)

f[argList] := a + b + c

DownValues@f
 (* {HoldPattern[f[a_, b_, c_]] :> a + b + c} *)

f[1, 2, 3]
 (* 6 *)

I think it fits your request. If not, maybe try manipulating DownValues[f] directly, but it won't be elegant!

Edit: Code hygiene issues — scope

This code is a bit unhygienic because it uses global definitions for function arguments (i.e. things that should be local). There is a significant risk of confusing side effects.

list has to be correctly defined at the time f[arglist] is called. After that, you can over-write it. This is problematic because list isn't apparent in the line f[argList] := a + b + c; so another reader wouldn't necessarily realise that overwriting list would cause that definition to fail. Much the same is true of a, b and c.

$\endgroup$
2
  • $\begingroup$ I see your point... But can I make it simplier? I added the way I'm trying now to my question. But I can't make it work. However you way is also very nice! The added question starts from "One way I'm trying to solve this problem is this:" $\endgroup$
    – dart_kaide
    Commented Apr 25, 2015 at 22:27
  • $\begingroup$ Sorry, you way is actually the BEST! $\endgroup$
    – dart_kaide
    Commented Apr 25, 2015 at 22:33
1
$\begingroup$

Simplest solution

Use Set instead of SetDelayed:

vl = {a_, m_, s_, τ_, c_, x$HH_, x$HF_, x$FF_, x$FH_, λ$H_, λ$F_, L$H_, L$F_, w_, δ_};
vle = {a, m, s, τ, c, x$HH, x$HF, x$FF, x$FH, λ$H, λ$F, L$H, L$F, w, δ};
vle1 = {1, 1, 1, 1, 1, 1, 1, x$FF, x$FH, λ$H, λ$F, L$H, L$F, w, 1};
xx[vl] = vle;
xx[vle1]
xx[2 vle1]
(*  {1, 1, 1, 1, 1, 1, 1, x$FF, x$FH, λ$H, λ$F, L$H, L$F, w, 1}  *)
(*  {2, 2, 2, 2, 2, 2, 2, 2 x$FF, 2 x$FH, 2 λ$H, 2 λ$F, 2 L$H, 2 L$F, 2 w, 2} *)

This has pitfalls and does not quite address all the conditions set out in the OP's question. First, if the variables in vle = {a,...} have values, then they will be used instead of the symbols when xx is defined. If for instance a = 43 at the time xx[vl] = vle is executed, then xx[vle1] would return {43, 1,...}. Assuming vl is a flat list of named patterns, a workaround is to block all the symbols used as pattern names using an injector pattern:

Hold @@ vl /. Verbatim[Pattern][v_, _] :> v /. Hold[vars___] :>
 Block[{vars}, xx[vl] = vle]

Second, this solution doesn't satisfy the desired property that the definition depends on vl. The symbol vl is evaluated and replaced by an explicit list of argument patterns before the down-value for xx is created.

??xx
(*
  xx[{a_,m_,s_,τ_,c_,x$HH_,x$HF_,x$FF_,x$FH_,λ$H_,λ$F_,L$H_,L$F_,w_,δ_}] =
   {a,m,s,τ,c,x$HH,x$HF,x$FF,x$FH,λ$H,λ$F,L$H,L$F,w,δ}
*)

The solution f[argList] := a + b + c by djp also has this drawback. In both solutions, xx has to be redefined if vl is changed.

Solution to an implicit question

It's really annoying to spend the time seeking what variables I could miss...

Here is a function that will check the definitions (i.e. each down value) of a function and report Global` symbols present in the definition but missing from the arguments. (There seemed to me to be both advantages and disadvantages to making getHeldSymbols and getHeldPatternNames both HoldAll and adapting the code of missingArgs to those attributes. I could not decide which way was best.)

ClearAll[getHeldSymbols, getHeldPatternNames];
getHeldSymbols[e_] := DeleteDuplicates@
  Cases[e, v_Symbol /; Context[v] === "Global`" :> Hold[v], Infinity];
getHeldPatternNames[e_] := DeleteDuplicates@
  Cases[e, Verbatim[Pattern][v_, _] :> Hold[v], Infinity]

ClearAll[missingArgs];
missingArgs::nofn = "Symbol `` has no down values.";
missingArgs[f_Symbol] /; DownValues[f] =!= {} :=
  missingArgs /@ DownValues[f];
missingArgs[Verbatim[HoldPattern][f_[args___]] :> val_] :=
  HoldPattern[f[args]] -> Complement[
    getHeldSymbols[Hold[val]],
    getHeldPatternNames[Hold[args]]
    ];
missingArgs[f_Symbol] := Message[missingArgs::nofn, f];

Examples:

Clear[f];
f[a_, b_, c_] := a + b + c;
missingArgs[f]
  (*
   {HoldPattern[f[a_, b_, c_]] -> {}}
  *)
Clear[f];
f[a_, b_, c_] := a + Exp[c] + d;
missingArgs[f]
  (*
   {HoldPattern[f[a_, b_, c_]] -> {Hold[d]}}
  *)
Clear[f];
f[a_, b_, c_] := a + b + c + d;
f[{a_, b_, c_}] := a + Exp[c] + e;
missingArgs[f]
  (*
   {HoldPattern[f[a_, b_, c_]] -> {Hold[d]}, 
    HoldPattern[f[{a_, b_, c_}]] -> {Hold[e]}}
  *)

Improvement or triviality? Re my original interpretation and solution

The problem originally appeared to me to be one of constructing a function definition that would at run-time adapt to a dynamically changing argument pattern. In its general scope, such a problem is not a duplicate of Can a function be made to accept a variable amount of inputs?. The MWE, which might not reflect the subtleties of the general use-case, might be solved with code like one of the following or approaches from the linked question:

f[{a_, b_, c_, ___}] := a + b + c;
f[list_] /; Length[list] >= 3 := Total[list[[ ;; 3]]];

On the other hand, most situations do not require such indirect function construction as shown below, and I have to echo Szabolcs's comment that there is probably a better approach.

Indeed, here's a general solution for creating a delayed function evaluation that adapts to changes to the argument pattern, but which raises the question of the triviality of the problem. It stores the argument patterns and replacement value in delayedFunction, which is evaluated when passed an input.

ClearAll[setDelayedDelayed, delayedFunction];
SetAttributes[setDelayedDelayed, HoldAll];
SetAttributes[delayedFunction, HoldAll]; 
setDelayedDelayed[f_Symbol, args_, val_] /; ListQ[args] := 
  f = delayedFunction[args, val];
With[{f = Unique[]}, 
  delayedFunction[args_, val_][x___] /; MatchQ[{x}, args] := 
   Thread[getHeldPatternNames[Evaluate@args], Hold] /. 
    Hold[vars_] :> Block[vars, Block @@ Hold[{f}, (f[##] = val) & @@ args; f[x]]]
  ];

Example:

Clear[xx, a];
vl = {a_, m_, s_, τ_, c_, x$HH_, x$HF_, x$FF_, x$FH_, λ$H_, λ$F_, L$H_, L$F_, w_, δ_};
vle = {a, m, s, τ, c, x$HH, x$HF, x$FF, x$FH, λ$H, λ$F, L$H, L$F, w, δ};
a = 43;   (* to verify that  a  is blocked *)

setDelayedDelayed[xx, {vl}, vle]
(*  delayedFunction[{vl}, vle]  *)

xx[vle1]
(*  {1, 1, 1, 1, 1, 1, 1, x$FF, x$FH, λ$H, λ$F, L$H, L$F, w, 1}  *)

AppendTo[vl, dummy_]     (* add an extra argument to  vl  *)
(*  {a_, m_, s_, τ_, c_, x$HH_, x$HF_, x$FF_, x$FH_, λ$H_, λ$F_, L$H_, L$F_, w_, δ_,
     dummy_}  *)

xx[Append[vle1, extra]]  (* and an extra input *)
(*  {1, 1, 1, 1, 1, 1, 1, x$FF, x$FH, λ$H, λ$F, L$H, L$F, w, 1}  *)

What the code shows, Mathematica already has such functions for constructing functions at run-time, namely Set and SetDelayed (even ReplaceAll could be used). The only nontrivial aspect is localizing the arguments in args and val. But that can be done as shown at the end of the simplest answer. One of the sillier things about the delayedFunction solution is that the function is basically constructed each time it is evaluated. This is probably fast in almost all cases, so it is not terribly inefficient.

Original solution

In any case, working from my initial interpretation, it seems to me that if the list of arguments changes, the expression the function computes might change, too. There are two forms for the argument pattern, one which appears in the OP's question and one that appears in djp's answer. It is easy to give both.

arglist = {a_, b_, c_};
expr = a + b + c;

Clear[f];
f[x_] /; MatchQ[x, arglist] := 
 x /. arglist -> (Hold[expr] /. OwnValues[expr]) // ReleaseHold

f[{x, y, z}]
(*  x + y + z  *)

Clear[f];
f[x__] /; MatchQ[{x}, arglist] :=
   {x} /. arglist -> (Hold[expr] /. OwnValues[expr]) // ReleaseHold

f[x, y, z]
(*  x + y + z  *)

arglist = {a_, b_, c_, d_};
expr = a + b + c + 2 d;
f[x, y, z, w]
(*  2 w + x + y + z  *)

If any of the variables have a value, then the values will be substituted in the assignment to expr, which is probably an undesired behavior. A way around this is to Block the variables while expr is being defined.

Block @@ (Hold[#,
      expr = a + b + c + d
      ] &@arglist /. Verbatim[Pattern][x_, _] :> x)
$\endgroup$

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