Por que Scala tem retorno, mas não quebra e continua

22

Scala não possui breakou continue, portanto, alguns comportamentos de loop exigem um pouco mais de reflexão.

O encerramento antecipado de um loop requer recursão final , exceções ou scala.util.control.Breaks(que usa exceções).

A lógica para isso é que, assim goto, são construções de fluxo que obscurecem o fluxo e podem ser realizadas de maneiras melhores e menos surpreendentes.

Mas parece que esses mesmos argumentos poderiam ser usados return.

Por que Scala deliberadamente omitiu breake continue, mas não return?

Paul Draper
fonte
1
Eu posso imaginar que os autores da linguagem considerem a recursão da cauda como a maneira de construir a iteração. Eu posso imaginar isso breake continuepreciso de algumas máquinas de limpeza adicionais. OTOH returné uma maneira de finalizar ordenadamente uma função e qualquer máquina de limpeza já está lá de qualquer maneira.
9000
1
breakable { for { break; } }apenas uma reflexão tardia, e provavelmente longe de ser eficiente.
Joop Eggen
Porque com funções, na verdade não há razão para isso. É o mesmo em python. Cada vez que você usa um loop for com break, você pode escrever uma função, colocar seu loop na função e usar return. Não consigo pensar em uma situação em que essa não seja uma boa idéia em relação ao código limpo. Para o desempenho, uma limpeza pode ser melhor, mas o desempenho não tem a maior prioridade no scala.
valenterry
2
Esta pergunta parece que tem uma resposta aqui: stackoverflow.com/questions/3770989/...
Michael Shaw
3
@PaulDraper: a resposta breake continueestá contida na sua pergunta e no link da sua pergunta. A pergunta returné exatamente sobre o que eu vinculei e foi respondida, pelo menos na resposta mais votada e aceita. Se as duas respostas juntas não responderem à sua pergunta, talvez você possa editá-la para esclarecê-la.
Michael Shaw

Respostas:

16

Quebrar e continuar:

Em uma palestra sobre Scala , Martin Odersky deu três razões para não incluir uma pausa ou continuar no slide 22:

  • Eles são um pouco imperativos; melhor usar muitas funções menores.
  • Questões sobre como interagir com fechamentos.
  • Eles não são necessários!

E ele então diz: "Nós podemos apoiá-los exclusivamente nas bibliotecas". No slide 23, ele fornece o código que implementa break. Embora eu não conheça bem o Scala o suficiente para ter certeza, parece que o pequeno trecho desse slide é tudo o que é necessário para implementar breake que continuepode ser implementado em código igualmente curto.

Ser capaz de implementar coisas como essas em bibliotecas simplifica a linguagem principal.

Em 'Programming in Scala, Second Edition', de Martin Odersky, Lex Spoon e Bill Venners, a seguinte explicação é dada:

Você deve ter notado que não houve menção a breakou continue. Scala deixa de fora esses comandos porque eles não combinam bem com literais de função ... É claro o que continuesignifica dentro de um whileloop, mas o que significaria dentro de uma literal de função? ... Existem muitas maneiras de programar sem breake continue, e se você tirar proveito das literais de função, essas alternativas geralmente podem ser mais curtas que o código original.

Retorna:

Retornos podem ser considerados um pouco imperativos em estilo, já que retorno é um verbo, um comando para fazer alguma coisa. Mas eles também podem ser vistos de uma maneira puramente funcional / declarativa: eles definem qual é o valor de retorno da função (mesmo que, em uma função com retornos múltiplos, apenas cada um dê uma definição parcial).

No mesmo livro, eles dizem o seguinte sobre return:

Na ausência de qualquer returndeclaração explícita , um método Scala retorna o último valor calculado pelo método. O estilo recomendado para métodos é de fato evitar returndeclarações explícitas, e especialmente múltiplas . Em vez disso, pense em cada método como uma expressão que gera um valor, que é retornado.

Os métodos finalizam e retornam um valor, mesmo que uma returninstrução não seja usada, para que não haja problemas com os fechamentos, pois, caso contrário, os fechamentos não funcionariam.

Também não pode haver problema em mesclar bem com literais de função, pois a função precisa retornar um valor de qualquer maneira.

Michael Shaw
fonte
2
Sobre a devolução, não parecem ser alguns perigos leves: tpolecat.github.io/2014/05/09/return.html
bbarker
0

Penso que as respostas anteriores justificam os problemas de definir semântica para breakou continuede uma maneira em todo o idioma para Scala, com contextos relativamente irrestritos.

Escrevi uma pequena biblioteca que define breake continueem um contexto mais restrito: iteração sobre seqüências via Scala para compreensão. Ao focar nesse contexto, acredito que a semântica se torna inequívoca e fácil de raciocinar.

A biblioteca está disponível aqui: https://github.com/erikerlandson/breakable

Aqui está um exemplo simples de como ele se parece no código:

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))
eje
fonte