3
$\begingroup$

The following is just an example that captures what I want to do (more on it at the bottom).

I'm trying to do something that can be paraphrased as follows: I have a global list, say list = {0,0,1,0,0,1,1,1,1}. I have a function which can modify the global list:

f[element_] := (If[element == 1, list = Select[list, # != 1 &]]; counter++)

My problem is that when I map the function on the list, it goes through all the elements of the initial version of it, without skipping the values that have been eliminated:

counter = 0;
f /@ list;
counter
(* 9 *)

How do I get around this problem? (I'm ok with making the list locally defined)

EDIT: From the comments I gather that it might be useful to explain what I actually want to do. Well, here it is. I have a quantum system which is transformed and it may incur in errors: my list is a list of possible errors. My function looks at the errors and determines if they are correctable or not. To save time, if an error $E$ is not correctable I want to skip those errors in the list that come from composing $E$ with others.

$\endgroup$
  • $\begingroup$ Is your goal to compute counter...? When you first hit an element that is 1 all the other 1s will not be selected, so what do you need the modified list for? $\endgroup$ – Marius Ladegård Meyer Feb 9 '16 at 22:03
  • $\begingroup$ No, this is only an example. My function does much else on other objects in the meantime. My actual list is a list of possible errors on a quantum system and my function checks if they are correctable. If one is not correctable I want it to skip checking all the ones that "include it" because they must be uncorrectable as well. $\endgroup$ – Ziofil Feb 9 '16 at 22:12
  • 3
    $\begingroup$ Seems like your better off using Select on list rather than reassigning it lots of times inside f. $\endgroup$ – evanb Feb 9 '16 at 22:15
  • $\begingroup$ I would advise (not only in Mathematica) to avoid mutating state at all cost. As others have suggested better use Select and leave the original list intact or use Sow and Reap to build a list of results if you really have to do things iteratively. $\endgroup$ – Sascha Feb 9 '16 at 22:23
  • 1
    $\begingroup$ So, basically, you want to stop after first 1? $\endgroup$ – Kuba Feb 10 '16 at 7:16
3
$\begingroup$

I think you are misunderstanding the nature of Map. It applies a head to a series of elements, then evaluates the complete expression. Please see my answer to Scan vs. Map vs. Apply for examples and further description.

What you want might be handled by redefining f rather than redefining list.

ClearAll[f, list, counter]

f[element_] := (If[element == 1, f[1] = "skip"]; counter++)

list = {0, 0, 1, 0, 0, 1, 1, 1, 1};

counter = 0;
f /@ list
counter
{0, 1, 2, 3, 4, "skip", "skip", "skip", "skip"}

5

After the first instance of f[1] is evaluated a new definition f[1] = "skip" is added to the rule table. Further evaluations of f[1] trigger that rule instead (because it is more specific) and do not increase the counter. The new rule could also be pattern based, e.g.:

ClearAll[f, list, counter]

f[element_] := (If[element == 1, f[x_ /; x == 1] = x]; counter++)

list = {0, 0, 1, 0, 0, 1, 1.0, 1, 1};

counter = 0;
f /@ list
{0, 1, 2, 3, 4, 1, 1., 1, 1}

This returns unmodified any elements which match the Condition x == 1, after that rule is initially set by evaluation of the third element of the list.

$\endgroup$
2
$\begingroup$

You may use Scan and Return.

Return from f after you reset the list.

f[element_] := (If[element == 1, list = Select[list, # != 1 &]; 
   Return[]]; counter++;)

Then Scan the function with the Return .

Scan[f, list]

counter
(* 4 *)

Hope this helps.

$\endgroup$
  • $\begingroup$ What do you modify list for? $\endgroup$ – Berg Feb 9 '16 at 22:25
  • $\begingroup$ @Berg Using the OP's example function. $\endgroup$ – Edmund Feb 9 '16 at 22:27
  • $\begingroup$ Wait, it should evaluate 4 times, no? There are 4 zeros. $\endgroup$ – Ziofil Feb 9 '16 at 22:47
  • $\begingroup$ Okay. One second. $\endgroup$ – Edmund Feb 9 '16 at 22:54
  • $\begingroup$ What if I have the function f return the new list and I use NestWhile on it with the condition that list not be empty? As in f[list_]:=Module[{el=First@list},If[el == 1, Return[ Select[list, # != 1 &]]]; counter++] and then use NestWhile[f,initialList,Length@#>0&] $\endgroup$ – Ziofil Feb 9 '16 at 23:04
1
$\begingroup$

A solution is to manage the loop yourself, i.e.

list = {0, 0, 1, 0, 0, 1, 1, 1, 1};

p = MapIndexed[Prepend[#2, #1] &, list];

f[{element_}] := (If[First[element] == 1, p = Select[p, First[#] != 1 &]]; counter++)

f[{}] := counter++

counter = 0;

Array[f[Cases[p, {_, #}]] &, Length[p]];

counter

9

list = Map[First, p]

{0, 0, 0, 0}

$\endgroup$

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.