Я новичок в C# и практикуюсь, пишу решение классической задачи о 100 дверях. Приведенный ниже (и, вероятно, плохой) код решает проблему:
internal class Program
{
private static void Main(string[] args)
{
var doors = new bool[100];
var ints = new int[100];
ints = Enumerable.Range(1, 100).ToArray();
for (int i = 1; i <= 100; i++)
{
ints.Where(j => j % i == 0)
.Select(j => j)
.ToList()
.ForEach(j => doors[j - 1] = !doors[j - 1]); //This is the relevant line.
}
Array.ForEach(doors, i => Console.WriteLine(i));
}
}
Строка, на которую я поставил комментарий, меня удивила. Он отлично работает, но заменив его одним из следующих бросков System.IndexOutOfRangeException
.ForEach(j => doors[--j] = !doors[--j]);
.ForEach(j => doors[j--] = !doors[j--]);
Почему оба они недействительны, несмотря на то, что j - 1 в порядке? Насколько я понимаю, при любых обстоятельствах ровно один из --j и j-- будет эквивалентен j - 1.
Насколько я понимаю, при любых обстоятельствах ровно один из --j и j-- будет эквивалентен j - 1.
Нет, определенно нет.
Однако они терпят неудачу по разным причинам: один, когда j изначально равен 1, а другой, когда j изначально равен 100.
Сначала подумайте об этом:
.ForEach(j => doors[--j] = !doors[--j]);
Это уменьшает j дважды и в каждом случае использует значение j после того, как произошло уменьшение.
Сначала оценивается левый операнд, поэтому, когда j равно 1, это заканчивается оценкой индексов массива как:
doors[0] = !doors[-1];
Очевидно, что doors[-1] недействителен.
Теперь рассмотрим второй вариант:
.ForEach(j => doors[j--] = !doors[j--]);
Это снова уменьшает j дважды, но в каждой операции индексации массива используется значение j перед уменьшением. Итак, когда j равно 100, это приводит к оценке индексов массива как:
doors[100] = !doors[99];
На этот раз это doors[100], что недействительно.
В вашем рабочем коде вы используете j - 1 для обоих индексов, вообще не изменяя j, поэтому, когда j изменяется от 1 до 100, индексы массива, которые вы используете, изменяются от 0 до 99, что является допустимым.
Я бы настоятельно рекомендовал не использовать пре-/пост-инкременты/декременты в любом выражении или операторе, где вы используете измененную переменную более чем в одном месте, и определенно избегайте пре-/пост-инкрементов/декрементов одной и той же переменной несколько раз в одном и том же выражении. или заявление.
Важный момент, который следует уточнить, прежде чем даже перейти к индексу за пределами границ: оператор приращения преобразуется в j = j - 1 либо до (--j), либо после (j--) переменной j, используемой в операторе. j - 1 == j - 1 ключевое отличие состоит в том, что присваивание j не происходит