while switch loop for examples c# for-loop iteration while-loop

switch - ¿Por qué es "while" tan popular en C#?



syntax for c# (7)

El campo de preguntas es demasiado corto para plantear mi verdadera pregunta. Si alguien puede recapitularlo mejor, siéntete libre.

Mi verdadera pregunta es esta: estoy leyendo mucho código de otras personas en C # en estos días, y he notado que una forma particular de iteración está muy extendida (ver en el código).
Mi primera pregunta es:

¿Son todas estas iteraciones equivalentes?

Y mi segundo es: ¿por qué prefieren el primero? ¿Tiene algo que ver con la legibilidad? Ahora no creo que la primera forma sea más legible que la forma una vez que te acostumbras, y la capacidad de lectura es demasiado un elemento subjetivo en estos constructos, por supuesto, lo que usas más parecerá más legible, pero puedo asegurarles a todos que la forma es al menos tan legible, ya que tiene todo en una línea, e incluso puede leer la inicialización en la construcción.

Por lo tanto, la segunda pregunta: ¿por qué la tercera forma se ve mucho menos en el código?

// the ''widespread'' construct int nr = getNumber(); while (NotZero(nr)) { Console.Write(1/nr); nr = getNumber(); } // the somewhat shorter form int nr; while (NotZero(nr = getNumber())) Console.Write(1 / nr); // the for - form for (int nr = getNumber(); NotZero(nr); nr = getNumber()) Console.Write(1 / nr);


¿Son todas estas iteraciones equivalentes?

¿Por qué preferir el primero? ¿Tiene algo? que ver con la legibilidad?

porque es posible que desee ampliar el alcance del nr var más allá del ciclo while?

¿Por qué la 3ª forma se ve mucho menos en el código?

es equivalente, las mismas declaraciones! Puede preferir este último porque no desea ampliar el alcance de la variable nr


Aquí hay una especulación al azar:

Cuando escribo código C #, las únicas dos construcciones de bucle que escribo son while () y foreach (). Es decir, nadie usa ''para'' más, ya que ''foreach'' a menudo funciona y suele ser superior. (Esto es una generalización excesiva, pero tiene un núcleo de verdad.) Como resultado, mi cerebro tiene que esforzarse para leer cualquier ciclo de ''por'' porque no es familiar.


Creo que la gente usa el ciclo while () a menudo porque representa mejor la forma en que visualizarías la tarea en tu cabeza. Creo que hay beneficios de rendimiento para usarlo sobre cualquier otra estructura de bucle.


Creo que la tercera forma (for-loop) es la mejor de estas alternativas, porque pone las cosas en el alcance correcto. Por otro lado, tener que repetir la llamada a getNumber () también es un poco incómodo.

En general, creo que el bucle explícito es ampliamente usado en exceso. Los lenguajes de alto nivel deberían proporcionar mapeo, filtrado y reducción. Cuando estos constructos de alto nivel son aplicables y están disponibles, el bucle en su lugar es como usar goto lugar de bucle.

Si el mapeo, el filtrado o la reducción no son aplicables, tal vez escribiría una pequeña macro para este tipo de bucle (C # no tiene esos, ¿verdad?).


En cuanto a por qué (1) y (2) son "preferidos" sobre (3), mi sensación es que la mayoría de la gente piensa en esto último como una forma de iterar sobre un rango, usando la condición para definir el rango, en lugar de continuar iterar sobre un bloque mientras todavía se cumple alguna condición. La semántica de la palabra clave se presta a esta interpretación y sospecho que, en parte debido a eso, las personas encuentran que las expresiones son más legibles en ese contexto. Por ejemplo, nunca usaría (1) o (2) para iterar sobre un rango, aunque podría.

Entre (1) y (2), estoy desgarrado. Solía ​​usar (2) (en C) con más frecuencia debido a la compacidad, pero ahora (en C #) generalmente escribo (1). Supongo que he llegado a valorar la legibilidad sobre la compacidad y (1) parece más fácil de analizar rápidamente y, por lo tanto, más legible para mi mente, aunque termino repitiendo una pequeña cantidad de lógica.

Honestamente, rara vez escribo mientras las declaraciones ya están, por lo general usando foreach - o LINQ - en los casos en que las declaraciones while se hubieran usado previamente. Ahora que lo pienso, no estoy seguro de usar muchas declaraciones para, excepto en las pruebas unitarias donde estoy generando un número fijo de un objeto de prueba.


Los formularios primero y tercero que ha mostrado repiten la llamada a GetNumber. Prefiero la segunda forma, aunque tiene la desventaja de usar un efecto secundario dentro de una condición, por supuesto. Sin embargo, prácticamente solo hago eso con un ciclo while. Por lo general, no termino pasando el resultado como argumento: las situaciones comunes en las que me encuentro son:

string line; while ( (line = reader.ReadLine()) != null) ...

y

int bytesRead; while ( (bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) ...

Ambas son ahora tan idiomáticas para mí que no me causan ningún problema, y ​​como digo, me permiten decir solo cada parte de la lógica una vez.

Si no te gusta que la variable tenga demasiado alcance, solo puedes introducir un bloque adicional:

{ int bytesRead; while ( (bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) { // Body } }

Personalmente, no suelo hacer esto; el alcance "demasiado amplio" no me molesta demasiado.

Sospecho que no sería muy difícil escribir un método para encapsular todo esto. Algo como:

ForEach(() => reader.ReadLine(), // Way to obtain a value line => line != null, // Condition line => { // body };

Eso sí, para leer en línea tengo una clase que ayuda:

foreach (string line in new LineReader(file)) { // body }

(No solo funciona con archivos, es bastante flexible).


Ofrezco otra alternativa

foreach (var x in InitInfinite(() => GetNumber()).TakeWhile(NotZero)) { Console.WriteLine(1.0/x); }

donde InitInfinite es una función auxiliar trivial. Programa completo:

using System; using System.Collections.Generic; using System.Linq; class Program { static IEnumerable<T> InitInfinite<T>(Func<T> f) { while (true) { yield return f(); } } static int N = 5; static int GetNumber() { N--; return N; } static bool NotZero(int n) { return n != 0; } static void Main(string[] args) { foreach (var x in InitInfinite(() => GetNumber()).TakeWhile(NotZero)) { Console.WriteLine(1.0/x); } } }