Есть ли способ очистить этот тип цикла с помощью LINQ? Я действительно думаю, что этот метод должен быть одним лайнером с LINQ. Но я не понимаю ..
Мой метод выглядит так:
int[] MakeList(int stepWidth)
{
var ret = new List<int>();
for (var i=0;i<360;i+=stepWidth)
{
ret.Add(i);
}
return ret.ToArray();
}
Единственное решение, которое я мог придумать, было:
Enumerable.Range(0, 360).Where(x => x % stepWidth == 0).ToArray();
Но этот Enumerabl.Range (0,360) мне кажется слишком грубым :)
Не думаю, что ваше решение грубое. Он делает то, что вы хотите, и вам всегда будет нужен аспект stepWidth.
@TheGeneral: Я думаю, это зависит от того, что вы предпочитаете ... Мне очень нравится LINQ, и я думаю, что выражение linq будет быстрее читать, чем мой метод MakeList ..
Вы можете немного ускорить это. Enumerable.Range(0, 360 / stepWidth).Select(x => x * stepWidth).ToArray()
Я думаю, что это совершенно ясно и так, но если это то, что вы часто называете, его, безусловно, можно оптимизировать, чтобы он создавал только один массив, а не создавал список, а затем копировал содержимое этого списка в массив ...
@Matthew Я посмотрел исходный код C#, и, очевидно, List<T>
внутренне реализован как массив, и из ToArray()
этот внутренний массив будет возвращен. Таким образом, нет окончательной копии, но все еще есть динамическое расширение массива, которое предотвращается, если мы создали массив сами.
@Dialecticus Нет, это совсем не так. Если бы это было так, изменение данных внутри массива, возвращаемого из ToArray()
, также изменило бы данные в списке, чего не происходит (к счастью)!
@ Мэтью, да, конечно. Я посмотрел на это снова, и новый массив создается из внутренней структуры Buffer
. Кто знает, что я увидел впервые ..
Комбинация .Select
и .TakeWhile
может быть одним из подходов, но он очень похож на другие подходы.
var result = Enumerable.Range(0, int.MaxValue)
.Select(i => i * step)
.TakeWhile(i => i < 360)
.ToArray();
По иронии судьбы, если вы подсчитаете все непробельные символы в подходе Linq, он на самом деле длиннее, чем код OP (а также труднее читать, IMO).
@MatthewWatson, я думаю (IMO :)), TakeWhile
более информативен в отношении намерений, и даже дольше он будет повторять только необходимое количество раз, где Where
будет повторять все числа в пределах диапазона.
Во-первых: ваш код приятный и читабельный.
Второе: не бойтесь циклов - циклы отлично показывают замыслы программиста. Если бы мне пришлось что-то изменить, я бы исключил List<i>
:
int[] MakeList(int stepWidth)
{
var length = (360 + stepWidth - 1) / stepWidth;
var ret = new int[length];
for (var i = 0; i < length; i++)
{
ret[i] = i * stepWidth;
}
return ret;
}
В-третьих: если LINQ используется часто, я бы вернул IEnumerable<int>
(см. Генераторы):
IEnumerable<int> MakeSequence(int step)
{
for (var i = 0; i < 360; i += step)
yield return i;
}
И используйте его так: MakeSequence(x).ToArray()
или MakeSequence(x).[SomeLinqMethods]
.
На самом деле это не возвращает правильный результат для некоторых значений stepWidth
. Например, попробуйте это с stepWidth ==7
и сравните с результатами OP. Он вернет на одно значение меньше. Фактическое количество нужно округлить, например (360 + stepWidth - 1) / stepWidth
.
Спасибо, Мэттью, ты прав. Исправленный. Генератор проверил, работает как положено.
Ответ - нет, и на самом деле не выше того, что у вас есть. и, кроме того, не бойтесь читабельного кода.