Respostas:
É usado em compreensões de sequência (como compreensão de lista e geradores do Python, onde você também pode usar yield).
É aplicado em combinação com fore grava um novo elemento na sequê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.
Ruby yieldtem um efeito diferente.
Eu acho que a resposta aceita é ótima, mas parece que muitas pessoas não conseguiram entender alguns pontos fundamentais.
Primeiro, as forcompreensões de Scala são equivalentes às de Haskelldo notação , e nada mais é do que um açúcar sintático para a composição de múltiplas operações monádicas. Como esta declaração provavelmente não ajudará quem precisa de ajuda, vamos tentar novamente ... :-)
As forcompreensões de Scala são açúcar sintático para composição de múltiplas operações com mapa flatMape filter. Or foreach. O Scala, na verdade, traduz uma forexpressão-em chamadas para esses métodos, para que qualquer classe que os forneça, ou um subconjunto deles, possa ser usada para compreensão.
Primeiro, vamos falar sobre as traduções. Existem regras muito simples:
este
for(x <- c1; y <- c2; z <-c3) {...}
é traduzido para
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))este
for(x <- c1; y <- c2; z <- c3) yield {...}
é traduzido para
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))este
for(x <- c; if cond) yield {...}
é traduzido no Scala 2.7 para
c.filter(x => cond).map(x => {...})
ou, no Scala 2.8, em
c.withFilter(x => cond).map(x => {...})
com um fallback para o antigo se o método withFilternão estiver disponível, mas filterestiver. Consulte a seção abaixo para obter mais informações sobre isso.
este
for(x <- c; y = ...) yield {...}
é traduzido para
c.map(x => (x, ...)).map((x,y) => {...})Quando você olha para forcompreensões muito simples , as alternativas map/ foreachparecem, de fato, melhores. Depois de começar a compor, você pode facilmente se perder nos níveis de parênteses e aninhamento. Quando isso acontece, as forcompreensões são geralmente muito mais claras.
Vou mostrar um exemplo simples e omitir intencionalmente qualquer explicação. Você pode decidir qual sintaxe foi 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
withFilterO Scala 2.8 introduziu um método chamado withFilter, cuja principal diferença é que, em vez de retornar uma nova coleção filtrada, ele filtra sob demanda. O filtermétodo tem seu comportamento definido com base no rigor da coleção. Para entender isso 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 filteré aplicada imediatamente com List, retornando uma lista de probabilidades - desde que foundé false. Só então foreaché executado, mas, a essa altura, a mudança foundnão tem sentido, como filterjá foi executado.
No caso de Stream, a condição não é aplicada imediatamente. Em vez disso, à medida que cada elemento é solicitado foreach, filtertesta a condição, que permite foreachinfluenciá-la found. Apenas para esclarecer, eis 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 ifque isso fosse considerado sob demanda, em vez de ser aplicado a toda a coleção com antecedência.
Introdução do Scala 2.8 withFilter, que é sempre não rigorosa, independentemente do rigor da coleção. O exemplo a seguir mostra Listcom os dois métodos 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 filtercomportamento. Como observação, Rangefoi alterado de não-estrito para estrito entre o Scala 2.7 e o Scala 2.8.
withFilterdeve ser também não-estrito, mesmo para coleções rigorosas, o que merece alguma explicação. Eu vou considerar isso ...
for(x <- c; y <- x; z <-y) {...}é traduzido em c.foreach(x => x.foreach(y => y.foreach(z => {...}))) 2. for(x <- c; y <- x; z <- y) yield {...}é traduzido emc.flatMap(x => x.flatMap(y => y.map(z => {...})))
for(x <- c; y = ...) yield {...}realmente traduzido c.map(x => (x, ...)).map((x,y) => {...})? Eu acho que está traduzido c.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})ou estou faltando alguma coisa?
Sim, como Earwicker disse, é praticamente o equivalente ao LINQ selecte tem muito pouco a ver com Ruby e Python yield. Basicamente, onde em C # você escreveria
from ... select ???
em Scala você tem
for ... yield ???
Também é importante entender que as compreensões fornão funcionam apenas com sequências, mas com qualquer tipo que define determinados métodos, assim como o LINQ:
map, ele permite for-expressions consistindo em um único gerador.flatMap, bem comomap , permitirá forexpressões que consistem em vários geradores.foreach, permitefor -loops sem rendimento (ambos com geradores únicos e múltiplos).filter, ele permite forexpressões -filter começando com um if
na forexpressão.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 entrada (embora eu acredite que haja uma maneira de eliminar duplicatas).
Isso é bem diferente das "continuações imperativas" ativadas pelo rendimento em outros idiomas, onde fornece uma maneira de gerar uma lista de qualquer tamanho, a partir de algum código imperativo com quase qualquer estrutura.
(Se você conhece C #, é mais próximo do select operador do LINQ do que yield return).
A palavra-chave yieldem Scala é simplesmente açúcar sintático que pode ser facilmente substituído por a map, como Daniel Sobral já explicou em detalhes.
Por outro lado, yieldé absolutamente enganoso se você estiver procurando por geradores (ou continuações) semelhantes aos do Python . Veja este tópico do SO para obter mais informações: Qual é a maneira preferida de implementar 'yield' no Scala?
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 da seguinte maneira
" Para cada número inteiro i, se for maior que 3, então produza (produza) ie adicione-o à listaA ."
Em termos de notação matemática de construtores de conjuntos , a compreensão acima é análoga a
que pode ser lido como
" Para cada número inteiro , se for maior que
, então é um membro do conjunto
."
ou alternativamente como
" é o conjunto de todos os números inteiros
, de modo que cada um
é maior que
."
O rendimento é semelhante ao loop for, 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 termina a execução, ele retorna a coleção de todos os valores produzidos. O rendimento pode ser usado como operadores aritméticos simples ou 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!!
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.
rendimento é mais flexível que 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 )
o rendimento imprimirá resultados como: List (5, 6), o que é bom
enquanto map () retornará resultado como: List (false, false, true, true, true), o que provavelmente não é o que você pretende.