Eu entendo o rendimento de Ruby e Python. O que o rendimento de Scala faz?
312
Eu entendo o rendimento de Ruby e Python. O que o rendimento de Scala faz?
É 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 for
e 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 yield
tem um efeito diferente.
Eu acho que a resposta aceita é ótima, mas parece que muitas pessoas não conseguiram entender alguns pontos fundamentais.
Primeiro, as
for
compreensõ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
for
compreensões de Scala são açúcar sintático para composição de múltiplas operações com mapaflatMap
efilter
. Orforeach
. O Scala, na verdade, traduz umafor
expressã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
é traduzido para
este
é traduzido para
este
é traduzido no Scala 2.7 para
ou, no Scala 2.8, em
com um fallback para o antigo se o método
withFilter
não estiver disponível, masfilter
estiver. Consulte a seção abaixo para obter mais informações sobre isso.este
é traduzido para
Quando você olha para
for
compreensões muito simples , as alternativasmap
/foreach
parecem, 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, asfor
compreensõ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.
ou
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, ele filtra sob demanda. Ofilter
mé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 comList
(strict) eStream
(non-strict):A diferença acontece porque
filter
é aplicada imediatamente comList
, retornando uma lista de probabilidades - desde quefound
éfalse
. Só entãoforeach
é executado, mas, a essa altura, a mudançafound
não tem sentido, comofilter
já foi executado.No caso de
Stream
, a condição não é aplicada imediatamente. Em vez disso, à medida que cada elemento é solicitadoforeach
,filter
testa a condição, que permiteforeach
influenciá-lafound
. Apenas para esclarecer, eis o código de compreensão equivalente:Isso causou muitos problemas, porque as pessoas esperavam
if
que 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 mostraList
com os dois métodos no Scala 2.8:Isso produz o resultado que a maioria das pessoas espera, sem alterar o
filter
comportamento. Como observação,Range
foi alterado de não-estrito para estrito entre o Scala 2.7 e o Scala 2.8.fonte
withFilter
deve 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 emc.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 traduzidoc.map(x => (x, ...)).map((x,y) => {...})
? Eu acho que está traduzidoc.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
select
e tem muito pouco a ver com Ruby e Pythonyield
. Basicamente, onde em C # você escreveriaem Scala você tem
Também é importante entender que as compreensões
for
não funcionam apenas com sequências, mas com qualquer tipo que define determinados métodos, assim como o LINQ:map
, ele permitefor
-expressions consistindo em um único gerador.flatMap
, bem comomap
, permitiráfor
expressões que consistem em vários geradores.foreach
, permitefor
-loops sem rendimento (ambos com geradores únicos e múltiplos).filter
, ele permitefor
expressões -filter começando com umif
nafor
expressão.fonte
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:
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 queyield return
).fonte
A palavra-chave
yield
em Scala é simplesmente açúcar sintático que pode ser facilmente substituído por amap
, 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?fonte
Considere o seguinte para compreensão
Pode ser útil ler em voz alta da seguinte maneira
" Para cada número inteiro
i
, se for maior que3
, então produza (produza)i
e 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 ."
fonte
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
res: scala.collection.immutable.IndexedSeq [Int] = Vetor (3, 6, 9, 12, 15)
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!!
fonte
Esses dois pedaços de código são equivalentes.
Esses dois pedaços de código também são equivalentes.
O mapa é tão flexível quanto o rendimento e vice-versa.
fonte
rendimento é mais flexível que map (), veja o exemplo abaixo
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.
fonte