5
$\begingroup$

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?

$\endgroup$
  • 4
    $\begingroup$ Can you put this into context? What are you really trying to do? There may be better approaches. $\endgroup$ – Szabolcs Jun 15 '18 at 10:40
  • $\begingroup$ 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. $\endgroup$ – Robert Nowak Jun 15 '18 at 11:13
  • $\begingroup$ Why not use formal variables instead, e.g., \[FormalA], \[FormalB] etc instead of a, b etc $\endgroup$ – Carl Woll Jun 17 '18 at 13:28
  • $\begingroup$ @Carl Woll can you clarify your suggestion $\endgroup$ – Robert Nowak Jun 18 '18 at 14:18
  • $\begingroup$ Formal variables are protected, so you can’t give them values, and so you don’t have to worry about them having OwnValues $\endgroup$ – Carl Woll Jun 18 '18 at 14:33
4
$\begingroup$

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

$\endgroup$
  • 3
    $\begingroup$ You could also use Hold instead of Unevaluated for similar effect, I think $\endgroup$ – b3m2a1 Jun 17 '18 at 0:47
  • $\begingroup$ @b3m2a1 yes, this time it was an overreaction $\endgroup$ – Kuba Jun 17 '18 at 8:23
  • $\begingroup$ I’m not sure it was an overreaction :) but perhaps a bit more complicated than you needed $\endgroup$ – b3m2a1 Jun 17 '18 at 8:33
3
$\begingroup$

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

$\endgroup$
3
$\begingroup$
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
$\endgroup$
  • $\begingroup$ This is not a solution, because the Global a is destroyed, exactly this behavior should be avoided. The Symbol a should be kept local. $\endgroup$ – Robert Nowak Jun 15 '18 at 11:22
  • 1
    $\begingroup$ @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. $\endgroup$ – xzczd Jun 15 '18 at 12:22
  • 1
    $\begingroup$ @RobertNowak Again, check my edit. And, you should clarify all of these in the body of your question. $\endgroup$ – xzczd Jun 15 '18 at 12:36
  • 1
    $\begingroup$ @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 $\endgroup$ – xzczd Jun 18 '18 at 14:11
  • 1
    $\begingroup$ 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 $\endgroup$ – Robert Nowak Jun 21 '18 at 15:04

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.