5

OK, I think this problem is more close to philosophy. :P

I've some idea now. But I'm not sure whether it's correct. For the purpose of convenience, I will use some C-like code instead of Mathematica's.

A While loop is something like:

i=0; // some initialize of variable i
While(test of variable i)   // such like i<10 or i^3<90, etc.
{
   // involving doing something which has nothing to do with i (e.g. Print["*"])
   // or involving taking i as an argument of another function, such like Sin[i]
   // or involving re-assign some value to i, such like i=i+7
}
Print[i] // after doing such evaluations, we want to get the final value of i

The first and second line surrounded in while is not really a problem for NestWhile. It is just because NestWhile is NestWhile[f, expr, test], one can write (Print["*"]; Sin[#] + 1 &) in the place of f, doing the same thing you want when you're dealing with While.

However, when you want to re-assign expr in NestWhile, No, you cannot. i = 0; NestWhile[# = # + 1 &, i, # < 70 &] is not what you want. (The i is immediately change into 0 before you would want to do i = i+1)

And just because of this simple difference between While and NestWhile, I doubt that EVERY While-code can change into NestWhile-code.

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
Eric
  • 1,191
  • 7
  • 20

2 Answers2

9

Taking the question at face value, I think the answer is yes. A While loop

While[test, body]

evaluates test then body until test fails to give True. This can be implemented in NestWhile by putting test and body into Functions:

NestWhile[body &, Null, test &]

For example the following both do the same thing:

i = 0; While[i < 10, i++];
i = 0; NestWhile[(i++) &, Null, (i < 10) &];

I don't think this is what you are after though. In the question you describe a restricted subset of While loops in which body consists of "doing stuff with some variable i" and test consists of "a function of i". The question, I think, is whether such While loops can be converted to NestWhile with the variable i used as the expr (i.e. second argument) in NestList.

As you pointed out, this is not easily done. Here is a possible approach using Hold to prevent unwanted evaluations:

i = 0;
NestWhile[((Increment @@ #); #) &, Hold[i], ReleaseHold[#] < 10 &]

This is pretty horrible. Not only do we have to jump through hoops to evaluate i++ without prematurely evaluating i, the iterated function now takes Hold[i] as input and returns the same Hold[i] as output. The whole principle of functional iteration has been murdered just to allow us to (awkwardly) manipulate the global symbol i as a side effect.

In summary the answer is yes, you can change every usage of While into NestWhile, but you shouldn't. NestWhile's purpose is to implement functional iteration - don't think of it as a special case of While, think of it as a special case of Nest.

Simon Woods
  • 84,945
  • 8
  • 175
  • 324
2

One approach to change While to NestWhile is to incorporate variable increments, such as i++, as part the repeated function application. The idea of incorporating increments is similar to what Simon Woods proposed.

You can go a step further and roll the initialisation into Nestwhile as well--closer in spirit perhaps to a functional version.

Example for Fibonacci numbers:

The function fib generates the k-th fibonacci number:

fib[k_] := Nest[{#[[2]], #[[1]] + #[[2]]} &, {0, 1}, k][[2]]

The following While-code produces the list of fibonacci numbers valued strictly below 10:

n = 0;
fibList = {};
While[fib[n] < 10, fibList = Join[fibList, {fib[n]}]; n++]
fibList

output: {1, 1, 2, 3, 5, 8}

Compare this with a NestWhile implementation producing the same output:

NestWhile[{Join[#[[1]], {fib[#[[2]]]}], #[[2]] + 1} &, {{}, 0}, 
  fib[#[[2]]] < 10 &][[1]]

The initialisation value is {{ },0}

The nested function is Join[#[[1]], {fib[#[[2]]]}], #[[2]] + 1} &

ExpressionCoder
  • 1,678
  • 7
  • 15
  • 2
    (+1) I would describe the conversion from While[] to NestWhile[] in terms "state variables," but that's to do with my background which is not CS: NestWhile[ Apply@Function[ statevariables, body ], initialvalues, Apply@Function[ statevariables, test ]] // First. For instance, in your example statevariables becomes {fibList, n} and body becomes {Join[fibList, {fib[n]}], n + 1}; and test becomes fib[n] < 10. One could also do a more direct translation like this: Block[ statevariables, statevariables = #; whilebody; statevariables ] & – Michael E2 Sep 11 '22 at 13:39