Как я убегаю из цикла в 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
    }
}

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

От Разговора Scala по http://www.slideshare.net/Odersky/fosdem-2009-1013261 2009 года FOSDEM на 22-й странице:

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

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

266
задан bjb568 2 November 2014 в 12:35
поделиться

4 ответа

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

Предположим, вы хотите суммировать числа, пока общая сумма не превысит 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) Throw исключение.

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 на самом деле не являются обычными циклами - это вызовы методов, у которых есть собственный цикл, или они являются рекурсией, которая может быть, а может и не быть циклом - и хотя они действуют , и трудно придумать последовательный способ узнать, что делать «break» и тому подобное. Итак, чтобы быть последовательным, разумнее вообще не делать «перерыв».

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

363
ответ дан 23 November 2019 в 02:24
поделиться

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

Scala 2.8, однако, включает способ нарушить

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

6
ответ дан 23 November 2019 в 02:24
поделиться

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

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

      var sum = 0 {{1} } для (i <- от 0 до 1000; если сумма <1000) sum + = i 
     
13
ответ дан 23 November 2019 в 02:24
поделиться

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

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 дополнительных реверса. :) Более математический подход мог бы быть более элегантным.

2
ответ дан 23 November 2019 в 02:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: