Как мне выйти из цикла в Scala?

Как разорвать петлю?

var largest=0
for(i<-999 to 1 by -1) {
    for (j<-i to 1 by -1) {
        val product=i*j
        if (largest>product)
            // I want to break out here
        else
           if(product.toString.equals(product.toString.reverse))
              largest=largest max product
    }
}

Как превратить вложенные циклы for в хвостовую рекурсию?

Из Scala Talk на FOSDEM 2009 http://www.slideshare.net/Odersky/fosdem-2009-1013261 на 22-й странице:

Прерывание и продолжение В Scala их нет. Почему? Они немного императивны; лучше использовать много меньших функций. Проблема, как взаимодействовать с замыканиями. Они не нужны!

Какое объяснение?


person TiansHUo    schedule 30.04.2010    source источник
comment
Для вашего сравнения нужен второй знак равенства: if (product.toString == product.toString.reverse) или, возможно, вызов метода equals.   -  person user unknown    schedule 30.04.2010
comment
да, я пропустил это, когда набирал его   -  person TiansHUo    schedule 04.05.2010
comment
Я знаю, что воскрешаю старый вопрос, но мне хотелось бы знать, какова цель этого кода? Сначала я подумал, что вы пытались найти наибольший возможный продукт палиндрома с заданными комбинациями i и j. Если этот код выполняется до завершения без выхода из цикла, результатом будет 906609, но при преждевременном выходе из цикла результатом будет 90909, поэтому выход из цикла не делает код более эффективным, поскольку он изменяет результат.   -  person Ryan H.    schedule 20.07.2018


Ответы (19)


У вас есть три (или около того) варианта выхода из петель.

Предположим, вы хотите суммировать числа, пока общая сумма не превысит 1000. Вы пытаетесь

var sum = 0
for (i <- 0 to 1000) sum += i

за исключением того, что вы хотите остановиться, когда (сумма> 1000).

Что делать? Есть несколько вариантов.

(1a) Используйте конструкцию, включающую тестируемое вами условие.

var sum = 0
(0 to 1000).iterator.takeWhile(_ => sum < 1000).foreach(i => sum+=i)

(предупреждение - это зависит от деталей того, как тест takeWhile и foreach чередуются во время оценки, и, вероятно, не должны использоваться на практике!).

(1b) Используйте хвостовую рекурсию вместо цикла for, воспользовавшись тем, насколько легко написать новый метод в Scala:

var sum = 0
def addTo(i: Int, max: Int) {
  sum += i; if (sum < max) addTo(i+1,max)
}
addTo(0,1000)

(1c) Вернитесь к использованию цикла while

var sum = 0
var i = 0
while (i <= 1000 && sum <= 1000) { sum += 1; i += 1 }

(2) Вывести исключение.

object AllDone extends Exception { }
var sum = 0
try {
  for (i <- 0 to 1000) { sum += i; if (sum>=1000) throw AllDone }
} catch {
  case AllDone =>
}

(2a) В Scala 2.8+ это уже предварительно упаковано в scala.util.control.Breaks с использованием синтаксиса, который очень похож на ваш знакомый старый перерыв от C / Java:

import scala.util.control.Breaks._
var sum = 0
breakable { for (i <- 0 to 1000) {
  sum += i
  if (sum >= 1000) break
} }

(3) Поместите код в метод и используйте return.

var sum = 0
def findSum { for (i <- 0 to 1000) { sum += i; if (sum>=1000) return } }
findSum

Это намеренно сделано не слишком легко по крайней мере по трем причинам, о которых я могу думать. Во-первых, в больших блоках кода легко упустить из виду операторы continue и break, или подумать, что вы выходите из большего или меньшего количества, чем вы есть на самом деле, или вам нужно разорвать два цикла, которые вы не можете сделать. в любом случае легко - так что стандартное использование, хотя и удобно, имеет свои проблемы, и поэтому вы должны попытаться структурировать свой код по-другому. Во-вторых, в Scala есть всевозможные вложенности, которые вы, вероятно, даже не замечаете, поэтому, если бы вы могли вырваться из чего-то, вы, вероятно, были бы удивлены тем, где закончился поток кода (особенно с замыканиями). В-третьих, большинство «циклов» Scala на самом деле не являются обычными циклами - это вызовы методов, которые имеют собственный цикл, или они являются рекурсией, которая может быть, а может и не быть циклом - и хотя они act < / em> looplike, трудно придумать последовательный способ узнать, что должно делать "break" и т.п. Итак, чтобы быть последовательным, разумнее вообще не делать «перерыв».

Примечание. Существуют функциональные эквиваленты всего этого, в которых вы возвращаете значение sum, а не изменяете его на месте. Это более идиоматическая Scala. Однако логика осталась прежней. (return становится return x и т. Д.).

person Rex Kerr    schedule 30.04.2010
comment
Есть еще один. Сделайте это методом и return явно. - person Daniel C. Sobral; 01.05.2010
comment
@ Дэниел: Хм, разве (3) это не покрывает? - person Rex Kerr; 01.05.2010
comment
Я думал о return sum, а не только о return, но я думаю, что это так. - person Daniel C. Sobral; 01.05.2010
comment
@rex kerr, 1a медленный, потому что память для потока инициализирована, особенно если это во внутреннем цикле 1b - это то, о чем я думал, но я вижу, что мне понадобятся две хвостовые рекурсии, и это не очень хорошо для ясности на все 2 и 3 медленнее, чем прямые петли - person TiansHUo; 04.05.2010
comment
Вы можете использовать .iterator (.elements в 2.7) вместо .toStream, чтобы не сохранять вещи в памяти. Я приводил примеры каждого, не обязательно указывая самый быстрый для вашего конкретного случая. Каковы ваши ориентиры для (2) и (3)? Насколько они медленнее? - person Rex Kerr; 04.05.2010
comment
Честно говоря, никаких тестов я не делал. Но я хотел бы отметить, что любые накладные расходы во внутреннем цикле должны быть устранены. - person TiansHUo; 06.05.2010
comment
@TiansHUo: Действительно, должно. Вы понимаете, сколько накладных расходов возникает, если вы пишете for (i <- 1 to 1000000) { ... } вместо использования цикла while? И цикл while является частным случаем (1a) - просто проверьте какое-нибудь другое условие, чтобы прервать его раньше. - person Rex Kerr; 06.05.2010
comment
@Rex Керр, на самом деле я не знаю, почему накладные расходы на цикл for? - person TiansHUo; 07.05.2010
comment
@TiansHUo: Потому что for транслируется в вызов foreach (возможно, с другими вызовами по пути), и все это полностью универсально, что не так быстро, как увеличение примитивного счетчика. Если вы действительно хотите, чтобы код был быстрым, используйте var i=0; while (i<1000) { i+=1; ... }. Однако, за исключением внутренних циклов, нет особых причин быть быстрыми, поэтому лучше использовать более мощный / гибкий / удобный подход. - person Rex Kerr; 07.05.2010
comment
@kerr, что ?! Как ты это узнал? - person TiansHUo; 09.05.2010
comment
@TiansHUo: Я прочитал «Программирование на Scala» Одерски и др., Где описан цикл for, и протестировал различные циклы, чтобы увидеть, как они работают. - person Rex Kerr; 09.05.2010
comment
Это в некоторой степени связано с этим вопросом: http://stackoverflow.com/questions/3770989/purpose-of-return-statement-in-scala. - person Jus12; 28.02.2011
comment
В отношении исключений, хотя это строго верно, что вы можете вызвать исключение, это, возможно, злоупотребление механизмом исключения (см. Эффективная Java). Исключения действительно указывают на ситуации, которые являются действительно неожиданными и / или требуют радикального выхода из кода, то есть ошибок какого-либо рода. Помимо этого, они определенно были довольно медленными (не уверен в текущей ситуации), потому что у JVM мало причин для их оптимизации. - person Jonathan; 30.05.2011
comment
@Jonathan - Исключения работают медленно, только если вам нужно вычислить трассировку стека - обратите внимание, как я создал статическое исключение для генерации вместо генерации его на лету! И они вполне допустимая конструкция управления; они используются во многих местах библиотеки Scala, поскольку на самом деле это единственный способ возврата с помощью нескольких методов (что, если у вас есть куча замыканий, вам иногда нужно делать). - person Rex Kerr; 30.05.2011
comment
Для меня лучшее решение - хвостовая рекурсия, так здорово! - person Galder Zamarreño; 03.08.2011
comment
В рекурсивном методе addTo кажется более понятным поместить sum в качестве параметра и заставить метод возвращать Int, а не Unit, поэтому вам не нужно использовать изменяемые vars. Номер (4) для списка может заключаться в замене for-computing на эквивалентные циклы while с соответствующими условиями остановки. - person Luigi Plinge; 20.08.2011
comment
@macias - Тогда используйте breakable. - person Rex Kerr; 20.01.2012
comment
@Rex Kerr, нет, если я не ошибаюсь, это основано на исключениях. Я думаю, что внутренняя функция на лету - лучший подход. - person greenoldman; 20.01.2012
comment
@macias - Что плохого в использовании исключений как части вашего механизма потока управления? У каждого механизма есть свои сильные и слабые стороны. - person Rex Kerr; 20.01.2012
comment
@Rex Керр, вы указываете на слабые места конструкции break (я с ними не согласен), но затем вы предлагаете использовать исключения для нормального рабочего процесса! Выход из цикла не является исключительным случаем, это часть алгоритма, это не случай записи в несуществующий файл (например). Короче говоря, предлагаемое лекарство хуже самой болезни. И когда я думаю о том, чтобы выбросить настоящее исключение в breakable раздел ... и все эти обручи, чтобы избежать зла break, хм ;-) Согласитесь, жизнь иронична. - person greenoldman; 23.01.2012
comment
@macias - Извините, моя ошибка. JVM использует Throwables для потока управления. Лучше? Тот факт, что они обычно используются для поддержки обработки исключений, не означает, что они могут использоваться только для обработки исключений. Возврат в определенное место из замыкания - это точно так же, как выброс исключения с точки зрения потока управления. Поэтому неудивительно, что используется именно этот механизм. - person Rex Kerr; 24.01.2012
comment
@Rex Kerr, я остановлюсь на этом, потому что это все больше и больше похоже на личный чат (теперь мне придется задаться вопросом, являются ли Java и JVM чем-то с проверенным качеством дизайна). Я совершенно не уверен, но пока мы не найдем лучшее место для обсуждения этого, давайте остановимся, хорошо? - person greenoldman; 24.01.2012
comment
@RexKerr Что ж, ты меня убедил. Обычно я бы возражал против Исключений для нормального выполнения программы, но здесь не действуют две основные причины. К ним относятся: (1) они медленные [не при таком использовании] и (2) они предлагают исключительное поведение для тех, кто читает ваш код [не, если ваша библиотека позволяет вам вызывать их break] Если это похоже на break и он работает как break, насколько я понимаю, это break. - person Tim Goodman; 24.01.2014
comment
Правильно ли я понимаю, что return внутри for тела скомпилирован в throw new SomeException, а конструкция for помещается в try-catch? Потому что я не вижу другого способа, как это можно реализовать. - person sasha.sochka; 13.04.2014
comment
Я сам нашел ответ - мое понимание было правильным. Вместо return создается исключение: scala.runtime.NonLocalReturnControl - person sasha.sochka; 13.04.2014
comment
Из этих вариантов только 1a и 1b существенно отличаются от «break». Пример 2 использует исключение точно так же, как и break, только с дополнительным шаблоном для определения класса исключения. В примере 3 «return» используется точно так же, как и «break», только с дополнительным шаблоном для определения замыкания. Если в середине цикла легко упустить из виду «break» или «continue», почему слова «return» и «throw» каким-то волшебным образом отличаются? - person Josh Milthorpe; 06.04.2015
comment
Я думаю, что для программирования процедур лучше использовать цикл while - person Cyanny; 09.11.2016

Это изменилось в Scala 2.8, в котором есть механизм для использования разрывов. Теперь вы можете сделать следующее:

import scala.util.control.Breaks._
var largest = 0
// pass a function to the breakable method
breakable { 
    for (i<-999 to 1  by -1; j <- i to 1 by -1) {
        val product = i * j
        if (largest > product) {
            break  // BREAK!!
        }
        else if (product.toString.equals(product.toString.reverse)) {
            largest = largest max product
        }
    }
}
person hohonuuli    schedule 28.02.2011
comment
Использует ли это скрытые исключения? - person Mike; 31.05.2011
comment
Это использование Scala в качестве процедурного языка, игнорирующего преимущества функционального программирования (т.е. хвостовую рекурсию). Не очень. - person Galder Zamarreño; 03.08.2011
comment
Майк: Да, Scala генерирует исключение для выхода из цикла. Галдер: Это отвечает на заданный вопрос Как мне выйти из цикла в Scala ?. «Красиво» это или нет - не имеет значения. - person hohonuuli; 07.10.2011
comment
@hohonuuli, значит, он в блоке try-catch, он не сломается, верно? - person greenoldman; 20.01.2012
comment
@macias, верно. Если вы окружите оператор break оператором try / catch, он не выйдет из цикла. (Я это проверил). Для любопытных: исходный код Breaks находится по адресу scala-lang.org/api/current/index.html#scala.util.control.Breaks - person hohonuuli; 20.01.2012
comment
Вы можете отнести это к категории дырявых абстракций. Немного грустно, но это цена, которую приходится платить за работу на виртуальной машине, которая не была написана для поддержки конструкций scala. Опять же, вы не должны ловить все бросаемые объекты. Рекомендуемый способ написать правильный обработчик catch all - использовать NonFatal, который не случайно не перехватит NonLocalReturnControl (исключение, используемое внутри при выполнении return изнутри анонимной функции), ни BreakControl (тот, который используется для реализации конструкции break) - person Régis Jean-Gilles; 08.06.2015
comment
@Galder Zamarreño Почему хвостовая рекурсия дает преимущество в этом случае? Разве это не просто оптимизация (чье приложение скрыто для новичка и запутанно применено для опытных). Есть ли в этом примере преимущества хвостовой рекурсии? - person user48956; 26.04.2016

Выход из цикла for никогда не является хорошей идеей. Если вы используете цикл for, это означает, что вы знаете, сколько раз вы хотите выполнить итерацию. Используйте цикл while с двумя условиями.

Например

var done = false
while (i <= length && !done) {
  if (sum > 1000) {
     done = true
  }
}
person Keith Blanchard    schedule 01.08.2014
comment
Это то, что я считаю правильным способом избавиться от циклов в Scala. Что-то не так с этим ответом? (учитывая небольшое количество голосов за). - person Jus12; 18.09.2015
comment
действительно простой и более читаемый. даже хрупкая штуковина правильная, она выглядит некрасиво и имеет проблемы с внутренним try-catch. Хотя ваше решение не работает с foreach, я проголосую за вас, учитывая простоту. - person yerlilbilgin; 25.02.2016

Чтобы добавить Рекса Керра, ответьте другим способом:

  • (1c) Вы также можете использовать охранник в своем цикле:

     var sum = 0
     for (i <- 0 to 1000 ; if sum<1000) sum += i
    
person Patrick    schedule 30.04.2010
comment
Я не включил это в качестве опции, потому что на самом деле он не прерывает цикл - он проходит через все, но оператор if не работает на каждой итерации после того, как сумма достаточно высока, поэтому он выполняет только один оператор if стоит работы каждый раз. К сожалению, в зависимости от того, как вы написали цикл, это может потребовать много работы. - person Rex Kerr; 30.04.2010
comment
@RexKerr: А компилятор все равно не оптимизирует его? Не будет ли он оптимизирован, если не во время первого запуска, то во время JIT. - person Maciej Piechotka; 01.01.2012
comment
@MaciejPiechotka - JIT-компилятор обычно не содержит достаточно сложной логики, чтобы распознать, что оператор if для изменяющейся переменной всегда (в этой особой ситуации) возвращает false и, следовательно, может быть опущен. - person Rex Kerr; 01.01.2012

Подход, который генерирует значения в диапазоне по мере того, как мы выполняем итерацию, вплоть до условия разрыва, вместо того, чтобы сначала генерировать весь диапазон, а затем повторять его, используя Iterator, (вдохновленный использованием @RexKerr Stream)

var sum = 0
for ( i <- Iterator.from(1).takeWhile( _ => sum < 1000) ) sum += i
person elm    schedule 12.02.2015
comment
Да, мне это нравится. нет, извините, я думаю, это выглядит лучше. - person ses; 16.01.2017

Поскольку в Scala еще нет break, вы можете попытаться решить эту проблему с помощью оператора return. Поэтому вам нужно поместить свой внутренний цикл в функцию, иначе при возврате будет пропущен весь цикл.

Однако в Scala 2.8 есть способ сломать

http://www.scala-lang.org/api/rc/scala/util/control/Breaks.html

person Ham Vocke    schedule 30.04.2010
comment
извините, но я хотел вырвать только внутренний цикл. Вы не имеете в виду, что я должен поместить это в функцию? - person TiansHUo; 30.04.2010
comment
Извините, я должен был это прояснить. Конечно, использование возврата означает, что вам нужно инкапсулировать цикл в функцию. Я отредактировал свой ответ. - person Ham Vocke; 30.04.2010
comment
Это совсем не хорошо. Похоже, что Scala не любит вложенные циклы. - person TiansHUo; 30.04.2010
comment
По-видимому, другого пути нет. Возможно, вы захотите взглянуть на это: scala-lang.org/node/257 - person Ham Vocke; 30.04.2010
comment
@TiansHUo: Почему вы говорите, что Scala не любит вложенные циклы? У вас те же проблемы, если вы пытаетесь выйти из одиночного цикла. - person Rex Kerr; 30.04.2010

// import following package
import scala.util.control._

// create a Breaks object as follows
val loop = new Breaks;

// Keep the loop inside breakable as follows
loop.breakable{
// Loop will go here
for(...){
   ....
   // Break will go here
   loop.break;
   }
}

используйте модуль Break http://www.tutorialspoint.com/scala/scala_break_statement.htm

person user1836270    schedule 10.03.2014

Просто используйте цикл while:

var (i, sum) = (0, 0)
while (sum < 1000) {
  sum += i
  i += 1
}
person pathikrit    schedule 21.04.2014

Вот хвостовая рекурсивная версия. По сравнению с for-computing, надо признать, что это немного загадочно, но я бы сказал, что это функционально :)

def run(start:Int) = {
  @tailrec
  def tr(i:Int, largest:Int):Int = tr1(i, i, largest) match {
    case x if i > 1 => tr(i-1, x)
    case _ => largest
  }

  @tailrec
  def tr1(i:Int,j:Int, largest:Int):Int = i*j match {
    case x if x < largest || j < 2 => largest
    case x if x.toString.equals(x.toString.reverse) => tr1(i, j-1, x)
    case _ => tr1(i, j-1, largest)
  }

  tr(start, 0)
}

Как видите, функция tr является эквивалентом внешнего for-complation, а tr1 - внутреннего. Пожалуйста, если знаете способ оптимизировать мою версию.

person fresskoma    schedule 06.06.2011

Просто мы можем сделать в scala это

scala> import util.control.Breaks._

scala> object TestBreak {
       def main(args : Array[String]) {
         breakable {
           for (i <- 1 to 10) {
             println(i)
             if (i == 5)
               break;
       } } } }

вывод:

scala> TestBreak.main(Array())
1
2
3
4
5
person Viraj Wadate    schedule 20.07.2018

Близко к вашему решению будет следующее:

var largest = 0
for (i <- 999 to 1 by -1;
  j <- i to 1 by -1;
  product = i * j;
  if (largest <= product && product.toString.reverse.equals (product.toString.reverse.reverse)))
    largest = product

println (largest)

J-итерация выполняется без новой области видимости, а генерация продукта и условие выполняются в операторе for (не очень хорошее выражение - я не могу найти лучшего). Условие меняется на обратное, что довольно быстро для такого размера задачи - возможно, вы получите что-то с перерывом для больших циклов.

String.reverse неявно преобразуется в RichString, поэтому я делаю 2 дополнительных реверса. :) Более математический подход мог бы быть более элегантным.

person user unknown    schedule 18.05.2010

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

object awhile {
def apply(condition: () => Boolean, action: () => breakwhen): Unit = {
    while (condition()) {
        action() match {
            case breakwhen(true)    => return ;
            case _                  => { };
        }
    }
}
case class breakwhen(break:Boolean);

используйте это так:

var i = 0
awhile(() => i < 20, () => {
    i = i + 1
    breakwhen(i == 5)
});
println(i)

если не хочешь ломаться:

awhile(() => i < 20, () => {
    i = i + 1
    breakwhen(false)
});
person Mohamad Alallan    schedule 21.01.2016

Сторонний пакет breakable - одна из возможных альтернатив

https://github.com/erikerlandson/breakable

Пример кода:

scala> import com.manyangled.breakable._
import com.manyangled.breakable._

scala> val bkb2 = for {
     |   (x, xLab) <- Stream.from(0).breakable   // create breakable sequence with a method
     |   (y, yLab) <- breakable(Stream.from(0))  // create with a function
     |   if (x % 2 == 1) continue(xLab)          // continue to next in outer "x" loop
     |   if (y % 2 == 0) continue(yLab)          // continue to next in inner "y" loop
     |   if (x > 10) break(xLab)                 // break the outer "x" loop
     |   if (y > x) break(yLab)                  // break the inner "y" loop
     | } yield (x, y)
bkb2: com.manyangled.breakable.Breakable[(Int, Int)] = com.manyangled.breakable.Breakable@34dc53d2

scala> bkb2.toVector
res0: Vector[(Int, Int)] = Vector((2,1), (4,1), (4,3), (6,1), (6,3), (6,5), (8,1), (8,3), (8,5), (8,7), (10,1), (10,3), (10,5), (10,7), (10,9))
person eje    schedule 05.03.2017

import scala.util.control._

object demo_brk_963 
{
   def main(args: Array[String]) 
   {
      var a = 0;
      var b = 0;
      val numList1 = List(1,2,3,4,5,6,7,8,9,10);
      val numList2 = List(11,12,13);

      val outer = new Breaks; //object for break
      val inner = new Breaks; //object for break

      outer.breakable // Outer Block
      {
         for( a <- numList1)
         {
            println( "Value of a: " + a);

            inner.breakable // Inner Block
            {
               for( b <- numList2)
               {
                  println( "Value of b: " + b);

                  if( b == 12 )
                  {
                      println( "break-INNER;");
                       inner.break;
                  }
               }
            } // inner breakable
            if( a == 6 )
            {
                println( "break-OUTER;");
                outer.break;
            }
         }
      } // outer breakable.
   }
}

Базовый метод разрыва цикла с использованием класса Breaks. Объявив петлю разрывной.

person Parvathirajan Natarajan    schedule 10.05.2018

По иронии судьбы, разрыв Scala в scala.util.control.Breaks является исключением:

def break(): Nothing = { throw breakException }

Лучший совет: НЕ используйте break, continue и goto! ИМО они такие же, плохая практика и злой источник всех видов проблем (и горячих дискуссий) и, наконец, «считаются вредными». Блок кода структурирован, также в этом примере разрывы излишни. Наш Эдсгер В. Дейкстра † писал:

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

person Epicurist    schedule 23.06.2014

У меня такая ситуация, как в приведенном ниже коде

 for(id<-0 to 99) {
    try {
      var symbol = ctx.read("$.stocks[" + id + "].symbol").toString
      var name = ctx.read("$.stocks[" + id + "].name").toString
      stocklist(symbol) = name
    }catch {
      case ex: com.jayway.jsonpath.PathNotFoundException=>{break}
    }
  }

Я использую java lib, и механизм заключается в том, что ctx.read выдает исключение, когда ничего не может найти. Я оказался в ловушке ситуации, когда: я должен разорвать цикл, когда было сгенерировано исключение, но scala.util.control.Breaks.break использует Exception для разрыва цикла, и он был в блоке catch, поэтому он был пойман.

У меня есть уродливый способ решить эту проблему: сделать цикл в первый раз и подсчитать реальную длину. и используйте его для второго цикла.

вырваться из Scala не так уж и хорошо, когда вы используете некоторые java-библиотеки.

person Lucas Liu    schedule 08.10.2015

Умное использование метода find для сбора поможет вам.

var largest = 0
lazy val ij =
  for (i <- 999 to 1 by -1; j <- i to 1 by -1) yield (i, j)

val largest_ij = ij.find { case(i,j) =>
  val product = i * j
  if (product.toString == product.toString.reverse)
    largest = largest max product
  largest > product
}

println(largest_ij.get)
println(largest)
person AlvaPan    schedule 26.02.2016

Ниже приведен код для простого разрыва цикла

import scala.util.control.Breaks.break

object RecurringCharacter {
  def main(args: Array[String]) {
    val str = "nileshshinde";

    for (i <- 0 to str.length() - 1) {
      for (j <- i + 1 to str.length() - 1) {

        if (str(i) == str(j)) {
          println("First Repeted Character " + str(i))
          break()     //break method will exit the loop with an Exception "Exception in thread "main" scala.util.control.BreakControl"

        }
      }
    }
  }
}
person Nilesh Shinde    schedule 16.12.2018

Я не знаю, насколько изменился стиль Scala за последние 9 лет, но мне показалось интересным, что в большинстве существующих ответов используется vars, или трудночитаемая рекурсия. Ключ к раннему выходу - использовать ленивую коллекцию для генерации возможных кандидатов, а затем проверять условие отдельно. Для создания продуктов:

val products = for {
  i <- (999 to 1 by -1).view
  j <- (i to 1 by -1).view
} yield (i*j)

Затем, чтобы найти первый палиндром из этого представления без создания каждой комбинации:

val palindromes = products filter {p => p.toString == p.toString.reverse}
palindromes.head

Чтобы найти самый большой палиндром (хотя лень вам мало, потому что вам все равно придется проверять весь список):

palindromes.max

Ваш исходный код фактически проверяет первый палиндром, который больше, чем последующий продукт, что аналогично проверке первого палиндрома, за исключением странного граничного условия, которое, я не думаю, вы намеревались. Продукты не строго монотонно убывают. Например, 998*998 больше 999*997, но появляется намного позже в циклах.

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

person Karl Bielefeldt    schedule 15.08.2019