8
$\begingroup$

Consider the following approach to modifying a list:

updateList01[lst_List, test_] :=
 Module[{xs = lst}, If[test, xs[[1]] += 1]; xs]
lst = {1, 2, 3}
lst = updateList[lst, True]

The good: all arguments are passed explicitly. The bad: xs and lst are apparently both entirely in memory(?). If that is right, what is the Mathematica idiom to avoid building a (potentially large) second list? (Assume there are many lists that needed to be updated and that actual updating is more complex.) Looking at HoldFirst, it sounds like I might avoid a copy by setting HoldFirst. That is also how I am inclined to read How to modify function argument?. Is that a correct interpretation? But even if one can avoid a copy this way, I'm clearly failing to understand something. The following fails:

SetAttributes[updateList02, HoldFirst]
updateList02[lst_List, test_] := If[test, lst[[1]] += 1]
lst02 = {1, 2, 3}
updateList02[lst02, True]  (* Not evaluated? *)
lst02   (* Unchanged *)

How do I fix it?

$\endgroup$
  • 3
    $\begingroup$ I'm not sure what the problem is yet, but it seems like the Hold attribute is preventing MMA from noticing that lst02 is a List, so you're head specification isn't matched, which is why it's not evaluating. You'll also run into a problem because you actually want to modify what is located in lst02, try defining updateList02[lst_Symbol,test_]:=...what you have. $\endgroup$ – N.J.Evans Nov 3 '15 at 16:29
  • $\begingroup$ I guess the OP wants the updating function to remain pure so it can be applied to any lists, but also doesn't want the list to be duplicated inside the function. In other words, I guess he wants to pass just something like the pointer to a global list into the function à la C. $\endgroup$ – Taiki Nov 3 '15 at 16:33
  • 1
    $\begingroup$ @N.J.Evans Yeah I know. I just wanted to point out what the problem was, despite that your comment is already a fix to it. :) $\endgroup$ – Taiki Nov 3 '15 at 16:49
  • 2
    $\begingroup$ @N.J.Evans, Head doesn't hold its argument so the ReleaseHold isn't required. $\endgroup$ – Simon Woods Nov 3 '15 at 16:51
  • 4
    $\begingroup$ Mathematica is smart and it doesn't store duplicate arrays. Even explicit lst03=lst02 is lazy. However there is one important (and weird) thing: set $HistoryLength = 0 before dealing with big arrays! $\endgroup$ – ybeltukov Nov 3 '15 at 17:16
9
$\begingroup$

You've actually got two problems. The first is the fact that the function isn't evaluating. This happens because you pass lst02 to your function, which is a Symbol not, a List, so the pattern doesn't match your definition. You can see this by doing:

SetAttributes[updateList02, HoldFirst]
updateList02[lst_List, test_] := If[test, lst[[1]] += 1]
lst02 = {1, 2, 3}
updateList02[Evaluate@lst02, True]  
lst02   (* still unchanged *)

This evaluates, but not in the way you wanted it to.

Since you're trying to change the stuff referred to as lst02, write your function to accept the Symbol referring to the stuff you're trying to modify. But since you lost your original pattern match you'll need to make sure you only evaluate the function for lists, which you can do with a conditional:

ClearAll[updateList02];
SetAttributes[updateList02, HoldFirst]
updateList02[lst_Symbol, test_] /; Head@lst == List := 
 If[test, lst[[1]] += 1];

Now doing

lst02={1,2,3};
updateList02[lst02,True];
lst02

gives {2,2,3}

You can also use the conditional to add other safety measures, make sure your list is long enough etc.

$\endgroup$
  • 4
    $\begingroup$ The semi-documented ListQ can be used tersely with PatternTest: updateList02[lst_Symbol?ListQ, test_] := . . . $\endgroup$ – Mr.Wizard Nov 3 '15 at 19:00

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.