Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Select turn my while loop into an infinite loop?

Tags:

c#

linq

At work I ran into a strange problem, where a loop I expected to terminate was actually running indefinitely.

I traced the problem back to a use of Select. Interestingly, the loop terminated as expected when I added a .ToList() right after the Select. I boiled it down to a small example.

class WrappedBool
{
    public WrappedBool(bool inner)
    {
        InnerBool = inner;
    }
    public bool InnerBool { get; set; } = false;
}

// remove .ToList() here and the following loop will go infinite
IEnumerable<WrappedBool> enumerable = 
   new List<bool>() { false, true, false }
  .Select(b => new WrappedBool(b))
  .ToList();

while (enumerable.Any(wb => !wb.InnerBool))
{
    WrappedBool firstFalse = enumerable.Where(wb => !wb.InnerBool).First();
    firstFalse.InnerBool = true;
}

While I don't have to deal with my code not terminating anymore, I still wonder where this behaviour is coming from in the first place.

like image 914
Pseudoradius Avatar asked Aug 05 '19 11:08

Pseudoradius


People also ask

Why is my while loop running infinitely?

Basically, the infinite loop happens when the condition in the while loop always evaluates to true. This can happen when the variables within the loop aren't updated correctly, or aren't updated at all. Let's say you have a variable that's set to 10 and you want to loop while the value is less than 100.

How do I stop a while loop running infinitely?

An infinite loop is a loop that runs indefinitely and it only stops with external intervention or when a break statement is found. You can stop an infinite loop with CTRL + C . You can generate an infinite loop intentionally with while True . The break statement can be used to stop a while loop immediately.

Why is my while loop infinite Python?

A loop becomes infinite loop if a condition never becomes FALSE. You must use caution when using while loops because of the possibility that this condition never resolves to a FALSE value. This results in a loop that never ends. Such a loop is called an infinite loop.


1 Answers

Well, without materialization (.ToList()) enumerable is just a query

IEnumerable<WrappedBool> enumerable = 
   new List<bool>() { false, true, false }
  .Select(b => new WrappedBool(b));

whenever you call it, it creates a new instance of List<bool>() {false, true, false} where you have false items to iterate on

// new List<bool>() { false, true, false } - do we have any false item here?
// Yes - keep on looping (forever)
while (enumerable.Any(wb => !wb.InnerBool)) 
{
    // get 1st false from new List<bool>() { false, true, false }
    WrappedBool firstFalse = enumerable.Where(wb => !wb.InnerBool).First();
    // turn it into true and discard
    firstFalse.InnerBool = true;
}

On the contrary

IEnumerable<WrappedBool> enumerable = 
   new List<bool>() { false, true, false }
  .Select(b => new WrappedBool(b))
  .ToList(); // create a List; call new List<bool>() { false, true, false } just once

is materialized so enumerable is List<T> which is created once and in which you modify 1st and 3d items:

// does enumerable collection (List) have any false item? 
while (enumerable.Any(wb => !wb.InnerBool)) 
{
    // get 1st false from enumerable List
    WrappedBool firstFalse = enumerable.Where(wb => !wb.InnerBool).First();
    // turn it into true 
    firstFalse.InnerBool = true;
}
like image 186
Dmitry Bychenko Avatar answered Oct 16 '22 20:10

Dmitry Bychenko