Вырваться из нескольких циклов for на Цейлоне

Допустим, у меня есть несколько вложенных циклов for на Цейлоне. Как вырваться из всех петель:

variable Integer? something = null;
for (i in 0:3) {
  for (j in 0:3) {
    for (k in 0:3) {
      something = someFunction(i,j,k);
      if (something exists) {
        // break out of everything, we found it
      }
    }
  }
}

person drhagen    schedule 23.01.2016    source источник


Ответы (3)


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

Integer? doLoops() {
  for (i in 0:3) {
    for (j in 0:3) {
      for (k in 0:3) {
        Integer? something = someFunction(i,j,k);
        if (something exists) {
          return something;
        }
      }
    }
  }
  return null;
}
Integer? something = doLoops();

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

person drhagen    schedule 23.01.2016

Вместо этого вы можете использовать синтаксис понимания и потоковой обработки:

Iterable<Integer?> stream = {
    for (i in 0:3) for (j in 0:3) for (k in 0:3) someFunction(i,j,k)
};

Integer? = stream.coalesced.first;

Это будет работать, как и ожидалось, потому что понимание { for ... } создает Iterable, которое оценивается лениво.

Атрибут coalesced итерируемого объекта возвращает еще один ленивый итерируемый объект, который пропускает все значения null в оригинале.

Затем просто запросите первое значение оставшейся итерации, и вот ваш ответ.

person Roland Tepp    schedule 25.01.2016

Другой способ сделать это — использовать блоки else в циклах for:

shared void run() {
    variable Integer? x = null;
    for (i in 0:3) {
        for (j in 0:3) {
            for (k in 0:3) {
                value n = i*j*k;
                if (n > 18) {
                    x = n;
                    break;
                } else {
                    continue;
                }
            } else {
                continue;
            }
            break;
        } else {
            continue;
        }
        break;
    } else {
        x = null;
    }
    print(x);
}

В общем место

else {
    continue;
}
break;

после каждой закрывающей скобки for.

(Примечание. В идеале присваиваемая переменная — x в моем примере, something в вашем — не обязательно должна быть variable, поскольку она назначается ровно один раз. Однако в настоящее время проверка типов не может этого доказать.)

Как это работает? else после цикла for выполняется, если этот цикл завершился без каких-либо break. В этом случае мы также хотим continue выполнить внешний цикл; в противном случае — то есть, если мы вышли из внутреннего цикла — мы хотим break также и из внешнего.

Это можно было бы написать более кратко, используя следующий синтаксис, предложенный в ceylon/ceylon#3223:

for (a in b) {
    for (x in y) {
        if (something) {
            break;
        }
    } then { // opposite of else: runs iff there was a break in the inner loop
        break;
    }
}

Я бы не рекомендовал это решение по трем причинам:

  • Это еще менее читабельно, чем ваше решение для закрытия. Функция for {} else {} довольно неясна, и ее значение совершенно неочевидно, если вы с ней не знакомы, что делает код непонятным для случайного читателя.
  • Он сильно раздувает код в каждом окружающем цикле (не только один раз), что также делает его нечитаемым.
  • Если вы хотите иметь код во внешних циклах после внутренних циклов, вам придется продублировать его: блок else и код после него нуждаются в копии.
person Lucas Werkmeister    schedule 23.01.2016