3

I have a function taking a function as input as well as other values, e.g.

f2[f_, a_, b_, c_, d_] = f[{a, b}, c, d];

Of course, the behavior differs for f1 being Listable or not. So if I define

f1[x_, y_, z_] = If[x > 0, x*y + z, -x + y + z]

it will yield

In= Attributes[f1] = {};f2[f1, 1, 2, 3, 4]
Out= If[{1, 2} > 0, {1, 2} 3 + 4, -{1, 2} + 3 + 4]

in contrast to

In= SetAttributes[f1, Listable]; f2[f1, 1, 2, 3, 4]
Out= {7, 10}

.

Now my question is: can I implement that f2 will always treat the given input function as Listable? So that I don't have to explicitely give the Attribute to the input function beforehand and still get the listable-ish output (in this case {7,10}.

I was trying

f3[f_, a_, b_, c_, d_] = Assuming[Element[Listable, Attributes[f]], f[{a, b}, c, d]];

but this behaves exactly like f2.

(Of course, in this simple case, I could map over the list {a,b} but let's assume this is not possible or not convenient.)

riddleculous
  • 393
  • 1
  • 9
  • 2
    Why not use Thread? Listable is just an automatic Thread. f3[f_, a_, b_, c_, d_] = Thread[f[{a, b}, c, d]]; – vapor Mar 10 '17 at 14:48
  • Right, this works, but I didn't come up with it because Thread didn't work in a similar though slightly different context – riddleculous Mar 10 '17 at 14:53
  • 1
    Can you tell me in what cases Thread does not work? – vapor Mar 10 '17 at 16:17
  • What do we know about a, b, c, d arguments? Can they be lists themselves? Do you want f function to thread them simultaneously with list you create in f2 body? Explicit mapping is safest in this respect, since it won't accidentally thread where you don't want it. – jkuczm Mar 11 '17 at 09:21
  • If performance is relevant then How can one write a robust ListableQ function? might be related. – jkuczm Mar 11 '17 at 09:35
  • @happyfish I had too many mapping constructs on one single pure function (defined with #1, #2, etc) and it simply got too complicated to do the mapping in the right order. – riddleculous Mar 11 '17 at 09:44
  • @jkuczm as mentioned below, a/b are strings, the other arguments are just real numbers, so no lists or so. {a,b} is actuall explicitely given in the definition of f2as {"T","S"}. – riddleculous Mar 11 '17 at 09:49
  • @jkuczm Thanks for the link, but i don't get the connection. For my functions I know that in 99% of the cases they are not directly listable, but in 100% of the cases it works when giving them the attribute. – riddleculous Mar 11 '17 at 09:50
  • It seems ListableQ is not useful in your use case. It would be if you'd have large numeric lists over which you'd like listable function to map, then adding top level Listable attribute could spoil performance of built-in listable functions. – jkuczm Mar 11 '17 at 15:22

1 Answers1

3

If you don't want to modify f1 globally, then you can do it locally:

ClearAll[f1, f2listable];
f1[x_, y_, z_] := If[x > 0, x*y + z, -x + y + z]
f2listable[f_, a_, b_, c_, d_] := 
 Module[{g}, g[x_, y_, z_] := f[x, y, z]; SetAttributes[g, Listable]; g[{a, b}, c, d]]

f2listable[f1, 1, 2, 3, 4]

{7, 10}

Stitch
  • 4,205
  • 1
  • 12
  • 28
  • Ah right, of course Module works nicely. However, other solutions would be interesting, too (if they exist) – riddleculous Mar 10 '17 at 14:42
  • @riddleculous You mentioned in the OP you are not willing to use any of the functions that apply functions over lists. What is the reason for this? – Stitch Mar 10 '17 at 14:52
  • 3
    For modifying attributes/general function behavior, I use Internal`InheritedBlock which acts like Block except the original behavior is still present, i.e. you can modify the existing behavior, temporarily. So, I would use f2listable[...] := Internal`InheritedBlock[{f1}, SetAttributes[f1, Listable]; f1[...]]. – rcollyer Mar 10 '17 at 14:53
  • @rcollyer that is a very nice solution! – Stitch Mar 10 '17 at 14:54
  • @Stitch because in my case, it's more complicated and I use Apply for other arguments of f1 which makes the use of Map impossible or at least very confusing here – riddleculous Mar 10 '17 at 14:55
  • @rcollyer nice, i like the brevity! Though I can't assess whether to prefer the Moduleor the InternalInheritedBlock` approach – riddleculous Mar 10 '17 at 15:03
  • @riddleculous There could be a potential for other functions that combine functionality, but without knowing other details, it seems like this or the rcollyer solution might be a way to go. – Stitch Mar 10 '17 at 15:05
  • @Stitch both approaches work and execute in almost the same time in my case, so I'm happy with both of them – riddleculous Mar 10 '17 at 15:07
  • @riddleculous brevity without obscurity is a nice attribute for code. An advantage of Module is it let's you create a unique closure around a symbol. For instance, I've in code injection where I need to wrap my code around multiple functions, but need a unique control variable for each. :) – rcollyer Mar 10 '17 at 16:05
  • @riddleculous What do we know about functions that will be passed as first argument of f2? If it can be an anonymous function, or symbol that modifies itself in any way that should be persistent, or recurrent function for which listability can break any of internal calls, then InheritedBlock is ruled out. Stitch's version is safer in this respect since it uses lexical scoping, personally id use Function[, f@##, Listable][{a, b}, c, d] or With[{fListable = Function[, f@##, Listable]}, fListable[{a, b}, c, d]] it it needs to be called more than once. – jkuczm Mar 11 '17 at 09:15
  • @riddleculous Can this function have any relevant Hold... attributes, if so then lexical scoping based solution would require slightly more work, than InheritedBlock, to take this into account. – jkuczm Mar 11 '17 at 09:26
  • @jkuczm for practical reasons, the function f1 is defined directly when calling f2[f1,a,b,c,d] and looks like f1=f3[#1,#2,#3,d]&, i.e. I would like to call f2[f3[#1,#2,#3,d]&,a,b,c,d]. Here, f3 is a list of simple matrices and it depends on the first argument (a string) how it will look like, i.e. there are two definitions f3]"a",x_,y_]:=… and f3]"b",x_,y_]:=… (to make it more complicated, f3 doesn't distinguish itself between "a" and "b" but the functions called by f3 do.) – riddleculous Mar 11 '17 at 09:38
  • @riddleculous So Stitch's answer seems to be the way to go. I'd prefer pure Function and With if necessary, as in my previous comment, but difference between those approaches is more stylistic than functional. – jkuczm Mar 11 '17 at 15:27