Многократная инициализация в цикле C # for - PullRequest
49 голосов
/ 02 ноября 2009

Как я могу (если это вообще возможно) инициализировать несколько переменных разного типа в цикле C # for? Пример:

for (MyClass i = 0, int j = 1; j<3; j++,i++)

Ответы [ 12 ]

78 голосов
/ 02 ноября 2009

Это не может быть сделано. Поместите одно из объявлений перед циклом:

MyClass i = 0;
for (int j = 1; j < 3; j++, i++)

Или для симметрии оба:

MyClass i = 0;
int j = 1;
for (; j < 3; j++, i++)

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

MyClass i = 0;
for (int j = 0; j < 3; j++)
{
    ...
    i++;
}

Обратите внимание, что если i и j были одного типа, вы могли бы объявить их обоих в цикле for:

for (int i = 0, j = 1; j < 3; j++, i++)
34 голосов
/ 09 марта 2014

Конечно, это МОЖЕТ быть сделано. Просто используйте ключевое слово dynamic :

public static void Main(string[] args) {
    for (dynamic x = 0, y = new MyClass { a = 20, b = 30 }; x < 100; x++, y.a++, y.b--) {
        Console.Write("X=" + x + " (" + x.GetType() + "\n" +
                      "Y.a=" + y.a + ",Y.b=" + y.b + " (" + y.GetType() + "\n");
     }
}

class MyClass {
    public int a = 0, b = 0;
}

Хорошего дня!

15 голосов
/ 04 августа 2010

Да, это можно сделать. Вы можете инициализировать переменные разных типов внутри оператора for, но вы не можете объявить переменные разных типов внутри оператора for. Чтобы инициализировать переменные разных типов внутри оператора for, вы должны объявить все типы перед циклом for. Например:

int xx;
string yy;
for(xx=0, yy=""; xx<10; xx++)
    {
    ....
    }

[EDIT] Добавление дополнительной информации для полноты. Это выходит за рамки того, что запрашивал OP, но может быть полезным для других. В цикле for легко инициализировать переменные одного типа, просто разделите инициализацию запятыми. Вы также можете изменить несколько переменных в третьем разделе. Вы не можете иметь несколько разделенных запятыми разделов во втором разделе сравнения, но вы можете использовать && || а также ! создать сложный логический раздел на основе нескольких переменных.

for(int i=0, j=0, k=99; i<10 && k<200; i++, j++, k += 2)

Однако не рекомендуется делать такое сложное утверждение, что трудно понять, что происходит.

8 голосов
/ 09 марта 2014

Это вредно?

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

На очень фундаментальном уровне анализатор языка различает объявления , выражения и выражения . Языки с фигурными скобками скрывают это различие, вы можете превратить любое выражение в утверждение, просто поставив точку с запятой после него. А в некоторых случаях принимайте объявление внутри оператора, хорошим примером является оператор for (;;). Проще говоря, этот синтаксис вполне приемлем для языков C или C ++:

int x = 42;
x;

Это не совсем хорошая вещь, это бессмысленный код. Язык C # поднял планку на этом, это будет отклонено. Но не:

int x = 42;
x++;

Для синтаксического анализа языка добавлено специальное правило.

То, что ни один из языков фигурных скобок не примет, превращает объявление в выражение. Таким образом, лежит безумие, драконы в конце карты, корабль падает с края без хорошего сообщения, чтобы сообщить. Оператор запятой требует, чтобы левый и правый операнды были выражениями. Декларация - это не выражение, конец истории.

5 голосов
/ 17 марта 2016

Обычно я помещаю объявления перед циклом и использую дополнительные фигурные скобки, чтобы ограничить область объявлений:

{ //limit the scope: i, count, iDivisibleBy2, iDivisibleBy3, iDivisibleBy5
    int i = 0, count = 100;
    bool iDivisibleBy2 = true, iDivisibleBy3 = true, iDivisibleBy5 = true;
    for( ; i < count; ++i, iDivisibleBy2 = (i % 2 == 0), iDivisibleBy3 = ( i % 3 == 0 ), iDivisibleBy5 = ( i % 5 == 0 ) )
    {
        //...
    }
}
2 голосов
/ 09 июля 2018

Начиная с C # 7, используйте кортеж:

for (var foo = (i:new MyClass(0), j:1); foo.j < 3; foo.i++, foo.j++)) { … }
1 голос
/ 04 марта 2014
for (initializer; condition; iterator)
{
    //body
}

Раздел initializer устанавливает начальные условия. Операторы в этом разделе выполняются только один раз, прежде чем войти в цикл. Раздел может содержать только одну из следующих двух опций.

1) Объявление и инициализация локальной переменной цикла. Переменная является локальной для цикла и недоступна извне цикла.

2) Ноль или более выражений операторов из следующего списка, разделенных запятыми:

  • Заявление о назначении;

  • Вызов метода;

  • Префиксное или постфиксное выражение приращения, например ++ i или i ++;

  • Выражение префикса или постфикса, например --i или i -;

  • Создание объекта с использованием new;

  • Ожидание выражения;

Как мы знаем, компилятор не предназначен для принятия того, что мы ожидаем. Таким образом, приведенные выше правила должны соблюдаться перед записью секции инициализатора внутри цикла for.

http://msdn.microsoft.com/en-us/library/ch45axte.aspx

0 голосов
/ 28 октября 2017

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

В инициализаторе цикла for можно (косвенно) объявлять и инициализировать столько переменных, сколько вам нужно, разных типов без использования динамического ключевого слова. Просто используйте пользовательскую структуру для вашей индексной переменной.

for(var i = new I1<MyClass>(0, 1); i < 3; i++, i.a++) {
    MyClass myClass = i.a;
}

Перегруженные операторы означают, что вы можете использовать «i» как int везде. Для чистого синтаксиса инициализируйте с 0:

for(I1<float> i = 0; i < array.Length; i++) {
    i.a += array[i]; // accumulate a float value
}

Еще несколько глупых примеров:

// Three variables
for(I3<object, string, int> i = 0; i < 100; i++) {
    i.a = new object();
    i.b = "This is index " + i;
    i.c = 100 - i;
}

// A class
for(var i = new I1<SomeClass>(0, new SomeClass()); i < 20; i += 2) {
    i.a.someVar1 = "We can have even more variables in here! Woot!";
    i.a.DoSomething(i);
}

// An array
for(var i = new I1<string[]>(0, new[] { "Hi", "Mom" }); i < 10; i++) {
    for(int j = 0; j < i.a.Length; j++) {
        Log(i.a[j]);
    }
}

Вот структуры. Они работают, но не проходят тщательного тестирования, поэтому могут быть ошибки:

public struct I1<T> {

    public int index;
    public T a;

    public I1(int index) {
        this.index = index;
        this.a = default(T);
    }
    public I1(int index, T a) {
        this.index = index;
        this.a = a;
    }

    public override bool Equals(object obj) {
        if(!(obj is I1<T>)) return false;
        I1<T> other = (I1<T>)obj;
        return index == other.index && EqualityComparer<T>.Default.Equals(a, other.a);
    }

    public override int GetHashCode() {
        int hash = 17;
        hash = hash * 29 + index.GetHashCode();
        if(typeof(T).IsValueType && !object.ReferenceEquals(a, null)) hash = hash * 29 + a.GetHashCode();
        return hash;
    }

    public override string ToString() {
        return index.ToString();
    }

    public static implicit operator I1<T>(int other) {
        return new I1<T>(other);
    }

    public static implicit operator int(I1<T> other) {
        return other.index;
    }

    // Unary operators

    public static int operator +(I1<T> a) {
        return +a.index;
    }

    public static int operator -(I1<T> a) {
        return -a.index;
    }

    public static int operator ~(I1<T> a) {
        return ~a.index;
    }

    public static I1<T> operator ++(I1<T> a) {
        a.index++;
        return a;
    }

    public static I1<T> operator --(I1<T> a) {
        a.index--;
        return a;
    }

    // Binary operators

    public static I1<T> operator +(I1<T> a, int b) {
        a.index += b;
        return a;
    }
    public static I1<T> operator +(int a, I1<T> b) {
        b.index += a;
        return b;
    }

    public static I1<T> operator -(I1<T> a, int b) {
        a.index -= b;
        return a;
    }
    public static I1<T> operator -(int a, I1<T> b) {
        b.index = a - b.index;
        return b;
    }

    public static I1<T> operator *(I1<T> a, int b) {
        a.index *= b;
        return a;
    }
    public static I1<T> operator *(int a, I1<T> b) {
        b.index *= a;
        return b;
    }

    public static I1<T> operator /(I1<T> a, int b) {
        a.index /= b;
        return a;
    }
    public static I1<T> operator /(int a, I1<T> b) {
        b.index = a / b.index;
        return b;
    }

    public static I1<T> operator %(I1<T> a, int b) {
        a.index %= b;
        return a;
    }
    public static I1<T> operator %(int a, I1<T> b) {
        b.index = a % b.index;
        return b;
    }

    public static I1<T> operator &(I1<T> a, int b) {
        a.index &= b;
        return a;
    }
    public static I1<T> operator &(int a, I1<T> b) {
        b.index = a & b.index;
        return b;
    }

    public static I1<T> operator |(I1<T> a, int b) {
        a.index |= b;
        return a;
    }
    public static I1<T> operator |(int a, I1<T> b) {
        b.index = a | b.index;
        return b;
    }

    public static I1<T> operator ^(I1<T> a, int b) {
        a.index ^= b;
        return a;
    }
    public static I1<T> operator ^(int a, I1<T> b) {
        b.index = a ^ b.index;
        return b;
    }

    public static I1<T> operator <<(I1<T> a, int b) {
        a.index <<= b;
        return a;
    }

    public static I1<T> operator >>(I1<T> a, int b) {
        a.index >>= b;
        return a;
    }

    // Comparison operators

    public static bool operator ==(I1<T> a, int b) {
        return a.index == b;
    }
    public static bool operator ==(int a, I1<T> b) {
        return a == b.index;
    }

    public static bool operator !=(I1<T> a, int b) {
        return a.index != b;
    }
    public static bool operator !=(int a, I1<T> b) {
        return a != b.index;
    }

    public static bool operator <(I1<T> a, int b) {
        return a.index < b;
    }
    public static bool operator <(int a, I1<T> b) {
        return a < b.index;
    }

    public static bool operator >(I1<T> a, int b) {
        return a.index > b;
    }
    public static bool operator >(int a, I1<T> b) {
        return a > b.index;
    }

    public static bool operator <=(I1<T> a, int b) {
        return a.index <= b;
    }
    public static bool operator <=(int a, I1<T> b) {
        return a <= b.index;
    }

    public static bool operator >=(I1<T> a, int b) {
        return a.index >= b;
    }
    public static bool operator >=(int a, I1<T> b) {
        return a >= b.index;
    }
}

public struct I2<T1, T2> {

    public int index;
    public T1 a;
    public T2 b;

    public I2(int index) {
        this.index = index;
        this.a = default(T1);
        this.b = default(T2);
    }
    public I2(int index, T1 a) {
        this.index = index;
        this.a = a;
        this.b = default(T2);
    }
    public I2(int index, T1 a, T2 b) {
        this.index = index;
        this.a = a;
        this.b = b;
    }

    public override bool Equals(object obj) {
        if(!(obj is I2<T1, T2>)) return false;
        I2<T1, T2> other = (I2<T1, T2>)obj;
        return index == other.index && EqualityComparer<T1>.Default.Equals(a, other.a) && EqualityComparer<T2>.Default.Equals(b, other.b);
    }

    public override int GetHashCode() {
        int hash = 17;
        hash = hash * 29 + index.GetHashCode();
        if(typeof(T1).IsValueType && !object.ReferenceEquals(a, null)) hash = hash * 29 + a.GetHashCode();
        if(typeof(T2).IsValueType && !object.ReferenceEquals(b, null)) hash = hash * 29 + b.GetHashCode();
        return hash;
    }

    public override string ToString() {
        return index.ToString();
    }

    public static implicit operator I2<T1, T2>(int other) {
        return new I2<T1, T2>(other);
    }

    public static implicit operator int(I2<T1, T2> other) {
        return other.index;
    }

    // Unary operators

    public static int operator +(I2<T1, T2> a) {
        return +a.index;
    }

    public static int operator -(I2<T1, T2> a) {
        return -a.index;
    }

    public static int operator ~(I2<T1, T2> a) {
        return ~a.index;
    }

    public static I2<T1, T2> operator ++(I2<T1, T2> a) {
        a.index++;
        return a;
    }

    public static I2<T1, T2> operator --(I2<T1, T2> a) {
        a.index--;
        return a;
    }

    // Binary operators

    public static I2<T1, T2> operator +(I2<T1, T2> a, int b) {
        a.index += b;
        return a;
    }
    public static I2<T1, T2> operator +(int a, I2<T1, T2> b) {
        b.index += a;
        return b;
    }

    public static I2<T1, T2> operator -(I2<T1, T2> a, int b) {
        a.index -= b;
        return a;
    }
    public static I2<T1, T2> operator -(int a, I2<T1, T2> b) {
        b.index = a - b.index;
        return b;
    }

    public static I2<T1, T2> operator *(I2<T1, T2> a, int b) {
        a.index *= b;
        return a;
    }
    public static I2<T1, T2> operator *(int a, I2<T1, T2> b) {
        b.index *= a;
        return b;
    }

    public static I2<T1, T2> operator /(I2<T1, T2> a, int b) {
        a.index /= b;
        return a;
    }
    public static I2<T1, T2> operator /(int a, I2<T1, T2> b) {
        b.index = a / b.index;
        return b;
    }

    public static I2<T1, T2> operator %(I2<T1, T2> a, int b) {
        a.index %= b;
        return a;
    }
    public static I2<T1, T2> operator %(int a, I2<T1, T2> b) {
        b.index = a % b.index;
        return b;
    }

    public static I2<T1, T2> operator &(I2<T1, T2> a, int b) {
        a.index &= b;
        return a;
    }
    public static I2<T1, T2> operator &(int a, I2<T1, T2> b) {
        b.index = a & b.index;
        return b;
    }

    public static I2<T1, T2> operator |(I2<T1, T2> a, int b) {
        a.index |= b;
        return a;
    }
    public static I2<T1, T2> operator |(int a, I2<T1, T2> b) {
        b.index = a | b.index;
        return b;
    }

    public static I2<T1, T2> operator ^(I2<T1, T2> a, int b) {
        a.index ^= b;
        return a;
    }
    public static I2<T1, T2> operator ^(int a, I2<T1, T2> b) {
        b.index = a ^ b.index;
        return b;
    }

    public static I2<T1, T2> operator <<(I2<T1, T2> a, int b) {
        a.index <<= b;
        return a;
    }

    public static I2<T1, T2> operator >>(I2<T1, T2> a, int b) {
        a.index >>= b;
        return a;
    }

    // Comparison operators

    public static bool operator ==(I2<T1, T2> a, int b) {
        return a.index == b;
    }
    public static bool operator ==(int a, I2<T1, T2> b) {
        return a == b.index;
    }

    public static bool operator !=(I2<T1, T2> a, int b) {
        return a.index != b;
    }
    public static bool operator !=(int a, I2<T1, T2> b) {
        return a != b.index;
    }

    public static bool operator <(I2<T1, T2> a, int b) {
        return a.index < b;
    }
    public static bool operator <(int a, I2<T1, T2> b) {
        return a < b.index;
    }

    public static bool operator >(I2<T1, T2> a, int b) {
        return a.index > b;
    }
    public static bool operator >(int a, I2<T1, T2> b) {
        return a > b.index;
    }

    public static bool operator <=(I2<T1, T2> a, int b) {
        return a.index <= b;
    }
    public static bool operator <=(int a, I2<T1, T2> b) {
        return a <= b.index;
    }

    public static bool operator >=(I2<T1, T2> a, int b) {
        return a.index >= b;
    }
    public static bool operator >=(int a, I2<T1, T2> b) {
        return a >= b.index;
    }
}

public struct I3<T1, T2, T3> {

    public int index;
    public T1 a;
    public T2 b;
    public T3 c;

    public I3(int index) {
        this.index = index;
        this.a = default(T1);
        this.b = default(T2);
        this.c = default(T3);
    }
    public I3(int index, T1 a) {
        this.index = index;
        this.a = a;
        this.b = default(T2);
        this.c = default(T3);
    }
    public I3(int index, T1 a, T2 b) {
        this.index = index;
        this.a = a;
        this.b = b;
        this.c = default(T3);
    }
    public I3(int index, T1 a, T2 b, T3 c) {
        this.index = index;
        this.a = a;
        this.b = b;
        this.c = c;
    }

    public override bool Equals(object obj) {
        if(!(obj is I3<T1, T2, T3>)) return false;
        I3<T1, T2, T3> other = (I3<T1, T2, T3>)obj;
        return index == other.index && EqualityComparer<T1>.Default.Equals(a, other.a) &&
            EqualityComparer<T2>.Default.Equals(b, other.b) &&
            EqualityComparer<T3>.Default.Equals(c, other.c);
    }

    public override int GetHashCode() {
        int hash = 17;
        hash = hash * 29 + index.GetHashCode();
        if(typeof(T1).IsValueType && !object.ReferenceEquals(a, null)) hash = hash * 29 + a.GetHashCode();
        if(typeof(T2).IsValueType && !object.ReferenceEquals(b, null)) hash = hash * 29 + b.GetHashCode();
        if(typeof(T3).IsValueType && !object.ReferenceEquals(c, null)) hash = hash * 29 + c.GetHashCode();
        return hash;
    }

    public override string ToString() {
        return index.ToString();
    }

    public static implicit operator I3<T1, T2, T3>(int other) {
        return new I3<T1, T2, T3>(other);
    }

    public static implicit operator int(I3<T1, T2, T3> other) {
        return other.index;
    }

    // Unary operators

    public static int operator +(I3<T1, T2, T3> a) {
        return +a.index;
    }

    public static int operator -(I3<T1, T2, T3> a) {
        return -a.index;
    }

    public static int operator ~(I3<T1, T2, T3> a) {
        return ~a.index;
    }

    public static I3<T1, T2, T3> operator ++(I3<T1, T2, T3> a) {
        a.index++;
        return a;
    }

    public static I3<T1, T2, T3> operator --(I3<T1, T2, T3> a) {
        a.index--;
        return a;
    }

    // Binary operators

    public static I3<T1, T2, T3> operator +(I3<T1, T2, T3> a, int b) {
        a.index += b;
        return a;
    }
    public static I3<T1, T2, T3> operator +(int a, I3<T1, T2, T3> b) {
        b.index += a;
        return b;
    }

    public static I3<T1, T2, T3> operator -(I3<T1, T2, T3> a, int b) {
        a.index -= b;
        return a;
    }
    public static I3<T1, T2, T3> operator -(int a, I3<T1, T2, T3> b) {
        b.index = a - b.index;
        return b;
    }

    public static I3<T1, T2, T3> operator *(I3<T1, T2, T3> a, int b) {
        a.index *= b;
        return a;
    }
    public static I3<T1, T2, T3> operator *(int a, I3<T1, T2, T3> b) {
        b.index *= a;
        return b;
    }

    public static I3<T1, T2, T3> operator /(I3<T1, T2, T3> a, int b) {
        a.index /= b;
        return a;
    }
    public static I3<T1, T2, T3> operator /(int a, I3<T1, T2, T3> b) {
        b.index = a / b.index;
        return b;
    }

    public static I3<T1, T2, T3> operator %(I3<T1, T2, T3> a, int b) {
        a.index %= b;
        return a;
    }
    public static I3<T1, T2, T3> operator %(int a, I3<T1, T2, T3> b) {
        b.index = a % b.index;
        return b;
    }

    public static I3<T1, T2, T3> operator &(I3<T1, T2, T3> a, int b) {
        a.index &= b;
        return a;
    }
    public static I3<T1, T2, T3> operator &(int a, I3<T1, T2, T3> b) {
        b.index = a & b.index;
        return b;
    }

    public static I3<T1, T2, T3> operator |(I3<T1, T2, T3> a, int b) {
        a.index |= b;
        return a;
    }
    public static I3<T1, T2, T3> operator |(int a, I3<T1, T2, T3> b) {
        b.index = a | b.index;
        return b;
    }

    public static I3<T1, T2, T3> operator ^(I3<T1, T2, T3> a, int b) {
        a.index ^= b;
        return a;
    }
    public static I3<T1, T2, T3> operator ^(int a, I3<T1, T2, T3> b) {
        b.index = a ^ b.index;
        return b;
    }

    public static I3<T1, T2, T3> operator <<(I3<T1, T2, T3> a, int b) {
        a.index <<= b;
        return a;
    }

    public static I3<T1, T2, T3> operator >>(I3<T1, T2, T3> a, int b) {
        a.index >>= b;
        return a;
    }

    // Comparison operators

    public static bool operator ==(I3<T1, T2, T3> a, int b) {
        return a.index == b;
    }
    public static bool operator ==(int a, I3<T1, T2, T3> b) {
        return a == b.index;
    }

    public static bool operator !=(I3<T1, T2, T3> a, int b) {
        return a.index != b;
    }
    public static bool operator !=(int a, I3<T1, T2, T3> b) {
        return a != b.index;
    }

    public static bool operator <(I3<T1, T2, T3> a, int b) {
        return a.index < b;
    }
    public static bool operator <(int a, I3<T1, T2, T3> b) {
        return a < b.index;
    }

    public static bool operator >(I3<T1, T2, T3> a, int b) {
        return a.index > b;
    }
    public static bool operator >(int a, I3<T1, T2, T3> b) {
        return a > b.index;
    }

    public static bool operator <=(I3<T1, T2, T3> a, int b) {
        return a.index <= b;
    }
    public static bool operator <=(int a, I3<T1, T2, T3> b) {
        return a <= b.index;
    }

    public static bool operator >=(I3<T1, T2, T3> a, int b) {
        return a.index >= b;
    }
    public static bool operator >=(int a, I3<T1, T2, T3> b) {
        return a >= b.index;
    }
}
0 голосов
/ 29 февраля 2016

Нет особых причин не запускать альтернативный индексатор внутриполосно. Он защищает среду от мусорных назначений переменных.

for (int x=0,y = 0; x < 100; x++)
{
    if (true) {  y++; }
    // ... use y as a conditional indexer
    // ... x is always the loop indexer
    // ... no overflows
}
0 голосов
/ 09 марта 2014

Это не особенно мой опыт, но вот мой мозговой штурм по теме:

В теории языков программирования синтаксис языка должен быть определен без двусмысленности. Я не могу вдаваться в подробности, так как я изучал эти темы уже пару лет. Но вы можете проверить Форма Бэкуса-Наура , которая является техникой обозначения для описания «грамматики» языка. И это единственный, с кем я знаком.

Так что это описание используется при разборе кода. И ваш синтаксический анализатор должен иметь возможность связывать каждую часть вашего кода с «правилами» грамматики. Вы можете увидеть грамматику C # здесь . В некоторой степени похожа.

(1) Посмотрите на синтаксис оператора for

for-statement:
    for(for-initializer;for-condition;for-iterator)   
        embedded-statement

и синтаксис для инициализатора

for-initializer:
    local-variable-declaration
    statement-expression-list

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

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

Здесь - это хороший базовый набор слайдов, который демонстрирует, как сложные вещи могут справиться даже с тривиально простой грамматикой.

(2) То, что мы наблюдали в 1, это то, что мы можем поместить для инициализатора части цикла for. И мы знаем, почему ваше предложение не работает. Чтобы быть охотником за головами, давайте проанализируем причину этого выбора дизайна.

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

Если вы собираетесь поместить несколько операторов объявлений, возможно, вы захотите создать что-то вроде объявления-списка. И то, что вы собираетесь использовать для разделителя, вы, вероятно, не захотите использовать; потому что точка с запятой используется для разделения частей цикла for. Таким образом, вы всегда можете использовать запятую, но если вы используете запятую, тогда правило списка объявлений можно использовать только для циклов for, так как было бы непонятно, чтобы объявления во всем коде разделялись запятыми.

Во-вторых, что с этим не так? Если вы спросите меня, я тоже не вижу ничего плохого. Если они разработали язык, который он должен был бы работать. (Я не говорю, что набросок, который я только что сделал, на 100% правильный, его можно использовать только в качестве отправной точки сеанса мозгового штурма.)

Тогда, почему они решили не делать этого? Что заставило их избежать этого?

  • Как много сложностей вы вносите, добавляя это правило к вашему грамматики?
  • Вы также считаете, сколько дополнительных правил вы должны добавить? сделать язык однозначным?
  • Вы понимаете, насколько сложными могут стать уже существующие правила?
  • Сколько рабочей нагрузки добавляет ваш парсер, а затем компилятор?

После всех этих и многих других соображений

  • Сколько вы добавляете к удобству и простоте чтения вашего языка?
  • Или вы вообще теряете читабельность?
  • Считаете ли вы, как часто программистам нужны эти типы для петли
  • Даже если они действительно нуждаются в этом часто, следует ли вам препятствуя им для этого типа кодирования?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...