Qual é o rendimento do Scala?

Eu entendo o rendimento de Ruby e Python. O que o rendimento da Scala faz?

Ele é usado em compreensões de sequência (como as compreensões e geradores de lista do Python, onde você também pode usar yield ).

Ele é aplicado em combinação com e grava um novo elemento na seqüência resultante.

Exemplo simples (de scala-lang )

 /** Turn command line arguments to uppercase */ object Main { def main(args: Array[String]) { val res = for (a < - args) yield a.toUpperCase println("Arguments: " + res.toString) } } 

A expressão correspondente em F # seria

 [ for a in args -> a.toUpperCase ] 

ou

 from a in args select a.toUpperCase 

em Linq.

O yield de Ruby tem um efeito diferente.

Eu acho que a resposta aceita é ótima, mas parece que muitas pessoas falharam em entender alguns pontos fundamentais.

Primeiro, as compreensões de Scala são equivalentes à notação de Haskell, e nada mais é do que um açúcar sintático para composição de múltiplas operações monádicas. Como esta afirmação provavelmente não ajudará ninguém que precise de ajuda, vamos tentar novamente… 🙂

Scala’s for comprehensions é o açúcar sintático para composição de múltiplas operações com map, flatMap e filter . Ou foreach . O Scala realmente traduz uma expressão for em chamadas para esses methods, portanto, qualquer class que os forneça, ou um subconjunto deles, pode ser usada para fins de compreensão.

Primeiro, vamos falar sobre as traduções. Existem regras muito simples:

  1. este

     for(x < - c1; y <- c2; z <-c3) {...} 

    é traduzido em

     c1.foreach(x => c2.foreach(y => c3.foreach(z => {...}))) 
  2. este

     for(x < - c1; y <- c2; z <- c3) yield {...} 

    é traduzido em

     c1.flatMap(x => c2.flatMap(y => c3.map(z => {...}))) 
  3. este

     for(x < - c; if cond) yield {...} 

    é traduzido no Scala 2.7 em

     c.filter(x => cond).map(x => {...}) 

    ou, na Scala 2.8, em

     c.withFilter(x => cond).map(x => {...}) 

    com um fallback no primeiro se o método withFilter não estiver disponível, mas o filter estiver. Por favor, consulte a seção abaixo para obter mais informações sobre isso.

  4. este

     for(x < - c; y = ...) yield {...} 

    é traduzido em

     c.map(x => (x, ...)).map((x,y) => {...}) 

Quando você olha de maneira muito simples for compreensões, as alternativas do map / foreach parecem, de fato, melhores. Uma vez que você comece a compor, você pode facilmente se perder entre parênteses e níveis de aninhamento. Quando isso acontece, geralmente as compreensões são muito mais claras.

Vou mostrar um exemplo simples e omitir intencionalmente qualquer explicação. Você pode decidir qual syntax é mais fácil de entender.

 l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length)) 

ou

 for { sl < - l el <- sl if el > 0 } yield el.toString.length 

withFilter

O Scala 2.8 introduziu um método chamado withFilter , cuja principal diferença é que, em vez de retornar uma nova coleção filtrada, ela é filtrada por demanda. O método de filter tem seu comportamento definido com base no rigor da coleção. Para entender melhor, vamos dar uma olhada em alguns Scala 2.7 com List (strict) e Stream (non-strict):

 scala> var found = false found: Boolean = false scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) 1 3 7 9 scala> found = false found: Boolean = false scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) 1 3 

A diferença acontece porque o filter é imediatamente aplicado com List , retornando uma lista de odds - já que found é false . Só então foreach é executado, mas, a essa altura, a mudança found não tem sentido, já que o filter já foi executado.

No caso do Stream , a condição não é aplicada imediatamente. Em vez disso, à medida que cada elemento é solicitado por foreach , o filter testa a condição, o que permite que você o influencie pelo found . Só para deixar claro, aqui está o código de compreensão equivalente:

 for (x < - List.range(1, 10); if x % 2 == 1 && !found) if (x == 5) found = true else println(x) for (x <- Stream.range(1, 10); if x % 2 == 1 && !found) if (x == 5) found = true else println(x) 

Isso causou muitos problemas, porque as pessoas esperavam que o if fosse considerado sob demanda, em vez de ser aplicado a toda a coleção de antemão.

O Scala 2.8 introduziu o withFilter , que é sempre não rigoroso, não importando o rigor da coleção. O exemplo a seguir mostra List com os dois methods no Scala 2.8:

 scala> var found = false found: Boolean = false scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) 1 3 7 9 scala> found = false found: Boolean = false scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) 1 3 

Isso produz o resultado que a maioria das pessoas espera, sem alterar o comportamento do filter . Como uma nota lateral, o Range foi alterado de não-estrito para estrito entre Scala 2.7 e Scala 2.8.

Sim, como Earwicker disse, é praticamente o equivalente ao select do LINQ e tem muito pouco a ver com o yield do Ruby e do Python. Basicamente, onde em C # você escreveria

 from ... select ??? 

em Scala você tem

 for ... yield ??? 

Também é importante entender que for -comprehensions não funcionam apenas com sequências, mas com qualquer tipo que defina certos methods, assim como o LINQ:

  • Se o seu tipo define apenas map , ele permite -expressões consistindo de um único gerador.
  • Se ele define flatMap e map , ele permite expressões que consistem em vários geradores.
  • Se ele define foreach , ele permite -loops sem rendimento (ambos com geradores únicos e múltiplos).
  • Se definir filter , ele permite expressões -filter iniciando com um if na expressão for .

O yield palavras-chave em Scala é simplesmente o açúcar sintático, que pode ser facilmente substituído por um map , como Daniel Sobral já explicou em detalhes.

Por outro lado, o yield é absolutamente enganador se você estiver procurando por geradores (ou continuações) semelhantes aos do Python . Veja este encadeamento SO para mais informações: Qual é a maneira preferida de implementar ‘yield’ no Scala?

A menos que você obtenha uma resposta melhor de um usuário do Scala (o que eu não sou), aqui está o meu entendimento.

Ele aparece apenas como parte de uma expressão que começa com for , que indica como gerar uma nova lista a partir de uma lista existente.

Algo como:

 var doubled = for (n < - original) yield n * 2 

Portanto, há um item de saída para cada input (embora eu acredite que há uma maneira de descartar duplicatas).

Isso é bem diferente das "continuações imperativas" permitidas pelo rendimento em outras linguagens, onde fornece uma maneira de gerar uma lista de qualquer tamanho, de algum código imperativo com quase qualquer estrutura.

(Se você estiver familiarizado com o C #, ele estará mais próximo do operador de select do LINQ do que do yield return ).

Considere o seguinte para compreensão

 val A = for (i < - Int.MinValue to Int.MaxValue; if i > 3) yield i 

Pode ser útil ler em voz alta como segue

Para cada inteiro i , se for maior que 3 , então produza (produza) i e adicione-o à lista A

Em termos de notação matemática de conjunto-construtor , o acima para compreensão é análogo ao

conjunto de notação

que pode ser lido como

Para cada inteiro Eu , se for maior que 3 , então é um membro do conjunto UMA

ou alternativamente como

UMA é o conjunto de todos os inteiros Eu , de tal forma que cada Eu é melhor que 3

 val aList = List( 1,2,3,4,5 ) val res3 = for ( al < - aList if al > 3 ) yield al + 1 val res4 = aList.filter(_ > 3).map(_ + 1) println( res3 ) println( res4 ) 

Esses dois pedaços de código são equivalentes.

 val res3 = for (al < - aList) yield al + 1 > 3 val res4 = aList.map( _+ 1 > 3 ) println( res3 ) println( res4 ) 

Esses dois pedaços de código também são equivalentes.

O mapa é tão flexível quanto o rendimento e vice-versa.

O rendimento é semelhante ao loop que possui um buffer que não podemos ver e, para cada incremento, ele continua adicionando o próximo item ao buffer. Quando o loop for terminar a execução, ele retornará a coleta de todos os valores gerados. O rendimento pode ser usado como operadores aritméticos simples ou até mesmo em combinação com matrizes. Aqui estão dois exemplos simples para sua melhor compreensão

 scala>for (i < - 1 to 5) yield i * 3 

res: scala.collection.immutable.IndexedSeq [Int] = Vetor (3, 6, 9, 12, 15)

 scala> val nums = Seq(1,2,3) nums: Seq[Int] = List(1, 2, 3) scala> val letters = Seq('a', 'b', 'c') letters: Seq[Char] = List(a, b, c) scala> val res = for { | n < - nums | c <- letters | } yield (n, c) 

res: Seq [(Int, Char)] = Lista ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), ( 3, a), (3, b), (3, c))

Espero que isto ajude!!

o rendimento é mais flexível que o map (), veja o exemplo abaixo

 val aList = List( 1,2,3,4,5 ) val res3 = for ( al < - aList if al > 3 ) yield al + 1 val res4 = aList.map( _+ 1 > 3 ) println( res3 ) println( res4 ) 

rendimento irá imprimir resultado como: Lista (5, 6), o que é bom

while map () retornará o resultado como: List (false, false, true, true, true), o que provavelmente não é o que você pretende.