Scala: выбор подмножества типов из общей коллекции без стандартных коллекций.(Сбор) - PullRequest
2 голосов
/ 16 августа 2011

После моего предыдущего вопроса пришел быстрый и отличный ответ, однако оказалось, что мой пример недостаточно хорошо соответствовал моему фактическому производственному коду. Итак, мне нужна новая реализация метода collect.

Второй фруктовый мир (с довольно забавными фруктовыми деревьями):

class Fruit {
    var seeds:Array[Fruit] = Array()
    def naiveCollect[F <: Fruit]:Array[Fruit] = this match {
        case f:F => Array(this)
        case _ => seeds.map(_.select[F]).flatten.toArray
    }
}

class Apple extends Fruit
class Pear extends Fruit
class GrannySmith extends Apple

Не работает из-за стирания типа:

var tree = new Fruit { seeds = Array(
                new Apple,
                new Pear,
                new GrannySmith,
                new Pear { seeds = Array(
                    new Apple,
                    new Pear)},
                new Apple)}

scala> tree.naiveCollect[Apple]
res1: Array[Fruit] = Array($anon$2@5a4b99fa)

// wanted output: Apple, GrannySmith, Apple, Apple

РЕДАКТИРОВАТЬ, РЕШЕНИЕ1:

Оказывается, мне удалось создать что-то, что работает с использованием PartialFunction, как в стандартной библиотеке.

class Fruit {
    ...
    def clumsyCollect[F](pf:PartialFunction[Fruit, F]):Seq[F] = 
        if (pf.isDefinedAt(this))
            List(pf(this))
        else
            seeds.flatMap(_.selectPartial[F](pf))
}

Вариант использования:

tree.clumsyCollect { case a:Apple => a }

Любые альтернативы или советы по очистке этой системы все равно будут хороши!

Ответы [ 2 ]

1 голос
/ 16 августа 2011

Манифесты могут быть использованы для обхода стирания. Поскольку нас интересует только удаление манифеста, мы используем здесь ClassManifest, потому что он более легкий.

Следующее простое и работает, но тип возвращаемого значения select - Array [Fruit], а не Array [F].

  class Fruit {
      var seeds: Array[Fruit] = Array()
      def select[F <: Fruit](implicit m: ClassManifest[F]): Array[Fruit] =
        seeds.filter(s => m.erasure.isInstance(s)) 
  }

Ниже приведен тип возвращаемого массива [F], но он немного сложнее.

  class Fruit {
      var seeds: Array[Fruit] = Array()
      def select[F <: Fruit](implicit m: ClassManifest[F]): Array[F] = {
        seeds.foldLeft(Array.newBuilder[F]) { (b, s) =>
          if(m.erasure.isInstance(s)) b += s.asInstanceOf[F] else b
        }.result
      }
  }

Редактировать: Я только что заметил, что я не ответил на то, что спросил опера. Я все равно оставлю свой ответ, может быть, он кому-нибудь пригодится.

1 голос
/ 16 августа 2011

Я думаю, что вам нужно scala.reflect.ClassManifest

class Fruit {
    var seeds:Array[Fruit] = Array()
    def select[F <: Fruit](implicit cm: ClassManifest[F]): Array[F] = 
      if (cm.erasure.isInstance(this))        
        Array(this.asInstanceOf[F])
      else 
        seeds.flatMap(_.select[F])
}

При каждом вызове select будет передаваться неявный параметр, содержащий фактическую информацию о классе.Теперь вы можете делать проверки классов во время выполнения, а также вы можете возвращать Array более определенного типа.

Это дает желаемый результат:

scala> tree.select[Apple]
res12: Array[Apple] = Array(Apple@10fa4d, GrannySmith@a10ca8, Apple@14611ec, Apple@142b533)

Кроме того, вы можете использовать привязанный к контексту синтаксис:

def select[F <: Fruit : ClassManifest]: Array[F] = 
  if (classManifest[F].erasure.isInstance(this))        
...
...