Позвольте мне немного подробнее рассказать о Рекс Керр ответ . Во всей коллекции Scala есть три центральных класса, и только два из них действительно являются частью коллекций. Это Traversable
, Iterable
и Iterator
.
Самая базовая коллекция - Traversable
. Для того, чтобы что-то было Traversable
, есть только одно условие: ему необходимо реализовать метод foreach
. Таким образом, если можно передать функцию, которая затем будет применена к каждому элементу коллекции, это может быть Traversable
. Например:
class Three[A](a: A, b: A, c: A) extends Traversable[A] {
def foreach[U](f: (A) => U) {
f(a); f(b); f(c)
}
}
Это даст вам все Traversable
методы, хотя такие методы, как map
и filter
не будут возвращать Three
, но Traversable
. Получить возвращаемый вами класс гораздо сложнее, и многие специализированные классы просто не могут этого сделать. Например, Three
не может этого сделать, потому что будет Three
из filter
, который удалил некоторые элементы?
Далее, есть Iterator
, который действительно делает почти то же самое, что и Traversable
, но другим способом. Iterator
должен определять два метода: next
и hasNext
. Например:
class Three[A](a: A, b: A, c: A) extends Iterator[A] {
private var aReady, bIsRead, cIsRead = false
def hasNext = !(aIsRead && bIsRead && cIsRead)
def next = (aIsRead, bIsRead, cIsRead) match {
case (false, _, _) => aIsRead = true; a
case (_, false, _) => bIsRead = true; b
case (_, _, false) => cIsRead = true; c
case _ => Iterator.empty.next
}
}
Это даст все методы Iterator
, которые в основном выглядят так же, как методы Traversable
. Разница между методами в основном связана с тем, что Iterator
можно использовать только один раз.
Наконец, есть Iterable
. Чтобы быть Iterable
, класс должен реализовать один метод: iterator
, который возвращает Iterator
для этого класса. Например:
class Three[A](a: A, b: A, c: A) extends Iterable[A] {
// not necessary, but may offer a more efficient implementation
override def foreach[U](f: (A) => U) {
f(a); f(b); f(c)
}
def iterator = new Iterator[A] {
private var aReady, bIsRead, cIsRead = false
def hasNext = !(aIsRead && bIsRead && cIsRead)
def next = (aIsRead, bIsRead, cIsRead) match {
case (false, _, _) => aIsRead = true; a
case (_, false, _) => bIsRead = true; b
case (_, _, false) => cIsRead = true; c
case _ => Iterator.empty.next
}
}
}
Итак, возвращаясь к вашему вопросу, вы должны подумать, какое ожидаемое поведение вы хотите и как вы этого хотите. В частности, в коллекциях Scala нет понятий «вниз» и «слева», что означает, что Node
может иметь метод, возвращающий коллекцию Scala, но никогда не может быть , если говорить правильно, например см. решение Рекса Керра.
EDIT
Позвольте мне привести пример, отличный от Рекса Керра. Здесь я сделаю Traversable
Node
, и будет выбран порядок обхода.
class Node[A] extends Traversable[A] {
var left: Node[A] = _
var down: Node[A] = _
var traverseLeft = true
var value: A = _
def foreach[U](f: (A) => U) = if (traverseLeft) foreachLeft(f) else foreachDown(f)
def foreachLeft[U](f: (A) => U) { f(value); if (left != null) left.foreachLeft(f) }
def foreachDown[U](f: (A) => U) { f(value); if (down != null) down.foreachDown(f) }
}
Так что Node
- это Traversable
, и он поддерживает все методы Traversable
(хотя он все равно не вернет Node
из map
и т. Д. - посмотрите другие вопросы по этому поводу). Вы можете выбрать, будет ли он перемещаться влево или вниз с флагом (traverseLeft
), и все обычные методы Traversable
будут использовать все, что установлено в узле, где был вызван метод.
Это, однако, не очень хорошая модель. Я бы предпочел пойти с решением Рекса Керра о возврате итераторов влево и вниз или оставить коллекции Scala полностью и пойти с чем-то, обработанным с помощью Kiama . Последняя, однако, представляет собой совершенно иную парадигму, и вы не собираетесь использовать ее в качестве кода, транслитерируемого в Scala.