# Function definition from a variable (RESTATED)

This is similar to a problem I had earlier.

I have a variable Gj that is related to the nj, for example:

Clear[nj]
Gj = If[nj[1] == 0, 0, nj[1] Log[nj[1]/(nj[1] + nj[2] + nj[3])]]


I would like to have a way of defining a function getGj for a given Gj. For the above example, the function would be defined by:

getGj[{a_,b_,c_}] := If[a == 0, 0,a Log[a/(a+b+c)]]


The closest I have come (thanks, Kuba) is:

Gj := If[nj[[1]]==0,0,nj[[1]] Log[nj[[1]]/(nj[[1]]+nj[[2]]+nj[[3]])]];
Unevaluated[getGj[nj_] := Gj;] /. OwnValues[Gj]


which works well for an input list with numeric values, but

ClearAll[xx,x]
xx=Array[x,3];
getGj[xx]


returns

If[x[1] == 0, 0,
{x[1],x[2],x[3]}[[1]] Log[{x[1],x[2],x[3]}[[1]]/({x[1],x[2],x[3]}[[1]] + {x[1],x[2],x[3]}[[2]]+{x[1],x[2],x[3]}[[3]])]]


rather than

If[x[1] == 0, 0, x[1] Log[x[1]/(x[1]+x[2]+x[3])]]

• Restating the question makes answers out of sync which will confuse future readers... About the question, why do you need to have a variable Gj = If[nj[1] == 0, 0, nj[1] Log[nj[1]/(nj[1] + nj[2] + nj[3])]], where does it come from?
– Kuba
Commented Jan 4, 2020 at 7:14
• It comes from a thermodynamics program that I am trying to write, and I need to calculate the product of the concentration nj[1] and the log of the activity which is n[1]/(n[1]+n[2]+n[3]), even when n[1] equals zero, in which case the product is zero. (Should I have started a new question?) Commented Jan 4, 2020 at 11:39

The first element from a list is nj[[1]], not nj[1].

Additionally notice that Gj can accidentally evaluate to something you don't expect, here nj[[1]] == 0 will remain and keep If unevaluated but e.g. TrueQ@nj will not wait till you provide a value for nj.

That is why this is not the best way to create functions.

You could do:

ClearAll[getGj, nj, Gj];

Gj := If[ (* := !!!*)
nj[[1]] == 0, 0, nj[[1]] Log[nj[[1]]/(nj[[1]] + nj[[2]] + nj[[3]])]
];

Unevaluated[getGj[nj_] := Gj;] /. OwnValues[Gj]

getGj[{1, 2, 3}]

-Log[6]


It is hard to suggest something more handy without a broader context of the question.

• Hello - That works great for numerical values, but I was hoping to also be able to input : xx=Array[x,3];getGj[xx] and obtain If[x[1]==0,0,etc.]. In other words, to have it work for symbolic arrays as well. Commented Jan 3, 2020 at 17:19
• The function I am trying to build is getGj[{a_, b_, c_}] := If[a == 0, 0, a Log[a/(a + b + c)]]; but for general Gj as a function of the nj Commented Jan 3, 2020 at 18:24

I may be missing some subtlety in your code, or outright misunderstanding your intent, but this appears to do the same thing more simply:

ClearAll[getGj, nj, Gj, zz, z];

Gj = If[nj[1] == 0, 0, nj[1] Log[nj[1]/(nj[1] + nj[2] + nj[3])]];

getGj[in_] := Block[{nj}, Gj /. nj[i__] :> RuleCondition @ in[[i]] ];

zz = Array[z, 3];
nj[1] = 22;

getGj[zz]
getGj[{1, 2, 3}]

If[z[1] == 0, 0, z[1] Log[z[1]/(z[1] + z[2] + z[3])]]

-Log[6]


Reference:

• Another test I use is to set some nj to a numerical value, say nj[1]=22. In the above case, getGj[zz] and getGj[{1,2,3}] do not return the proper value. Commented Jan 4, 2020 at 11:32
• PS - I add the nj[1]=22 after the function definition. Commented Jan 4, 2020 at 11:43
• @Paul Please see and test the update. Commented Jan 4, 2020 at 12:49

This appears to work:

ClearAll[getGj, nj, Gj, zz, z, a, b, c, x, xx];
Gj = If[nj[1] == 0, 0, nj[1] Log[nj[1]/(nj[1] + nj[2] + nj[3])]];
rul = Table[nj[j] -> Slot[j], {j, 1, 3}]
getGjx = Evaluate[Gj /. rul] &;
xx = Array[x, 3];
getGj[xx_] := Apply[getGjx, xx]
ClearAll[xx]


as a test:

ClearAll[z]
zz = Array[z, 3];
nj[1]=22;
getGj[zz]
getGj[{1, 2, 3}]


yields

If[z[1] == 0, 0, z[1] Log[z[1]/(z[1] + z[2] + z[3])]]
-Log[6]


as hoped for. Any improvements would be welcome.

(P.S. I don't understand why I cant substitute the expression for getGjx into the definition of getGj.)