Полезные альтернативные структуры управления?

Иногда, когда я программирую, я обнаруживаю, что какая-то конкретная структура управления была бы мне очень полезна, но недоступна напрямую в моем языке программирования. Я думаю, что мое самое распространенное желание — это что-то вроде «разделить время» (я понятия не имею, как это назвать):

{
    foo();
} split_while( condition ) {
    bar();
}

Семантика этого кода будет заключаться в том, что foo() всегда запускается, а затем проверяется условие. Если true, то запускается bar() и мы возвращаемся к первому блоку (таким образом снова запускается foo() и т. д.). Благодаря комментарию пользователя Reddit zxqdms я узнал, что Дональд Э. Кнут пишет об этой структуре в своей статье "Структурное программирование с go to заявления" (см. стр. 279).

Какие альтернативные структуры управления, по вашему мнению, являются полезным способом организации вычислений?

Моя цель здесь — дать себе и другим новые способы мышления о структурировании кода, чтобы улучшить разбиение на фрагменты и рассуждения.

Примечание: я не спрашиваю о том, как обобщить все возможные управляющие структуры, будь то с помощью jne, if/goto, макросов Лиспа, продолжений, монад, комбинаторов, кварков, или что-то еще. Я спрашиваю, какие специализации полезны при описании кода.


person A. Rex    schedule 27.11.2010    source источник
comment
Re: ваш пример. Если части undo предназначены только для примера, то то, что у вас есть, не очень альтернативная структура управления. Он называется обработка исключений и существует (хотя и не обязательно под этим названием) почти столько же, сколько и само программирование. Если вы действительно хотите, чтобы undo были частью семантики, тогда это транзакция и не очень альтернатива.   -  person Jörg W Mittag    schedule 28.11.2010
comment
Примечание для запутавшихся читателей: я отредактировал свой вопрос, чтобы подчеркнуть свой вопрос и уменьшить акцент на конкретных примерах. В частности, я удалил один пример, на который ссылается предыдущий комментарий.   -  person A. Rex    schedule 28.11.2010
comment
Должна быть вики сообщества, чтобы избежать закрытия из-за субъективности (нет реального ответа).   -  person Matchu    schedule 28.11.2010
comment
Большой список, я нахожу это..., в общем-то спекулятивным, потому что вопрос касается управляющих структур, нереализованных (или малоизвестных). Этот вопрос на самом деле не падает на землю где-то конкретно, но он вызывает подозрение множеством разных способов. И 5 часов ответов посвящены способам избежать необходимости в новых структурах управления или способам создания примера из существующих... Я собираюсь использовать NaRQ.   -  person dmckee --- ex-moderator kitten    schedule 28.11.2010
comment
Для split_while в Sather ты можешь сделать loop statement1; while!(condition); statement2; end;   -  person Jordão    schedule 28.11.2010
comment
Между прочим, такая структура управления (splitWhile) может быть довольно легко реализована в Scala: paste.pocoo.org/show /297023 :-)   -  person missingfaktor    schedule 28.11.2010
comment
В Clojure еще проще: gist.github.com/718750 :D   -  person Rayne    schedule 28.11.2010
comment
Я отправил аналогичный вопрос на Programmers.Se: programmers.stackexchange.com/q/22070/389   -  person Maniero    schedule 28.11.2010
comment
@bigown: Спасибо. Надеюсь, это приведет к новым ответам, если вопрос не будет снова открыт здесь.   -  person A. Rex    schedule 28.11.2010


Ответы (28)


Одним из довольно распространенных является бесконечный цикл. Я хотел бы написать это так:

forever {
  // ...
}
person Jordão    schedule 28.11.2010
comment
#define ever (;;) навсегда {} - person Jookia; 28.11.2010
comment
@Джукия: #define forever for(;;) - person Jordão; 28.11.2010
comment
Я бы предпочел повторять бесконечно, но, возможно, это разные понятия. - person Peter Gibson; 28.11.2010
comment
@Peter Gibson: обычно вы repeat что-то until какое-то условие, как в Паскаль. - person Jordão; 28.11.2010
comment
@ Jordão: навсегда - это долго - повторение подразумевает, что в какой-то момент вы можете вырваться. Это было бы похоже на повтор... до (ложь) - где вы оставляете бит до (ложь), потому что это уродливо :) - person Peter Gibson; 28.11.2010
comment
Навсегда намерение, но я согласен; repeat, или даже loop, тоже получится. - person Jordão; 28.11.2010
comment
Скала: def forever(f: => Unit) { while(true) f }. - person missingfaktor; 28.11.2010
comment
@missingfaktor: я все еще хочу использовать break и continue. Для вашего кода break превращается в return; но continue не адресовано. - person Jordão; 28.11.2010
comment
@Jordão: в Scala нет ни одной из этих управляющих конструкций — break и continue. Это в первую очередь функциональный язык, и императивные функции, хотя и доступны, обычно не рекомендуются. По многочисленным просьбам недавно предоставили break (правда, в виде библиотечного метода). У нас до сих пор нет continue (и я не думаю, что у нас когда-нибудь будет, поскольку вам вряд ли нужно писать циклы, свернутые вручную, на функциональном языке.) - person missingfaktor; 28.11.2010
comment
@Jordão: и о да, вы можете использовать эту библиотеку break в управляющей конструкции forever, которую я определил в моем предпоследнем комментарии выше. - person missingfaktor; 28.11.2010
comment
forever определенно намного красивее, чем while(true)! - person Sean Patrick Floyd; 29.11.2010
comment
В QBASIC от Microsoft был Do...Loop без условий, который выполнялся до тех пор, пока не выполнялся Exit Do. VB.Net предоставляет такое же заявление. По иронии судьбы, то, что мне нужно в VB, - это оператор управления, чтобы выразить намерение, просто сделайте следующее один раз (иногда, чтобы разрешить прорыв; чаще, чтобы определить область действия переменной). - person supercat; 02.12.2010
comment
@supercat: я часто использовал для этого вложенные области видимости в C# (но выхода нет); но это общее указание на то, что вам нужно реорганизовать некоторый код для нового метода. Чтобы сделать это один раз, вы можете использовать конструкцию do { ... } while(false), но семантически once { ... } будет лучше. Может быть, вы можете добавить это к этому вопросу. Я также думал о конструкции never { ... } (или dont { ... }) как о способе, поддерживаемом компилятором, для комментирования некоторого кода. Но я не думал о многих преимуществах, чтобы добавить это к этому вопросу. Что вы думаете? - person Jordão; 02.12.2010
comment
@ Jordão: мне не особенно нравится слово «один раз», но я не уверен, что было бы лучше. Может быть, просто Block для языка с парами слов, такого как VB, чтобы его можно было использовать в паре с End Block. Для производных C можно использовать фигурные скобки без какого-либо ключевого слова, если не нужна семантика перехода к концу, хотя может быть неплохо иметь некоторый маркер того, что пара фигурных скобок существует для области видимости (вместо того, чтобы разграничивать, если это случайно удалил). - person supercat; 02.12.2010
comment
@supercat: имеет смысл, тогда добавьте это к этому вопросу. - person Jordão; 02.12.2010

Иногда мне нужно иметь цикл foreach с индексом. Можно было бы написать так:

foreach (index i) (var item in list) {
  // ...
}

(Мне не особенно нравится этот синтаксис, но вы поняли идею)

person Jordão    schedule 28.11.2010
comment
В python это обрабатывается с помощью встроенной функции перечисления, которая принимает список и возвращает список пар (индекс, элемент). для i, элемент перечисления (список). - person Peter Gibson; 28.11.2010
comment
Определенно! Я часто писал цикл foreach, но потом понял, что мне нужен индекс позже. - person A. Rex; 28.11.2010
comment
В Ruby мы используем Array#each_with_index - person Tianyi Cui; 28.11.2010
comment
В гавани у вас есть item:__enumIndex, чтобы получить его. Кроме того, вы можете использовать item:__enumIsFirst и item:__enumIsLast, которые возвращают логическое значение. Стоит отметить, что Harbour также допускает нисходящий foreach и foreach с несколькими массивами одновременно. По убыванию очень удобно удалять элементы во время цикла без проблем с синхронизацией. - person Bacco; 07.01.2017

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

string result = "";
for (int i = 0; i < items.Count; i++) {
    result += items[i];
    if (i < items.Count - 1) result += ", "; // This is gross.
    // What if I can't access items by index?
    // I have off-by-one errors *every* time I do this.
}

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

string result = "";
foreach (var item in items) {
    result += item;
} between {
    result += ", ";
}
person munificent    schedule 28.11.2010
comment
Мне это нравится, но будет ли «элемент» находиться в области действия во время блока «между», и если да, то это предыдущий или следующий элемент? Если бы я хотел превратить что-то вроде [a, 1, b, 2] в a=1,b=2, тогда мне нужно чередовать = и ,. Как мне получить индекс цикла в блоке между? Или мне нужна нелокальная переменная? - person Andrew Dalke; 28.11.2010
comment
Было бы неплохо. split_while решает эту проблему, но только для while, а не для других распространенных циклов. - person A. Rex; 28.11.2010
comment
@Andrew, вы всегда можете указать как предыдущий, так и следующий элементы в цикле: between(prev, next) {. - person munificent; 28.11.2010
comment
В Haskell использование intersperse не будет рутиной, имхо. - person LennyStackOverflow; 30.11.2010
comment
@ Андрей, в этом весь смысл. Блок between будет выполняться только между двумя элементами, а не после последнего. - person munificent; 08.12.2010
comment
@minificent: ба, вот что происходит, когда я возвращаюсь к теме SO после нескольких месяцев отсутствия. - person Andrew Dalke; 08.12.2010
comment
вы не должны зацикливаться на этом, но join(array, ',') - person Chii; 20.01.2011
comment
@Chii, что делает join в этом примере? - person Ehtesh Choudhury; 06.06.2012
comment
@Shurane: объединение в этом примере предназначено для функции, которая принимает массив и объединяет каждый элемент, разделенный вторым аргументом (в данном случае ,). это встроено в большинство языков, например, в javascript Array.join. - person Chii; 07.06.2012

Цикл с другим:

while (condition) {
  // ...
}
else {
  // the else runs if the loop didn't run
}
person Jordão    schedule 28.11.2010
comment
Если цикл запускается, foo не запускается в первый раз, а только в конце. Это противоположно тому, что он описал. - person Rayne; 28.11.2010
comment
Python имеет именно такую ​​конструкцию. Кроме того, он позволяет добавлять «else:» к «for:» и «try:». - person 9000; 28.11.2010
comment
В Python else запускается, если цикл завершился нормально, либо он выполняется без перерыва, либо никогда не запускался. - person Brian Neal; 28.11.2010
comment
+1, но для меня это в основном желание for(){...} else {...} - person tstenner; 08.12.2010
comment
@tstenner: я включаю это в свой вопрос, когда говорю «Цикл с другим». Просто я привел пример цикла while. Но да, идея состоит в том, чтобы иметь любой тип цикла с другим. - person Jordão; 08.12.2010

{
    foo();
} split_while( condition ) {
    bar();
}

Вы можете сделать это довольно легко, используя обычный while:

while (true) {
    foo();
    if (!condition) break;
    bar();
}

Я делаю это довольно часто теперь, когда преодолела свое иррациональное отвращение к break.

person munificent    schedule 27.11.2010
comment
Это правда, но я чувствую, что это не решает вопрос. Меня не особенно волнует, как вы решите реализовать эти альтернативные структуры управления, будь то с while и break или (вздох!) даже с goto. Мне нужны способы осмысления кода. Я обнаружил, что split_while позволяет мне по-новому взглянуть на структуру, аналогично тому, как while является структурным улучшением по сравнению с условным переходом. - person A. Rex; 28.11.2010
comment
Это не отвечает на вопрос ОП. - person Maniero; 28.11.2010
comment
Ppl не читает вопрос. Странно видеть, что этот ответ получил наибольшее количество голосов. - person Maniero; 28.11.2010
comment
Возможно, неявный смысл этого ответа в том, что управляющих структур уже достаточно — зачем добавлять для них дополнительную языковую поддержку, когда у вас уже есть достаточно хорошая абстракция? - person GreenieMeanie; 03.12.2010

Если вы посмотрите на Haskell, хотя там есть специальный синтаксис для различных структур управления, поток управления часто захватывается типами. Наиболее распространенными типами таких элементов управления являются монады, стрелки и аппликативные функторы. Итак, если вам нужен особый тип потока управления, это обычно какая-то функция более высокого порядка, и вы можете либо написать ее самостоятельно, либо найти ее в базе данных пакетов Haskells (Hackage), которая довольно велика.

Такие функции обычно находятся в пространстве имен Control, где вы можете найти модули для параллельного выполнения с обработкой ошибок. Многие управляющие структуры, обычно встречающиеся в процедурных языках, имеют аналог функции в Control.Monad, среди них циклы и операторы if. If-else — это выражение с ключевым словом в Haskell, если без else не имеет смысла в выражении, но имеет смысл в монаде, поэтому операторы if без else захватываются функциями when и unless.

Другим распространенным случаем является выполнение операции со списком в более общем контексте. Функциональные языки очень любят fold, а специализированные версии, такие как map и filter. Если у вас есть монада, то для нее есть естественное расширение fold. Это называется foldM, и поэтому существуют также расширения любой специализированной версии fold, о которой вы только можете подумать, например, mapM и filterM.

person HaskellElephant    schedule 27.11.2010

Это просто общая идея и синтаксис:

if (cond)
   //do something
else (cond)
   //do something
also (cond)
   //do something
else
   //do something
end

ТАКЖЕ условие всегда оценивается. ELSE работает как обычно.

Это работает и для случая. Вероятно, это хороший способ исключить оператор break:

case (exp)
   also (const)
      //do something
   else (const)
      //do something
   also (const)
      //do something
   else
      //do something
end

можно прочитать как:

switch (exp)
   case (const)
      //do something
   case (const)
      //do something
      break
   case (const)
      //do something
   default
      //do something
end

Я не знаю, полезно ли это или просто для чтения, но это пример.

person Maniero    schedule 28.11.2010

С макросами (в стиле lisp), хвостовыми вызовами и продолжениями все это выглядит причудливо.

С помощью макросов, если стандартных конструкций потока управления недостаточно для данного приложения, программист может написать свои собственные (и многое другое). Для реализации конструкций, которые вы привели в качестве примера, потребуется только простой макрос.

С помощью хвостовых вызовов можно разложить сложные шаблоны потока управления (например, реализацию конечного автомата) на функции.

Продолжения — это мощный примитив потока управления (try/catch — их ограниченная версия). В сочетании с хвостовыми вызовами и макросами сложные шаблоны потока управления (возврат, синтаксический анализ и т. д.) становятся простыми. Кроме того, они полезны в веб-программировании, так как с их помощью можно инвертировать инверсию управления; у вас может быть функция, которая запрашивает у пользователя некоторый ввод, выполняет некоторую обработку, запрашивает у пользователя дополнительный ввод и т. д.

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

person Brian    schedule 27.11.2010
comment
На самом деле я почти уверен, что макросы + хвостовые вызовы + продолжения с разделителями (+, возможно, изменяемое состояние) действительно являются универсальной основой для всех возможных управляющих структур. Я так думаю, потому что монада Cont — мать всего :) - person jkff; 28.11.2010
comment
Это по-прежнему оставляет открытым вопрос о том, какие структуры управления вы бы реализовали. Хорошо, отлично, макросы Лиспа обобщают все структуры управления, которые мне нужны; хорошо, отлично, может быть, они не должны быть на основном языке. Это по-прежнему не отвечает на вопрос, какую специализацию я выбираю. Мы исторически обнаружили, что if и while могут быть полезны — что еще? - person A. Rex; 28.11.2010
comment
@А. Рекс: на самом деле, я не могу вспомнить, когда в последний раз использовал цикл while вне какого-то очень особого контекста. Я полагаю, что в последний раз это было в написанном от руки анализаторе рекурсивного спуска, где я использовал его больше как элемент DSL, чем как реальный цикл. Я обычно использую карты, складки, катаморфизмы, фильтры, трансформеры. Я даже не так часто использую простые итераторы. В современном языке почти единственная причина использования цикла while — это когда вам нужен бесконечный цикл, например, в цикле событий операционной системы. Но для этого вы также можете использовать хвостовую рекурсивную функцию. - person Jörg W Mittag; 28.11.2010
comment
Придирка: есть документ, который доказывает, что исключения могут быть более мощными, чем продолжения. (На самом деле, одним из распространенных критических замечаний по поводу исключений является то, что они просто GOTO под другим именем.) Таким образом, с технической точки зрения продолжения являются ограниченной версией try/catch. Но это ерунда самого худшего вида :-) Я прекрасно понимаю, что вы имеете в виду. Однако это может быть полезно: компилятор Volta CIL-›ECMAScript использует исключения, например, для реализации продолжений и потоков. - person Jörg W Mittag; 28.11.2010

если не:

unless (condition) {
  // ...
}

в то время как не:

until (condition) {
  // ...
}
person Jordão    schedule 28.11.2010
comment
Мне потребовалось некоторое время, чтобы привыкнуть к ним (в Perl), но теперь я чувствую, что использую их изначально. - person A. Rex; 28.11.2010
comment
Скала: def unless(cond: => Boolean)(f: => Unit) { if(!cond) f } и def until(cond: => Boolean)(f: => Unit) { while(!cond) f }. :D - person missingfaktor; 28.11.2010
comment
Я на самом деле не люблю их в Perl. Я пытался их использовать, но обнаружил, что они запутывают код, а не делают его более понятным. Может быть, потому, что в других языках обычно не используются такие конструкции... - person Inshallah; 30.11.2010
comment
Чтобы пояснить, что я пытаюсь сказать: в других языках вместо них обычно используются if (!..) и while (!..), что делает их гораздо более идоматическими в разных языках. - person Inshallah; 30.11.2010
comment
Я думаю, что эти конструкции могут сделать намерение более ясным: сравните, например, while(!done) с until(done). - person Jordão; 30.11.2010
comment
@ Jordão, это тонкая проблема - мне нужно один раз посмотреть на if (!..), дважды на unless (..) и тысячу раз на остальные. - person Inshallah; 30.11.2010

Помеченные циклы — это то, чего мне иногда не хватает в основных языках. например.,

int i, j;
for outer ( i = 0; i < M; ++i )
    for ( j = 0; j < N; ++j )
        if ( l1[ i ] == l2[ j ] )
           break outer;

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

(Бонус: иногда мне хотелось бы иметь redo вместе с continue и break. Он возвращался к началу цикла без оценки приращения.)

person Boojum    schedule 28.11.2010
comment
Вы также можете эмулировать многоуровневый перерыв и продолжить с исключением. Это не самое элегантное решение. Однажды мне пришлось прибегнуть к нему. С другой стороны, это работает, даже если самый внутренний цикл был извлечен в другую функцию. - person Andrew Dalke; 28.11.2010
comment
Для справки, JavaScript и Java имеют это. - person Nicole; 30.11.2010
comment
+1 за повтор. Я обнаружил, что хочу этого, особенно при обходе и вставке/удалении только несколько раз в списке. (В С++, то есть не знаю, можно ли это сделать хорошо в другом) - person Macke; 04.12.2010

Я предлагаю оператор "тогда". Он возвращает левый операнд на первой итерации и правый операнд на всех остальных итерациях:

var result = "";
foreach (var item in items) {
    result += "" then ", ";
    result += item;
}

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

person helium    schedule 28.11.2010
comment
А как насчет нового блока, разрешенного в любом месте внутри цикла for... может быть, другого... first { ... } else { result += ", "; } - person Nicole; 30.11.2010

if (cond)
   //do something
else (cond)
   //do something
else (cond)
   //do something
first
   //do something
then
   //do something
else (cond)
   //do something
else
   //do something
end

Блоки FIRST и THEN запускаются, если любое из 3 условий оценивается как истинное. Блок FIRST запускается перед условным блоком, а THEN запускается после запуска условного блока.

ELSE условная или окончательная запись, следующая за оператором FIRST и THEN, не зависит от этих блоков.

Он может читаться как:

if (cond)
   first()
   //do something
   then()
else (cond)
   first()
   //do something
   then()
else (cond)
   first()
   //do something
   then()
else (cond)
   //do something
else
   //do something
end


function first()
   //do something
return
function then()
   //do something
return

Эти функции — просто форма для чтения. Они не создали бы размаха. Это больше похоже на gosub/возврат из Basic.

Полезность и удобочитаемость как предмет обсуждения.

person Maniero    schedule 28.11.2010

Как насчет

alternate {
    statement 1,
    statement 2,
    [statement 3,...]
}

для циклического перебора доступных операторов при каждом последующем проходе.

Изменить: тривиальные примеры

table_row_color = alternate(RED, GREEN, BLUE);

player_color = alternate(color_list); // cycles through list items

alternate(
    led_on(),
    led_off()
);

Редактировать 2. В третьем примере выше синтаксис может быть немного запутанным, так как он выглядит как функция. Фактически, на каждом проходе оценивается только один оператор, а не оба. Лучший синтаксис может быть чем-то вроде

alternate {
    led_on();
}
then {
    led_off();
}

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

person Peter Gibson    schedule 28.11.2010
comment
Это интересно. Можете ли вы привести пример использования? - person A. Rex; 28.11.2010
comment
Как это управляющая конструкция? - person missingfaktor; 28.11.2010
comment
@missingfaktor: Потому что управление меняется после каждого раза через alter(). Если они реализованы как функция (в большинстве языков), то led_on() и led_off() будут вызываться перед вызовом альтернативы. Его можно эмулировать с помощью лямбда-выражений и нелокальной переменной для хранения состояния, но это дополнительные сложности. С другой стороны, это кажется редкой необходимостью и не стоит иметь специальный синтаксис. В конце концов, легко реализовать что-то похожее на Python с помощью itertools.cycle(). - person Andrew Dalke; 28.11.2010
comment
+1 за мысль. Я думаю, это может сделать то, что вы предлагаете... def alter(*args): в то время как True: for arg in args: yield arg() itertools.cycle(), вероятно, библиотека, которая делает что-то подобное, но это заставило меня задуматься. про генераторы и сопрограммы! Редактировать: форматирование ужасное - person David Ackerman; 28.11.2010
comment
Вы можете добиться такого рода вещей, используя только функции с замыканиями. Например, в JavaScript вы можете написать такой код: function cycle(choices) { var i = 0; return function() { var current = i; я = (я + 1) % вариантов.длина; вернуть выбор[текущий]; }; } Вы можете использовать этот код следующим образом: var colors = cycle([синий, белый, красный]); for (var i = 0; i ‹ 5; ++i) alert(colors()); Если вы используете Python, эта функция уже есть в itertools, а если вы используете Haskell, вы можете найти ее в Prelude. - person gnuvince; 28.11.2010
comment
В Clojure есть такая функция, и поскольку последовательности clojure ленивы, вы можете использовать ее для создания списков любого размера. (навскидку я не знаю названия функции, поэтому в качестве примера буду использовать альтернативу): (взять 4 (отбросить 1 (альтернативная :красная :зеленая :синяя))) сгенерирует последовательность [:зеленая :синяя :красная :зеленый] - person ; 04.12.2010

защита прицела D – это полезная структура контроля, которую нечасто можно увидеть. .

person helium    schedule 28.11.2010
comment
Они похожи на defer в языке Go. - person jkff; 03.12.2010

Думаю, мне следует упомянуть CityScript (язык сценариев CityDesk), который некоторые действительно причудливые циклические конструкции.

Из файла справки:

{$ forEach n var in (condition) sort-order $}
... text which appears for each item ....
{$ between $}
.. text which appears between each two items ....
{$ odd $}
.. text which appears for every other item, including the first ....
{$ even $}
.. text which appears for every other item, starting with the second ....
{$ else $}
.. text which appears if there are no items matching condition ....
{$ before $}
..text which appears before the loop, only if there are items matching condition
{$ after $}
..text which appears after the loop, only of there are items matching condition
{$ next $}
person Danko Durbić    schedule 03.12.2010

Также обратите внимание, что многие управляющие структуры приобретают новое значение в монадическом контексте, в зависимости от конкретной монады - посмотрите на mapM, filterM, whileM, sequence и т. д. в Haskell.

person jkff    schedule 27.11.2010

ignoring — игнорировать исключения, возникающие в определенном блоке кода.

try {
  foo()
} catch {
  case ex: SomeException => /* ignore */
  case ex: SomeOtherException => /* ignore */
}

С управляющей конструкцией ignoring вы могли бы записать ее более кратко и читабельно так:

ignoring(classOf[SomeException], classOf[SomeOtherException]) {
  foo()
}

[ Scala предоставляет это (и многие другие конструкции управления обработкой исключений) в своей стандартной библиотеке в пакете util.control. ]

person missingfaktor    schedule 28.11.2010
comment
Игнорирование исключения обычно похоже на проглатывание ошибки. Что не очень хорошо для большинства приложений. - person Jordão; 28.11.2010
comment
@Jordão: бывают случаи, когда вам может понадобиться сделать это, и в таких случаях эта управляющая конструкция действительно делает код чище. - person missingfaktor; 28.11.2010
comment
По крайней мере, программное обеспечение должно отметить, что оно проигнорировало определенное исключение. Также может быть хорошей идеей добавить некоторую директиву в класс исключений, указывающую, что его можно игнорировать, чтобы предотвратить злоупотребление этой функцией. - person cthulhu; 29.11.2010

Я хотел бы видеть ключевое слово для группировки вывода. Вместо этого:

        int lastValue = 0;

        foreach (var val in dataSource)
        {
            if (lastValue != val.CustomerID)
            {                    
                WriteFooter(lastValue);
                WriteHeader(val);
                lastValue = val.CustomerID;
            }
            WriteRow(val);
        }
        if (lastValue != 0)
        {
            WriteFooter(lastValue);
        }

как насчет такого:

        foreach(var val in dataSource)
        groupon(val.CustomerID)
        {            
            startgroup
            {
                WriteHeader(val);
            }
            endgroup
            {
                WriteFooter(val)
            }
        }
        each
        {
            WriteRow(val);
        }

Если у вас есть достойная платформа, элементы управления и/или форматирование отчетов, вам не нужно писать этот код. Но удивительно, как часто я ловлю себя на том, что делаю это. Самая раздражающая часть — это нижний колонтитул после последней итерации — это сложно сделать в реальном примере без дублирования кода.

person Paul Keister    schedule 03.12.2010
comment
+1 Я пропустил это некоторое время назад, когда работал в VB6. Кроме того, ABAP имеет это. - person Jordão; 03.12.2010
comment
Очень интересно, это та специализация, о которой я думаю. At First и At Last для всей итерации — приятный штрих. - person Paul Keister; 04.12.2010
comment
В Harbour вы можете использовать item:__enumIsFirst и item:__enumIsLast, чтобы вернуть логическое значение, если вы находитесь на первом и последнем элементе. - person Bacco; 07.01.2017

Что-то, что заменяет

bool found = false;
for (int i = 0; i < N; i++) {
  if (hasProperty(A[i])) {
    found = true;
    DoSomething(A[i]);
    break;
  }
}
if (!found) {
  ...
}

как

for (int i = 0; i < N; i++) {
  if (hasProperty(A[i])) {
    DoSomething(A[i]);
    break;
  }
} ifnotinterrupted {
  ...
}

Я всегда чувствую, что должен быть лучший способ, чем введение флага, просто для выполнения чего-то после последнего (обычного) выполнения тела цикла. Можно было бы проверить !(i < N), но i выходит за рамки после цикла.

person Meinersbur    schedule 03.12.2010
comment
Мне очень нравится что-то вроде ifnotinterrupted. - person A. Rex; 03.12.2010
comment
В Python есть следующее: for и while имеют (необязательный) блок else, который запускается только в том случае, если они завершаются естественным образом (т. е. без break). - person Grant Paul; 07.12.2010
comment
@chpwn: Вау, кажется, я неправильно истолковал else после циклов в Python (я никогда его не использовал); Спасибо что сказал мне. Тем не менее, я думаю, что это неправильное название. - person Meinersbur; 07.12.2010

Это немного шутка, но вы можете получить желаемое поведение следующим образом:

#include <iostream>
#include <cstdlib>

int main (int argc, char *argv[])
{
  int N = std::strtol(argv[1], 0, 10); // Danger!
  int state = 0;
  switch (state%2) // Similar to Duff's device.
  {
    do {
      case 1: std::cout << (2*state) << " B" << std::endl;
      case 0: std::cout << (2*state+1) << " A" << std::endl; ++state;
    } while (state <= N);
      default: break;
  }

  return 0;
}

p.s. форматировать это было немного сложно, и я определенно не доволен этим; однако emacs делает еще хуже. Кто-нибудь хочет попробовать vim?

person thechao    schedule 28.11.2010
comment
Это не ответ на вопрос ОП. - person Maniero; 28.11.2010

Генераторы в Python действительно новы, если вы в основном работали с нефункциональными языками. В более общем смысле: продолжения, сопрограммы, ленивые списки.

person Paul Harrison    schedule 28.11.2010

Это, вероятно, не считается, но в Python я был расстроен отсутствием цикла do.

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

person Tom Ritter    schedule 28.11.2010
comment
Есть документ, который доказывает, что исключения могут быть более мощными, чем продолжения. Продолжения по своей выразительной силе практически равны GOTO. Итак, если в вашем языке есть исключения, то GOTO. - person Jörg W Mittag; 03.12.2010
comment
@Jörg: Не могли бы вы опубликовать ссылку на статью? - person missingfaktor; 03.12.2010
comment
@missingfaktor: Название статьи: [ Непроверенные исключения могут быть строго более мощными, чем Call/CC ](Lambda-the-Ultimate.Org/node/2966). Обратите внимание, что, как всегда с научной статьей, нужно очень внимательно читать что она на самом деле является статьей, доказывающей. В данном конкретном случае они показывают, что добавление исключений в стиле SML к просто типизированному λ-исчислению (которое, как известно, не является полным по Тьюрингу) делает его полным по Тьюрингу, в то время как добавление call/cc к системе Fω (которая, как известно, является строгое надмножество STλC) не работает. (Единственный способ сделать это TC нарушает целостность типов.) - person Jörg W Mittag; 03.12.2010
comment
Это не просто академический трюк, кстати. Я знаю по крайней мере один компилятор, который использовал исключения ECMAScript для реализации других конструкций потока управления: Volta Project Microsoft Live Labs был (помимо прочего) компилятором, который компилировал произвольный байт-код CIL в исходный код ECMAScript. CIL (и, в более общем смысле, .NET и C#) имеет все виды управления потоком, которых нет в ECMAScript: GOTO, сопрограммы, потоки (!), и все они были реализованы с использованием исключений ECMAScript. - person Jörg W Mittag; 03.12.2010
comment
Также обратите внимание, что: Может быть более мощным, чем не обязательно означает, что может использоваться человеком. В частности, я считаю, что для использования таких исключений, как GOTO, вам придется переписать свой код в стиле передачи продолжения, а затем закодировать свой стиль передачи продолжения в исключения. - person Jörg W Mittag; 03.12.2010

for int i := 0 [down]to UpperBound() [step 2]

Отсутствует во всех языках, производных от C.

Пожалуйста, подумайте, прежде чем голосовать или писать комментарий:
Это не лишнее для for (int i = 0; i <= UpperBound(); i++), оно имеет другую семантику:

  1. UpperBound() оценивается только один раз

  2. Случай UpperBound() == MAX_INT не приводит к бесконечному циклу

person Meinersbur    schedule 03.12.2010

Это похоже на ответ @Paul Keister.

(бормочет, бормочет) много лет назад приложение, над которым я работал, имело множество вариаций так называемой обработки прерывания управления — всей той логики, которая используется для разбиения отсортированных строк данных на группы и подгруппы с верхними и нижними колонтитулами. Поскольку приложение было написано на LISP, мы уловили распространенные идиомы в макросе WITH-CONTROL-BREAKS. Если бы я транспонировал этот синтаксис в популярную волнистую форму, это могло бы выглядеть примерно так:

withControlBreaks (x, y, z : readSortedRecords()) {
  first (x) :     { emitHeader(x); subcount = 0; }
  first (x, y) :  { emitSubheader(x, y); zTotal = 0; }
  all (x, y, z) : { emitDetail(x, y, z); ztotal += z; }
  last (x, y) :   { emitSubfooter(x, y, zTotal); ++subCount; }
  last (x) :      { emitFooter(x, subcount); }
}

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

person WReach    schedule 12.12.2010

Как насчет диапазонов циклов for в стиле PL/I? Эквивалент VB будет:

' Counts 1, 2, ... 49, 50, 23, 999, 998, ..., 991, 990
  For I = 1 to 50, 23, 999 to 990 Step -1

Наиболее распространенное использование, которое я вижу, это запустить цикл для списка индексов, а затем добавить еще один. Кстати, использование For-Each также может быть удобно:

' Bar1, Bar2, Bar3 are an IEnum(Wazoo); Boz is a Wazoo
  For Each Foo as Wazoo in Bar1, Bar2, Enumerable.One(Boz), Bar3

Это запустит цикл для всех элементов в Bar1, всех элементов в Bar2, Boz и Bar3. Linq, вероятно, позволит это без особых трудностей, но встроенная поддержка языка может быть немного более эффективной.

person supercat    schedule 02.12.2010

Одной из управляющих структур, недоступной во многих языках, является структура типа case-in. Подобно структуре типа переключателя, она позволяет вам иметь аккуратно отформатированный список возможных параметров, но соответствует первому истинному (а не первому, который соответствует вводу). LISP такого типа (у которого он есть):

(cond
   ((evenp a) a)        ;if a is even return a
   ((> a 7) (/ a 2))    ;else if a is bigger than 7 return a/2
   ((< a 5) (- a 1))    ;else if a is smaller than 5 return a-1
   (t 17))              ;else return 17

Или, для тех, кто предпочитает более C-подобный формат

cond 
    (a % 2 == 0): 
        a;     break;
    (a > 7):
        a / 2; break;
    (a < 5):
        a - 1; break;
    default:
        17;    break;

По сути, это более точное представление конструкции if/elseif/elseif/else, чем переключатель, и оно может оказаться чрезвычайно удобным для выражения этой логики в чистом и удобочитаемом виде.

person RHSeeger    schedule 14.11.2011

Как насчет перебора с движущимся окном (из n элементов вместо 1) по списку? Я думаю, что это косвенно связано с ответом @munificent.

Что-то типа

#python
#sum of adjacent elements
for x,y in pairs(list):
    print x + y

def pairs(l):              
    i=0                    
    while i < len(l)-1:    
        yield (l[i],l[i+1])
        i+=1               

Это полезно для определенных типов вещей. Не поймите меня неправильно, это легко реализовать как функцию, но я думаю, что многие люди пытаются вывести циклы for и while, когда для работы есть более конкретные/описательные инструменты.

person Ehtesh Choudhury    schedule 06.06.2012

person    schedule
comment
Это даже не делает то, что должен делать его slot_while. И настоящий вопрос заключается в том, какие новые высокоуровневые структуры управления вы можете придумать, а не в том, как вы можете их реализовать. - person helium; 28.11.2010