5
$\begingroup$

I am trying to code a while loop that uses AppendTo to append the loop variable to an empty list.

even = {}
i = 1; While[i <= 5, Print[i]; AppendTo[even, i]; i++]

This code does not give any output. Can someone please tell what I am doing wrong? Thanks!

$\endgroup$
  • 3
    $\begingroup$ The code is not intended to give an output. It alters the symbol even five times, then even contains the (desired?) output - all numbers, not only the even ones. $\endgroup$ – mgamer Jul 22 '19 at 15:40
  • 7
    $\begingroup$ It should be noted that the AppendTo function isn't ideal for loops. The documentation explains in more detail. $\endgroup$ – BioPhysicist Jul 23 '19 at 14:53
  • 3
    $\begingroup$ Can you explain why you expect this piece of code to return a value (an output) at all? While does not return anything. If it did, what would be a reasonable thing for it to return? $\endgroup$ – Szabolcs Jul 24 '19 at 8:44
5
$\begingroup$

Try

even = {} ; 
i = 1; 
While[i <= 5, Print[i];If[EvenQ[i], AppendTo[even, i]]; i++]

even
(*{2, 4}*)
| improve this answer | |
$\endgroup$
4
$\begingroup$

The standard Table function also supports multi-line inputs, the last line will be used to create the table

Table[Print[i]; i, {i, 5}]

The output would look like this:

even = Table[Print[i]; i, {i, 5}]

1
2
3
4
5
{1, 2, 3, 4, 5}

But the value stored for even is just:

{1, 2, 3, 4, 5}
| improve this answer | |
$\endgroup$
4
$\begingroup$

Linked lists (see Performance tuning in Mathematica?) are another, efficient alternative.

even = {}
i = 1; While[i <= 5, even = {even, i}; i++]

Flatten[even]

(*  { 1, 2, 3, 4, 5 }  *)

(I don't know why people use Print[] except for debugging. I suppose it's a C thing. To monitor progress, you can use Monitor[] or my current favorite, PrintTemporary@Dynamic@{Clock[Infinity], i}. To output data, there are better ways than Print[].)

| improve this answer | |
$\endgroup$
4
$\begingroup$

Sow/Reap is more appropriate than AppendTo in the context of step-by-step list building: the latter takes longer as your list gets longer, whereas the former keeps up its speed until the end.

i = 1;
even = Reap[While[i <= 5, Print[i]; Sow[i]; i++]][[2, 1]]
(*    1    *)
(*    2    *)
(*    3    *)
(*    4    *)
(*    5    *)
(*    {1, 2, 3, 4, 5}    *)

Of course, this is only useful if you do something more complex in the loop. For this particular case, Table is even better, as user2757771 points out.

| improve this answer | |
$\endgroup$
3
$\begingroup$

Just to go overkill on Michael E2's point that Print is pretty much useless except in a debug environment, here's a little automated Monitor:

monitored~SetAttributes~HoldAll;
monitored[
  monitoredVars : {__Symbol} | Automatic : Automatic,
  expr_,
  ignoredContexts : StringPattern`StringPatternQ : "System`"
  ] :=
 Replace[
  If[Hold[monitoredVars] === Hold[Automatic],
   Thread[
    DeleteDuplicates@
     Cases[Hold[expr], 
      s_Symbol?(Function[Null, ! StringMatchQ[Context[#], ignoredContexts], 
          HoldFirst]) :>
       Hold[s],
      Infinity
      ],
    Hold
    ],
   Hold[monitoredVars]
   ],
  Hold[mvars_] :>
   (
    PrintTemporary@
     Internal`LoadingPanel@Dynamic[
      Grid[
       {
        {"Time:", NumberForm[Quantity[Clock[Infinity], "Seconds"], 3]},
        {"Vars:", 
         Grid[
          {Row@{Extract[#, 1, Defer], ":"}, ReleaseHold[#]} & /@ 
           Thread[Hold[mvars]],
          Alignment -> {Left, Top}
          ]
         }
        },
       Alignment -> Left
       ]
      ];
    expr
    )
  ]

Then we can watch what happens (the Pause is just there to be able to see it happen):

monitored[
 even = {};
 n = 50;
 i = 1; While[i <= n, Pause[.1]; even = {even, i}; i++];
 Flatten[even]
 ]

{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, \
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, \
41, 42, 43, 44, 45, 46, 47, 48, 49, 50}

enter image description here

We can also just watch i:

monitored[
 {i},
 even = {};
 n = 25;
 i = 1; While[i <= n, Pause[.1]; even = {even, i}; i++];
 Flatten[even]
 ]

{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, \
22, 23, 24, 25}
| improve this answer | |
$\endgroup$
0
$\begingroup$

If you wanted to Print the list at the end of the While loop, you can do the following

Module[{even = List[], i},
 i = 1;
 While[i <= 5, 
  Print["Now appending " <> ToString[i] <> " to the list"]; 
  AppendTo[even, i]; i++];
 even
 ]
| improve this answer | |
$\endgroup$
0
$\begingroup$

Just for fun, going full functional, and making use of the magic Nothing symbol:

feven[n_?EvenQ] := n
feven[_] = Nothing;
Map[feven, Range[5]]
(* {2, 4} *)

If you must see the input, you can look at what Range[5] yields.

This kind of approach helps speed things up if the function you're mapping to the input is complicated and expensive. In that case, use ParallelMap. Factoring the problem into simple functions you can try out before composing higher level (but still simple) expressions from them also avoids much difficult debugging.

| improve this answer | |
$\endgroup$

Not the answer you're looking for? Browse other questions tagged or ask your own question.