aCollection do: [ :element | "do something with element" ].
примеры:
aCollection do: [ :element | element blink ].
Посылает каждому элементу коллекции aCollection сообщение blink.
aCollection do: [ :element | anObject doSomethingWith: element ].
Посылает объекту anObject сообщение doSomethingWith: на каждый элемент коллекции aCollection. Параметром каждый раз передаётся текущий элемент коллекции.
Перебор по числам (реально требуется довольно редко):
общий вид:
aNumber1 to: aNumber2 do: [ :number | "do something with number" ].
или
(aNumber1 to: aNumber2) do: [ :number | "do something with number" ].
(с точки зрения поведения разницы нет)
пример:
1 to: 10 do: [ :i | anObject doSomethingWith: i ].
Перебор по числам с интервалом:
общий вид:
aNumber1 to: aNumber2 by: aNumber3 do: [ :number | "do something with number" ].
или
(aNumber1 to: aNumber2 by: aNumber3) do: [ :number | "do something with number" ].
(с точки зрения поведения разницы нет)
примеры:
1 to: 10 by: 3 do: [ :i | anObject doSomethingWith: i ].
Здравствуйте, IT, Вы писали:
IT>+1. Похоже, что именно поэтому так быстро прижился foreach в C#, гораздо чаще используется for в C++ по сравнению с while и в очень редких случаях применяется do while.
do while применяется так редко не поэтому, а потому что ситуации, когда необходимо выполнить тело цикла один раз обязательно, встречаются намного реже, чем ситуации, когда это не требуется.
for используеться чаще, чем while просто потому, что с for мы обычно ассоциируем последовательный перебор (0,1,2..) , а c while — произвольный итерационный процесс. Хотя это и неверно, но мы так подсознательно считаем. Есть у нас такие абстракции — for и while. А последовательный перебор встречается чаще.
Здравствуйте, VladD2, Вы писали:
VD>Хочется услышать мнение народа о том как бы он хотел видеть паттерн перебора значений из некоторого диапазона.
Да.. Есть еще проблемы на свете...
А проще всего было в Фортране
DO 100 I = 1,10
или с шагом
DO 100 I = 1,11,2
Только вот никому не советовали по ошибке вместо запятой ставить точку. Из-за этой точки в 60-е годы одна американская ракета при взлете самоуничтожилась (по крайней мере так говорят)
public class Range : IEnumerable<int>
{
private int _low;
private int _high;
public IEnumerator<int> GetEnumerator()
{
for (int i = _low; i <= _high; i++)
yield return i;
}
#region Construction
public Range(int low, int high)
{
_low = low;
_high = high;
}
public static Range Indexes<T>(T[] array)
{
return new Range(0, array.Length - 1);
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
использование:
int[] a = new int[5];
foreach(int i in Range.Indexes(a)) // нет выбора между Length/Length-1
Console.Write(i);
foreach(int i in new Range(0, 10))
Console.Write(i);
Предполагается набор перегруженных конструкторов для указания step. Предполагается наличие свойства Reverse для обращения обхода. Предполагается наличие операций пересечения/объединения интервалов. Велком
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, VladD2, Вы писали: VD>>В общем, приветствуются любые мысли. S>Мысль такая:
<skipped>
М-да... Похоже, пора объявить конкурс на самую длинную реализацию цикла for...
Господа, а не кажется ли Вам, что для того, чтобы сравнивать нынешний программный продукт по числу LOC c тем, что писали 10 лет назад, надо для нынешнего продукта автоматически применять понижающий коэффициент, равный так примерно 10 ?
P.S. А вообще дискуссия начинает понемногу напоминать незавбвенную "Hello, World".
Если использовать foreach, то надо подразумевать, что x итерирует по множеству, соответственно запись множества при использовании оператора цикла и при самостоятельном использовании должна быть одинакова, Ruby — как раз тому пример.
Если же мы просто определяем границы и правила итерации (шаг и выход за границы), то ИМХО, оператор for предпочтительней.
Это по самому ключевому слову было.
Далее.
Для первого случая, ИМХО, надо придумать операторы порождения потока, результат применения которых неплохо смотрелся бы как IEnumerator<> или IEnumerable<>.
Почему бы не взять "знакомые" конструкции с других языков (ruby, pascal)?
[1 .. 3] // от 1-го до 3-х включительно
[1 .. 10; 2] // шаг 2
[0 .. 10, 20 .. 30; 5] = {0, 5, 10, 20, 25, 30}
public delegate T NextIndexGenerator<T>(T current); // позволить использовать формулу для вычисления следующего члена ряда
// исходя из значения предыдущего
[1 .. 1024; @current*2] = {1, 2, 4, 8, ..., 1024}
[0.1 .. 0.9, Math.Sqrt] = {0.1, 0.316, ..., 0.8999 } // ряд, сходящщийся к 1.0 по формуле next = sqrt(current);
Несчет неудобства array.Length-1 ничего сказать не могу, лично мне безразлично. Если кому-то важно, то можно оппробовать разные формы записи для открытых и закрытых диапазонов. Я бы предложил так:
[0..array.Length),
но боюсь, случайная опечатка в парных скобочках будет пересекаться с семантикой, что не есть гут. Может быть будут еще идеи как указать открытый диапазон? А может быть ввести некое ключевое слово, типа before:
[1 .. before 1024; @current*4]
----------
Насчет же указания границ перебора для цикла for — все предложенные варианты интересны (хоть и записаны были как foreach). Действительно, надо взять несколько наиболее встречающихся случаев переборав цикла и заточить синтаксис под них.
Вот еще варианты:
for(i from 0 to 10 step 2) // закрытый диапазон слева и справаfor(i from 0 before 10 step 5) // открытый диапазон
Кстати, почему бы не сделать from 0 по умолчанию для before (самый востребованный сценарий использования)?
Совсем коротко выходит:
int[] arr = new int[10];
for(i before arr.Length)
arr[i]=i*i;
Здравствуйте, VladD2, Вы писали:
VD>Хочется услышать мнение народа о том как бы он хотел видеть паттерн перебора значений из некоторого диапазона.
VD>foreach (i in 0..3) // выдает последовательность 0, 1, 2, 3 VD>этот синтаксис хорош, но неудобен когда нужно перебрать значения индекса некой коллекции: VD>foreach (i in 0..array.Length — 1) // не нравится этот "- 1"
Есть такие мысли:
1. Использовать генератор xrange() (или range()) для открытых справа диапазонов. При этом ".." будет соответствовать закрытому интервалу, вроде closed_range()
2. for (int i = 0 while i < 10), for (int i = 0 while i <= 10 step 2). Но мне очень не нравится, что < можно спутать с <=, по сути это тот же Си-шный for.
3. Для перебора индексов использовать какой-нибудь генератор вроде IndexesOf из твоего примера и этим ограничиться.
4. Финт: for (int i = 0 to 10) для открытого сверху (0..9), for (int i = 1 upto 10) для закрытого (1..10). Перепутать трудно, визуально длина слов разная. Но с первого взгляда кажется непривычным. Собственно, сами слова "to" и "upto" можно исопльзовать вместо двух и трех точек для конструирования интервалов, не только в конструкции for.
Pavel Dvorkin,
PD>Господа, а не кажется ли Вам, что для того, чтобы сравнивать нынешний программный продукт по числу LOC c тем, что писали 10 лет назад, надо для нынешнего продукта автоматически применять понижающий коэффициент, равный так примерно 10 ?
Чаще всего величина библиотечного кода нас не волнует. Можно хоть на 1024 умножить, главное чтобы потом в своём файле иметь возможность писать
(+/ % #) list
вместо
int n = list.size();
double sum = 0;
for (int i = 0; i < n; i++)
sum += list[i];
return sum/n;
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
LCR>Чаще всего величина библиотечного кода нас не волнует. Можно хоть на 1024 умножить, главное чтобы потом в своём файле иметь возможность писать LCR>(+/ % #) list
LCR>вместо LCR>int n = list.size(); LCR>double sum = 0; LCR>for (int i = 0; i < n; i++) LCR> sum += list[i]; LCR>return sum/n;
Отличная иллюстрация двух диких крайностей
Я предпочитаю золотую середину:
sum = reduce(float.__add__, list, 0.0)
В других языках reduce имеет имя fold или accumulate.
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
Кё>>Я предпочитаю золотую середину: Кё>>sum = reduce(float.__add__, list, 0.0) LCR>Я так полагаю, это Питончик? Да, там кстати считалось среднее
Ну а насчет среднего — не намного сложнее, return reduce(float.__add__, lisT, 0.0)/len(lisT)
Кстати, а как в Nemerle сослаться на оператор + для класса, например, для целых? Пробовал System.Int32.op_Add и System.Int32.operator+, в документации не знаю где искать.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>do while применяется так редко не поэтому, а потому что ситуации, когда необходимо выполнить тело цикла один раз обязательно, встречаются намного реже, чем ситуации, когда это не требуется.
Это зависит от того, как мыслит человек. Например, у меня большая часть циклов вообще полуторные: одна часть цикла должна выполняться на одну итерацию больше, чем другая. А чистые do и while, как вырожденные случаи, встречаются реже.
VD>Понимаш ли в чем дело? С-шный for никто не отменяет. Но его гибкость выливается в ошибки и усложнение чтения кода когда он используется для простых вещей.
VD>Ведь если так мыслить, то даже самая приметивная программа станет архи-сложной.
VD>По этому за конструкцией "for (int i = 0; i < array.Length; i++)" ты улавливашь некий паттерн. В данном случае "перебрать все индексы массива array". Но этот паттерн тебе приходится угадывать. А тут есть две проблемы. 1. Ты можешь не врено принять за этот паттерн просто похожую конструкцию, например, "for (int i = 0; i <= array.Length; i++)" или "for (int i = 0; i < array.Length; i--)". 2. Разглядывать паттерны в накромождениях кода очень не просто.
VD>Посему код написанный в таком стиле плохо читается.
Эта сложность служит барьером от случайных людей. Можно избавить программистов от проблем на этом, весьма низком уровне. Однако, если человек не может научиться преодолевать подобные препядствия, то польза от него, как программиста, довольно сомнительна. Оперирование паттернами более высокого уровня, в действительности, намного сложнее. Если, конечно, речь не идёт о "Copy" и "Paste" фрагментов кода из документации и др. источников.
Здравствуйте, vdimas, Вы писали: V>Несчет неудобства array.Length-1 ничего сказать не могу, лично мне безразлично. Если кому-то важно, то можно оппробовать разные формы записи для открытых и закрытых диапазонов. Я бы предложил так: V>
[0..array.Length),
Поскольку итерация в массиве — очень частая штука, имеет смысл сделать ее отдельной операцией. В дотнете уже придумано отличное решение — в большинстве случаев мне совсем не надо париться с индексом благодаря встроенному foreach:
int[] a = new int[] {1, 2, 3, 4};
int sum = 0;
foreach(int item in a)
sum += item;
В тех редких случаях, когда мне все же нужен именно индекс, я бы предпочел вообще отказаться от манипуляций с Length. Оптимально было бы так:
int[] a = new int[] {1, 2, 3, 4};
int weightedSum = 0;
foreach(int i in a.Indexes)
weightedSum += i*a[i];
V>но боюсь, случайная опечатка в парных скобочках будет пересекаться с семантикой, что не есть гут. Может быть будут еще идеи как указать открытый диапазон? А может быть ввести некое ключевое слово, типа before:
Мне кажется, сама проблема использования диапазонов в значительной мере надумана. Во-первых, диапазон настолько часто приделан к коллекции, что проще сделать нарошные операции извлечения диапазона напрямую, чем давать человеку возможность ошибиться при "двухстадийной" схеме — сначала вытаскиваем границы, а потом формируем из них диапазон.
Во-вторых, все эти скобочки экономят буквально одиночные символы, в ущерб понятности и безошибочности. По мне, так гораздо лучше ввести необходимые функции-конструкторы диапазонов:
Range(low, high) // для (1, 4) переберет 1, 2, 3, 4
ExclusiveRange(beforeLow, afterHigh) // для (1, 4) переберет (2, 3)
Since(low) // для (1) будет перебирать 1, 2, 3, ... до бесконечности
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, VladD2, Вы писали:
VD>этот синтаксис хорош, но неудобен когда нужно перебрать значения индекса некой коллекции: VD>
VD>foreach (i in 0..array.Length - 1) // не нравится этот "- 1"
VD>
Для полноты картины:
for I := Low(A) to High(A) do;
Я для своих классов в которых нумерация начинается с нуля, ввожу еще одно поле Limit = Length — 1. В этом случае цикл:
for I := 0 to A.Limit do;
Хорошая реализация в MATLAB. Там цикл имеет вид
for i = A
...
end
где i это переменная, а A это массив (рассматривается как одномерный). Поскольку массивы можно определить при помощи оператора ":", то Цикл от 1 до 10 может быть записан как
for i = 1:10
...
end
Цикл от 10 до 1 дожет быть записан как
for i = 1:10:-1
...
end
Распечатать все элементы массива можно просто
for i = A
print(i)
end
Если при этом добавить для классов свойство Range, то получим
Здравствуйте, VladD2, Вы писали:
VD>Мне в голову прили следующие варианты: VD>
VD>foreach (x where 0 >= x < 10 step 1) // "step 1" необязательный параметр позволяющий задать шаг отличный от еденицы
VD>
И чем это лучше кода for (x = 0; x < 10; ++x)?
Почему так широко используется foreach? Потому что полностью соответствует задаче — "последовательно получить элементы коллекции". For задаче не соответствует, т.к. задача требует получения элементов, а не индексов и не содержит ни условия для отбора индексов, ни шага изменения индекса.
Ты же рассматриваешь другую задачу, а именно — "получить индексы в диапазоне от 0 до 10 с шагом 1". В чем здесь заключается несоответсвие for'а задаче? For работает именно с индексами, в нем задается диапазон и шаг, задавать еще что-то не надо. For для такой задачи по меньшей мере не хуже любой другой формы записи, соответственно не понятно стремление к его замене и тем более замене дублирующей.
Здравствуйте, Кодёнок, Вы писали:
Кё>Кстати, а как в Nemerle сослаться на оператор + для класса, например, для целых? Пробовал System.Int32.op_Add и System.Int32.operator+, в документации не знаю где искать.
Должно быть Int32.op_Addition. По крайней мере компилиться, но во время выполнения падает, видимо баг (может уже поправили).