5

I am trying to setup a bunch of pure Functions in particular with an identical rahter long parameter list.

({a, b, c, d, e, f} \[Function] a + b)[r, s, t, u, v, w]
({a, b, c, d, e, f} \[Function] a*f)[r, s, t, u, v, w]
({a, b, c, d, e, f} \[Function] a*b/c)[r, s, t, u, v, w]

this works as expected:

r+s
r w
(r s)/t

Now I would like to put the formal parameter List in a variable:

pl = {a, b, c, d, e, f};
(Evaluate@pl \[Function] a + b)[r, s, t, u, v, w]
(Evaluate@pl \[Function] a*f)[r, s, t, u, v, w]
(Evaluate@pl \[Function] a*b/c)[r, s, t, u, v, w]

So far this works too an gives the sam result as above.

But there is a problem with the localization of the formal parameters. If I e.g. assign a value to the global Symbol a:

a=42;

I get an Error:

Function::flpar: Parameter specification {42,b,c,d,e,f} in Function[{42,b,c,d,e,f},a+b] should be a symbol or a list of symbols.

Any hints how to solve this problem? Have already tried various combinations of the usual suspects (Hold, Symbol, ...)

Kind Regards Robert

Ok, very close related to the first question I got a second one.

How can I get the SymbolName's of the Symbol's listed in pl? The need for this feature is e.g. to form a Table with TableForm and TableHeadings -> {None, pl}

a = 42;
pl := {a, b, c, d, e, f};

TableForm[{{1, 2, 3, 4, 5, 6}, {10, 20, 30, 40, 50, 60}}, TableHeadings -> {None, pl}]

5   b   c   d   e   f
1   2   3   4   5   6
10  20  30  40  50  60

As can bee seen the assignment to a spoils everything.

I have a little bit nasty solution via string matching:

OwnValues@pl // ToString // 
  StringCases[#, __~~"{"~~s__~~"}"~~__ :>StringSplit[s, ", "]] &//First

{"a","b","c","d","e","f"}

Can this be don more elegantly?

Robert Nowak
  • 861
  • 6
  • 12
  • 4
    Can you put this into context? What are you really trying to do? There may be better approaches. – Szabolcs Jun 15 '18 at 10:40
  • Well, I have tabular data (2D List), each column for some property. Each row for a set of this properties. I want to Map[Apply] various functions/evaluations for every row of this data. Not in one place but spread on different positions in a notebook. So all the functions share the same parameter list. If for some reason the layout of the data changes I would like to have on central parameter list for all the functions to be adjusted. – Robert Nowak Jun 15 '18 at 11:13
  • Why not use formal variables instead, e.g., \[FormalA], \[FormalB] etc instead of a, b etc – Carl Woll Jun 17 '18 at 13:28
  • @Carl Woll can you clarify your suggestion – Robert Nowak Jun 18 '18 at 14:18
  • Formal variables are protected, so you can’t give them values, and so you don’t have to worry about them having OwnValues – Carl Woll Jun 18 '18 at 14:33
  • @Carl Woll, thank you, but the names of formal variables are to restrictive, further I want to be able to write (read) the names as column headers to an ASCII file. – Robert Nowak Jun 19 '18 at 09:27
  • Sorry to be off topic: @RobertNowak, Do you happen to be the Robert Nowak who wrote a Mathematica export to GDSii? If so, I am hoping that I might be able the source to that. Kind Regards, Craig – Craig Carter Jan 07 '20 at 21:20

3 Answers3

4

Would it be acceptable for you?

a = d = 2 (*just to test*)

myFunction // ClearAll
myFunction // Attributes = {HoldAll};
myFunction[expr_] := Function @@ Unevaluated[{{a, b, c, d, e, f}, expr}]

You can use your function constructor anywhere:

myFunction[a + b] @@ {1, 2, 3, 4, 5, 6}

3

and if data structure changes, you can redefine it with e.g....[{{a, c, d, e, f, b}, expr}]

And now:

  myFunction[a + b] @@ {1, 2, 3, 4, 5, 6}

7

p.s. Function @@ is there for Enforcing correct variable bindings and avoiding renamings for conflicting variables in nested scoping constructs

Kuba
  • 136,707
  • 13
  • 279
  • 740
3
a = 42;
pl := {a, b, c, d, e, f};
SetAttributes[buildfunc, HoldAll]
buildfunc[body_] := (Unevaluated[#[pl, body]] /. OwnValues@pl) &@Function
(* Alternatively: *)
(*
buildfunc[body_] := Function @@ Append[Trace[pl][[2]], Unevaluated@body]
 *)

buildfunc[c + d]
pl := {a, e, f}
buildfunc[e - f]

Update

As you've noticed, the new problem is similar to the original, so it can be solved in a similar manner:

a = 42;
pl := {a, b, c, d, e, f};

ClearAll[nameonly] SetAttributes[nameonly, HoldAll]

nameonly[lst_] := HoldForm[lst] /. OwnValues@lst // Thread (* Alternatively: ) ( nameonly[lst_] := Trace[lst][[2]] // Thread *) TableForm[{{1, 2, 3, 4, 5, 6}, {10, 20, 30, 40, 50, 60}}, TableHeadings -> {None, nameonly@pl}]

If you need a list of string as the output, you can:

(* Solution 1 *)
nameonly@pl /. HoldForm[a_] :> SymbolName@Unevaluated@a
(* Solution 2 *)
Function[a, SymbolName@Unevaluated@a, HoldAll] @@@ nameonly@pl
xzczd
  • 65,995
  • 9
  • 163
  • 468
  • This is not a solution, because the Global a is destroyed, exactly this behavior should be avoided. The Symbol a should be kept local. – Robert Nowak Jun 15 '18 at 11:22
  • @RobertNowak a is not destroyed, I cleared a at beginning just to avoid error when this piece of code is executed repetitively. My solution will work, as long as pl is built before a owns a value. Or a is always defined before pl is defined in your actual code? If so, is modification for definiton of pl allowed? – xzczd Jun 15 '18 at 11:44
  • Hi your "solution" works/fails exactly the same way as my simpler "solution" if I add the Clear. Imagine a large complicated notebook, I just don't want to take care If there is a Symbol a used anywhere in the code, if it is possibly used somewhere it's value should not be touched (cleared, destroyed, modified, etc.). Modification of pl is ok if it helps for a solution, for now pl is a simple list of desired formal parameters. If turning pl into some more sophisticated structure holding the parameter list helps then this would be ok. – Robert Nowak Jun 15 '18 at 12:10
  • This is a little bit confusing if you edit your solution, you should post a new answer. – Robert Nowak Jun 15 '18 at 12:18
  • 1
    @RobertNowak As far as I can tell, if an answer doesn't meet the requirement of asker, then editing the body of answer to improve or correct it rather than posting a new answer is the proper thing to do. – xzczd Jun 15 '18 at 12:22
  • Ok, your actual "solution" takes only care of a, what's with b, c, d, e, f ? you would need Block[{{a, b, c, d, e, f}}, (Evaluate@pl [Function] a + b)[r, s, t, u, v, w]], Again this solution does not provide a central parameter list, if the parameter list would be needed to change to adjust for the data layout one would have to change all occurrences in all Block expressions. – Robert Nowak Jun 15 '18 at 12:23
  • 1
    @RobertNowak Again, check my edit. And, you should clarify all of these in the body of your question. – xzczd Jun 15 '18 at 12:36
  • than you so far. what if I need the Symol's as a List of Strings? nameonly[pl]//Map@Apply@ToStringg does not realy help me. Again a is getting spoiled. – Robert Nowak Jun 17 '18 at 13:30
  • thank you so much. Would the first question/problem have had a simpler solution if pl started as a List of String's. At least the second question/problem would have vanished :). – Robert Nowak Jun 18 '18 at 13:31
  • 1
    @RobertNowak I'm afraid the first problem won't be easier to solve, because you'll have to transform strings to symbols in some stage if you want to define function. And, I believe string will probably make things more troublesome. A solution I can think out at the moment: a = 42; pl := {"a", "b", "c", "d", "e", "f"}; ClearAll[buildfunc]; SetAttributes[buildfunc, HoldAll]; buildfunc[body_] := Function[Join @@ ToExpression[pl, InputForm, HoldPattern] // Evaluate, body] /. HoldPattern -> List // Quiet – xzczd Jun 18 '18 at 14:11
  • 1
    ok, managed it to get a little of a bit nicer version on the List of String's buildfunc[body_] := Hold[Function][ToExpression[pl, InputForm, Hold], Hold[body]] // ReleaseHold – Robert Nowak Jun 21 '18 at 15:04
3

Thanks @xzczd and @Kuba

Based on your suggestions I came up with two (three) versions, the second one with an individual parameter list which in turn is passed as a parameter:

a = 42;
pl := {a, b, c, d, e, f};
gl := {a, b, d, c, e, f};

Attributes[buildfunc] = {HoldAll};
buildfunc[body_] := (F \[Function] F[pl, body] /. OwnValues@pl)@Function
buildfunc[body_, 
   pl_Symbol] := (F \[Function] F[pl, body] /. OwnValues@pl)@Function
buildfunc[body_, pl_List] := (F \[Function] F[pl, body])@Function

buildfunc[c - d]
buildfunc[c - d, gl]
buildfunc[c - d, {a, b, d, c, e, f}]

buildfunc[c - d][r, s, t, u, v, w]
buildfunc[c - d, gl][r, s, t, u, v, w]
buildfunc[c - l, {a, b, f, c, e, l}][r, s, t, u, v, w]

gives:

Function[{a, b, c, d, e, f}, c - d]
Function[{a, b, d, c, e, f}, c - d]
Function[{a, b, d, c, e, f}, c - d]

t - u
-t + u
u - w

Seems to work pretty well. Note the last usage with direct feed of a parameter list actually counteracts the original intention

Robert Nowak
  • 861
  • 6
  • 12