-6

I find myself doing this:

string states = "this,that,theother";

foreach(string state in states.Split(','))
{

}

And I wonder; Is the states string being split on every foreach loop?

This example is in c# but do other programming languages behave differently?

Do PHP and JavaScript split on each foreach loop?

PHP : Does explode occur on each loop?

$states = "this,that,theother";

foreach(explode(',', $states) as $state)
{

}

This questions isn't a duplicate as I am asking about more than just c# which is solely the language that the "duplicate question" refers to. All these anonymous down-votes will be the death of Stack Overflow.

iambriansreed
  • 21,935
  • 6
  • 63
  • 79

3 Answers3

1

No, the split happens once.

states.Split(',') returns an array. Array's in .NET implement IEnumerable

In general .NET collections are either vectors, arrays or other collections implementing IEnumerable or providing a GetEnumerator() method that returns an enumerator object with a property Current and a method MoveNext(). In some cases, the compiler will generate code to use GetEnumerator(), in other cases it will emit simple vector instructions using ldelem.ref, in other words, converting the foreach to a for loop.

At the start of the foreach() statement, the topic of the iteration, states.Split(), will be evaluated exactly once. In C#, at compile time it is decided what sort of container we are iterating, and choose a strategy. The compiler generates code to return the array (or other enumerable result) into a temporary variable, then the loop proceeds to access the N-th item from array one by one. Once the scope is destroyed, the "temp" container is garbage collected.

Now the compiler doesn't always use IEnumerator. It may convert a foreach() into a for() loop.

Consider:

string states = "1,2,3";
foreach (var state in states.Split(','))
{
    Console.WriteLine(state);
}

Sample MSIL:

IL_0017:  ldloc.s    CS$0$0000
IL_0019:  callvirt   instance string[] [mscorlib]System.String::Split(char[]) // happens once
IL_001e:  stloc.s    CS$6$0001    // <--- Here is where the temp array is stored, in CS$6$0001
IL_0020:  ldc.i4.0
IL_0021:  stloc.s    CS$7$0002    // load 0 into index
IL_0023:  br.s       IL_003a

IL_0025:  ldloc.s    CS$6$0001    // REPEAT - This is the top of the loop, note the Split is above this
IL_0027:  ldloc.s    CS$7$0002    // index iterator (like (for(int i = 0; i < array.Length; i++)
IL_0029:  ldelem.ref              // load the i-th element
IL_002a:  stloc.1
IL_002b:  nop
IL_002c:  ldloc.1
IL_002d:  call       void [mscorlib]System.Console::WriteLine(string)
IL_0032:  nop
IL_0033:  nop
IL_0034:  ldloc.s    CS$7$0002
IL_0036:  ldc.i4.1                 // add 1 into index
IL_0037:  add
IL_0038:  stloc.s    CS$7$0002
IL_003a:  ldloc.s    CS$7$0002
IL_003c:  ldloc.s    CS$6$0001
IL_003e:  ldlen
IL_003f:  conv.i4
IL_0040:  clt                      // compare i to array.Length
IL_0042:  stloc.s    CS$4$0003     // if i < array.Length
IL_0044:  ldloc.s    CS$4$0003     // then
IL_0046:  brtrue.s   IL_0025       // goto REPEAT (0025) for next iteration
codenheim
  • 20,467
  • 1
  • 59
  • 80
1

No, neither language splits the string every time (that would be absurd).

From the PHP manual:

On each iteration, the value of the current element is assigned to $value and the internal array pointer is advanced by one (so on the next iteration, you'll be looking at the next element).

Note the reference to the internal array pointer. If each iteration operated on a distinct array, changing the internal array pointer would be meaningless.

From the ES5 annotated reference:

When the forEach method is called with one or two arguments, the following steps are taken:

  1. Let O be the result of calling ToObject passing the this value as the argument.

Here O represents the object being iterated on; this result is only calculated once.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • Thanks. Jerky down voters aside - thanks for the link to documentation. – iambriansreed Sep 05 '14 at 19:16
  • You have not actually answered the question that's being asked: `This example is in c# but do other programming languages behave differently?` You've only listed two languages that don't behave differently, but you haven't answered whether there are any languages in existence that do behave differently. – Servy Sep 05 '14 at 19:42
  • @Servy: Considering that there is probably no human aware of all languages in existence, let alone how equivalent formulations would work in them, only an answer in the affirmative could possibly be acceptable by that logic. So I don't think that's a practical way to interpret the question, and chose to home into the very explicit *"Do PHP and JavaScript split on each foreach loop?"* part. I wish I could have met your high standards, but I can't. – Jon Sep 05 '14 at 20:04
  • What do you mean my interpretation. *It's an exact quote*. I agree that the question is, in any practical sense, unanswerable. The fact that the question is unanswerable doesn't mean that it's correct to just answer an entirely different question. – Servy Sep 05 '14 at 20:07
  • @Servy: Mine was an exact quote as well -- arguably, even more exact as "other languages" is somewhat ambiguous, especially when directly followed by explicit references. And considering that the OP found this answer useful (see first comment), why are we arguing? – Jon Sep 05 '14 at 20:08
  • @Jon So you answered one part of the question, the part that can be answered in just a few seconds by doing the basic research (checking the documentation) that is expected of all questions before they are asked, but not the rest of it. – Servy Sep 05 '14 at 20:11
  • @Servy: I don't agree with that assessment, but for the purposes of the discussion let's assume I agree. Does that help you explain why we are arguing? – Jon Sep 05 '14 at 20:26
0

foreach in C# is just a syntactic sugar. CLR/IL does not support anything like this. There are two versions of foreach - one for generics, and other one to support older collections, but in general it is expanded into something like this:

var enumerator = states.Split(',').GetEnumerator();
while (enumerator.MoveNext()) {
 string state = enumerator.Current;
 ...
}

See more details here: http://msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx

Ondra
  • 1,619
  • 13
  • 27