Scala: Qual é a diferença entre traços Traversable e Iterable em coleções Scala?


Respostas:


121

Para simplificar, os iteradores mantêm o estado, os percorríveis não.

Um Traversabletem um método abstrato: foreach. Quando você chama foreach, a coleção alimenta a função passada com todos os elementos que ela mantém, um após o outro.

Por outro lado, an Iterabletem como método abstrato iterator, que retorna um Iterator. Você pode chamar nextum Iteratorpara obter o próximo elemento na hora de sua escolha. Até que você faça isso, ele precisa manter o controle de onde estava na coleção e do que vem a seguir.


4
Mas Iterableestende Traversable, então eu acho que você quer dizer Traversables que não são Iterables.
Robin Green

4
@RobinGreen Quer dizer, o cumprimento da Traversableinterface não exige manutenção do estado, ao passo que o cumprimento da interface exige Iterator.
Daniel C. Sobral

10
Traversables que são Iterablenão mantêm nenhum estado de iteração. É o Iteratorcriado e devolvido pelo Iterableque mantém o estado.
Graham Lea

1
É importante notar que a partir de 2.13 o traço Traversable está obsoleto. Para citar Stefan Zeiger, "A abstração Traversable não teve seu peso na biblioteca atual e provavelmente não ressurgirá no novo design. Tudo o que queremos fazer pode ser expresso com Iterable."
Igor Urisman

226

Pense nisso como a diferença entre soprar e sugar.

Quando você chama um Traversables foreach, ou seus métodos derivados, ele vai soprar seus valores em sua função, um de cada vez - portanto, ele tem controle sobre a iteração.

Com o Iteratorretorno de um Iterablepensamento, você suga os valores dele, controlando quando passar para o próximo.


49
As pessoas costumam chamar isso de empurrar e puxar, em vez de soprar e sugar , mas gosto da sua mente aberta.
Martijn

2
Nunca esquecendo disso quando perguntado em minha próxima entrevista
thestephenstanton

23

tl; dr Iterables são Traversablesque podem produzir statefulIterators


Primeiro, saiba que Iterableé o subtítulo de Traversable.

Segundo,

  • Traversablerequer a implementação do foreachmétodo, que é usado por todo o resto.

  • Iterablerequer a implementação do iteratormétodo, que é usado por todo o resto.

Por exemplo, a implementação de findpara Traversableusa foreach(por meio de um para compreensão) e lança uma BreakControlexceção para interromper a iteração assim que um elemento satisfatório for encontrado.

trait TravserableLike {
  def find(p: A => Boolean): Option[A] = {
    var result: Option[A] = None
    breakable {
      for (x <- this)
        if (p(x)) { result = Some(x); break }
    }
    result
  }
}

Em contraste, a Iterablesubtração substitui essa implementação e chama findo Iterator, que simplesmente para de iterar assim que o elemento é encontrado:

trait Iterable {
  override /*TraversableLike*/ def find(p: A => Boolean): Option[A] =
    iterator.find(p)
}

trait Iterator {
  def find(p: A => Boolean): Option[A] = {
    var res: Option[A] = None
      while (res.isEmpty && hasNext) {
        val e = next()
        if (p(e)) res = Some(e)
      }
    res
  }
}

Seria bom não lançar exceções para a Traversableiteração, mas essa é a única maneira de iterar parcialmente ao usar apenas foreach.

De uma perspectiva, Iterableé o traço mais exigente / poderoso, já que você pode facilmente implementar foreachusing iterator, mas não pode realmente implementar iteratorusing foreach.


Em resumo, Iterablefornece uma maneira de pausar, retomar ou parar a iteração por meio de um stateful Iterator. Com Traversable, é tudo ou nada (sem exceções para controle de fluxo).

Na maioria das vezes, isso não importa e você desejará uma interface mais geral. Mas se você precisar de um controle mais personalizado sobre a iteração, precisará de um Iterator, que pode ser recuperado de um Iterable.


1

A resposta de Daniel parece boa. Deixe-me ver se consigo colocar em minhas próprias palavras.

Portanto, um Iterable pode fornecer um iterador, que permite percorrer os elementos um de cada vez (usando next ()) e parar e prosseguir quando quiser. Para fazer isso, o iterador precisa manter um "ponteiro" interno para a posição do elemento. Mas um Traversable fornece o método, foreach, para percorrer todos os elementos de uma vez sem parar.

Algo como Range (1, 10) precisa ter apenas 2 inteiros como estado de Traversable. Mas Range (1, 10) como um Iterable fornece um iterador que precisa usar 3 inteiros para o estado, um dos quais é um índice.

Considerando que Traversable também oferece foldLeft, foldRight, seu foreach precisa atravessar os elementos em uma ordem conhecida e fixa. Portanto, é possível implementar um iterador para um Traversable. Ex: def iterator = toList.iterator

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.