11

An example will be most specific:

func[list_, column_] := list[[All, column]] = Map[#*2 &, list[[All, column]]];

This throws errors.

I want to avoid doing something like this:

func2[list_] := Map[#*2 &; list];
list[[All, 2]] = func2[list[[All,2]]]

because nesting a couple of functions raises complexity unnecessarily, the output would have to be reassigned every time.

Thanks in advance.

As a followup, using HoldFirst works fine, but using the so defined function in a Map gives again errors.

The setup is as follows:

create a nested list

testList = Table[Table[{x y, x y 2}, {x, 1,3}], {y,1,3}]

define afunc with HoldFirst Attribute

afunc = Function[{list, col}, list[[All, col]] = Map[# * 2 &, list[[All, col]]], HoldFirst]

and another function using the first

bfunc[nestedList_, col_] := Map[afunc[#, col] &, nestedList]

now, a call to

bfunc[testList, 2]

should alter the 2'nd columns of the nested lists I'd expect, but it instead throws errors

i've tried to set Attribute HoldFirst on this function as well but it didn't work out as expected

Leonid Shifrin
  • 114,335
  • 15
  • 329
  • 420
phil
  • 113
  • 5
  • 2
    Well, with your follow-up, you really opened a can of worms. Without a very detailed discussion, here is a working version of your bfunc: ClearAll[bfunc]; SetAttributes[bfunc, HoldFirst]; bfunc[nestedList_, col_] := Table[With[{i = i}, afunc[nestedList[[i]], col]], {i, Length[nestedList]}]. Note that 1: You should have used patterns and HoldFirst for bfunc 2:the construction f[#]& is not the same as f,since it leaks evaluation during the parameter-passing in a pure function 3. Map is a wrong tool for the task, since there is no way with it to prevent evaluation of sub-parts. – Leonid Shifrin Mar 28 '12 at 11:34
  • I want to add that generally, programming with the pass-by-reference style is a bit going against the idiomatic Mathematica, and so you should get a really good understanding of evaluation process if you don't want nasty surprises on every mile of this road. – Leonid Shifrin Mar 28 '12 at 11:38
  • Thanks for your comments. I'm having a quite strong background in OOP and procedural programming languages, the functional approach is making me troubles though, but I think I'm not alone. – phil Mar 28 '12 at 11:48
  • Note also that Mathematica is not really a functional language at its core - it emulates functional paradigm (quite well), but its core is term-rewriting. Many of the issues here are specific to Mathematica, for example quite complex evaluation control, or patterns. If you are interested in FP in Mathematica specifically, you may check out my book, which is heavily centered around an FP paradigm. It does not however explain evaluation in great detail. – Leonid Shifrin Mar 28 '12 at 11:52
  • @Leonid, what do you mean by "... there is no way with it to prevent evaluation of sub-parts"? – Mr.Wizard Mar 28 '12 at 11:57
  • @Mr.Wizard I mean that if you have a list stored in some variable (say, lst), and you have a function which sets some expression to something (like Set), I don't see any way that you can use Map and this function to separately reset parts of lst to new values. This is why I was using Table with index injection. And the only reason that worked is that Part is a lower-level function, and in particularly you can assign sub-parts directly. – Leonid Shifrin Mar 28 '12 at 12:01

1 Answers1

12

You basically need a pass-by-reference semantics, which in Mathematica can be emulated with Hold-attributes. Add a HoldFirst attribute to your function:

SetAttributes[func,HoldFirst]

and you should be fine. Without it, list evaluates to its value before the assignment is attempted, and since expressions in Mathematica are immutable, you get an error.

To address your question in comments, the one-liner you asked for can be this:

func = Function[{list,column}, 
         list[[All, column]] = Map[#*2 &, list[[All, column]]],
         HoldFirst
       ]

Note however that, since this is a pure function, you can not do argument checks as elegantly as you can with patterns, and you can not overload your functions on different arguments as elegantly.

Note also that, while yet another way to do this is to keep your function as it is but rather wrap the first argument in Unevaluated in every function call, I would strongly advise against that. The reason is that it makes the function itself not self-contained, because it has to assume that the user will always remember to use Unevaluated (which it shouldn't), and there is no way to test whether or not the user actually did use it.

Leonid Shifrin
  • 114,335
  • 15
  • 329
  • 420