2009-04-18 5 views
4

Поле вопроса немного слишком короткое, чтобы представить мой реальный вопрос. Если кто-нибудь сможет лучше повторить его, пожалуйста, не стесняйтесь.Почему «пока» так популярен в C#?

Мой настоящий вопрос заключается в следующем: в настоящее время я читаю много кода других людей на C#, и я заметил, что одна конкретная форма итерации широко распространена (см. Код).
Мой первый вопрос:

Все ли эти итерации эквивалентны?

И второе: почему предпочитают первый? Это что-то связано с читабельностью? Теперь я не верю, что первая форма более читаема, а затем форма для форматирования, когда вы привыкнете к ней, а читаемость - слишком много субъективного элемента в этих конструкциях, конечно, то, что вы используете больше всего, будет казаться более читаемым, но я могу заверить всех, что for-form по крайней мере так же читабельна, поскольку он имеет все в одной строке, и вы даже можете прочитать инициализацию в конструкции.

Таким образом, второй вопрос: почему 3-я форма гораздо меньше видна в коде?

 // 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); 
+0

Svante, tx, я должен смотреть на язык лучше, но мое материнство не является английским, как вы наверняка догадались. – Peter

+0

Каким образом поле вопроса «слишком короткое»? Я не видел никаких ограничений на это ... –

+0

Боюсь, что я сделал, по-видимому, ошибочное - предположение. – Peter

ответ

5

Первая и третья формы, которые вы показали, повторяют вызов GetNumber. Я предпочитаю вторую форму, хотя у нее есть недостаток в использовании побочного эффекта в определенном состоянии. Однако я в значительной степени только сделать это с петлей while. Обычно я не в конечном итоге передавая результат в качестве аргумента, хотя - общие ситуации, я считаю себя в являются:

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

и

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

Оба они теперь так идиоматическое мне, что они не вызывайте у меня никаких проблем - и, как я уже сказал, они позволяют мне только излагать каждую логику один раз.

Если вам не нравится переменные слишком большая область действия, вы можете просто ввести дополнительный блок:

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

Лично я не склонен делать это - «слишком большой» объем Безразлично 't беспокоить меня что много.

Я подозреваю, что было бы не слишком сложно написать метод для инкапсуляции всего этого. Что-то вроде:

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

Имейте в виду, для чтения строки У меня есть класс, который помогает:

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

(. Это не просто работать с файлами - это довольно гибкая)

+0

Jon, эта форма является второй (есть 3 примера, а не 2), и странные логики используются для примера. (Я просто писал несколько regexloop: for (theMatch = r.Match (inputString); theMatch.Success; theMatch = theMatch.NextMatch()), но didnot хочу, что в примере – Peter

+0

И я использую, вероятно, ту же самую форму наиболее тоже, но я перефразирую вопрос для вас: «Зачем использовать распространенную форму в короткой форме?» Возможно, вы сами дали ответ: причина появления побочных эффектов? Но вы используете его в любом случае, так что это не так плохой из практики, я полагаю? – Peter

+0

А, правда - я не видел второго примера. Будет ли редактировать ... –

5

ли все это итерации эквиваленты?

да

почему предпочитают первый? Имеет это sth. делать с читабельностью?

потому что вы можете расширить область действия nr var за цикл while?

Почему 3-я форма в коде заметно меньше?

Это эквивалентно, те же заявления! Вы можете предпочесть последний, потому что вы не хотите расширять область действия переменной nr

+0

, потому что вы можете расширить область действия nr var за цикл while? -> хорошая точка, хотя в типичных случаях вы не будете – Peter

+0

И это не имеет смысла, поскольку вне цикла while nr var будет удерживать только последнее присвоенное ему значение - или нет значения, если в то время как условие не выполняется. – Hejazzman

+0

иногда это имеет смысл, это не тот случай – dfa

2

Я думаю, что люди часто используют цикл while(), потому что это лучше всего отражает то, как вы могли бы визуализировать задачу в своей голове. Я думаю, что есть какие-либо преимущества в производительности для его использования над любой другой структурой цикла.

+0

В любой из структур цикла нет преимуществ по производительности. –

2

Здесь является случайной спекуляцией:

Когда я пишу код C#, единственными двумя строками, которые я пишу, являются while() и foreach(). То есть никто больше не использует «для», так как «foreach» часто работает и часто превосходит. (Это чрезмерное обобщение, но в нем есть ядро ​​истины.) В результате мой мозг должен напрягаться, чтобы читать любой цикл «за», потому что он незнакомый.

+0

Да, я это вижу. Здесь, однако, используется условие проверки, поэтому foreach() - это еще одна история. – Peter

+0

Я не согласен, в смысле другого ответа, который я разместил (вы можете использовать условия теста с помощью foreach - например, TakeWhile). – Brian

+0

Если вы повторяете длинные строковые массивы, используйте для() вместо foreach() – Davorin

3

Я думаю, что третья форма (for-loop) является лучшей из этих альтернатив, потому что она помещает вещи в правильную область. С другой стороны, повторить вызов getNumber() тоже немного неудобно.

Как правило, я считаю, что явный цикл слишком распространен. Языки высокого уровня должны обеспечивать отображение, фильтрацию и сокращение. Когда эти высокоуровневые конструкции применимы и доступны, цикл вместо этого похож на использование goto вместо цикла.

Если сопоставление, фильтрация или сокращение не применимы, я, возможно, написал бы небольшой макрос для этого типа цикла (у C# нет этих, правда?).

3

Я предлагаю другой вариант

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

где InitInfinite тривиальный вспомогательную функцию. Всего программа:

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); 
     } 
    } 
} 
+0

Я думаю, что у Джона и у вас есть sth. похоже, но мне не хватает времени, чтобы нырнуть в него, все, я думаю, это очень интересно. – Peter

+1

Я думаю, это работает, но зачем это делать? – erikkallen

+0

Действительно? Это выглядит так естественно для меня, но я использую F # уже более года, поэтому, думаю, мой мозг постоянно искажен. – Brian

1

А почему (1) и (2) являются «предпочтительными» над (3), мне кажется, что большинство людей думают о последних, как способ итерации по диапазону, используя условие для определения диапазона, а не продолжение итерации над блоком, пока какое-то условие все еще выполняется. Семантика ключевых слов поддается этой интерпретации, и я подозреваю, что отчасти из-за этого люди находят, что выражения наиболее читабельны в этом контексте. Например, я бы никогда не использовал (1) или (2) для итерации по диапазону, хотя мог.

Между (1) и (2), я разорван. Раньше я использовал (2) (в C) чаще всего из-за компактности, но теперь (в C#) я обычно пишу (1). Я полагаю, что я пришел к пониманию удобочитаемости по сравнению с компактностью, и (1) кажется более легким для синтаксического анализа быстро и, следовательно, более читаемым для моего разума, хотя я в конечном итоге повторяю небольшую логику.

Честно говоря, я редко пишу в то время как утверждения больше, обычно используя foreach - или LINQ - в тех случаях, когда ранее были использованы операторы. Подумайте об этом, я не уверен, что я использую многие для операторов, кроме, кроме модульных тестов, где я генерирую некоторое фиксированное количество тестового объекта.