2010-10-22 2 views
2

Мне нужно пересечь типы входящих объектов с набором предопределенных.Как проверить наличие набора типов в коллекции объекта базового типа?

Необработанные подход для сканирования входящего сбора для каждого предопределенного типа:

trait Field 
class Field1 extends Field 
class Field2 extends Field 
class Field3 extends Field 
... 

class FieldManager(shownFields:Iterable[Field]) { 
    var hiddenFields = new ArrayBuffer[Field] 
    var found = false 
    for (sf <- shownFields) { 
    if (sf.isInstanceOf[Field1]) { 
     found = true 
     break 
    } 
    if (!found) 
    hiddenFields+=new Field1 
    for (sf <- shownFields) { 
    if (sf.isInstanceOf[Field2]) { 
     found = true 
     break 
    } 
    if (!found) 
    hiddenFields+=new Field2 
    for (sf <- shownFields) { 
    if (sf.isInstanceOf[Field3]) { 
     found = true 
     break 
    } 
    if (!found) 
    hiddenFields+=new Field3 
    ... 
} 

Ничего себе, это так многословным для Scala! Должен быть более короткий путь. Как шаблон функции в C++:

class FieldManager { 
    vector<Field*> hiddenFields, shownFields; 
    template<class T> 
    void fillHiddenType() { 
    FOR_EACH(Field *, field, shownFields) { 
     if (dynamic_cast<T*>(field)) 
     return 
     hiddenFields.push_back(new T) 
    } 
    } 
    void fillHidden() { 
    fillHiddenType<Field1>(); 
    fillHiddenType<Field2>(); 
    fillHiddenType<Field3>(); 
    ... 
    } 
}; 

В этом примере на C++ мы указываем каждый тип, который нужно отсканировать только один раз.

Ok, Scala также хорошо для его параметров типа, позволяет попробовать:

def fillHiddenType[T] { 
    for (sf <- shownFields) 
    if (sf.isInstanceOf[T]) 
     return 
    hiddenFields+=new T 
} 

Но компилятор не нравится создавать T экземпляр: class type required but T found. Попробуйте передать экземпляр в качестве аргумента, и появится следующая проблема. warning: abstract type T in type is unchecked since it is eliminated by erasure и isInstanceOf[] всегда верны! Тип T абстрагируется настолько хорошо, что полностью удаляется даже [T <: Поле] не позволяет сравнивать его с типами из нашей коллекции, показанной по умолчанию.

Вопрос: Как проверить целостность объекта данного типа в коллекции компактным способом?

Обновление На Scala 2.7.7 был протестирован следующий рабочий код. Оба ответа были полезны. Спасибо вам, ребята!

//Scala 2.7.7 misses toSet members 
implicit def toSet[T](i:Iterable[T]): Set[T] = { 
    val rv = new HashSet[T] 
    rv ++= i 
    rv 
} 

def filterOut[T](all:Iterable[T], toRemove:Set[T]) = { 
    all.filter(x => ! toRemove.contains(x)) 
} 

def filterOutByType[T <: AnyRef](all:Iterable[T], toFilter:Set[T]):Iterable[T] = { 
    def toClass(x:AnyRef) = x.getClass.asInstanceOf[Class[T]] 
    val allTypes = all map toClass 
    val extraTypes = toSet(filterOut(allTypes, toFilter map toClass)) 
    all.filter(extraTypes contains toClass(_)) 
} 

ответ

4

вы можете сохранить набор известных подклассов Field, например, так:

val allFieldClasses = Set[Class[_ <: Field]](
    classOf[Field1], 
    classOf[Field2], 
    classOf[Field3], 
    ...) 

Затем создать скрытые поля просто ма олучатель отфильтровывать показанные классы полей из этого множества и построения экземпляров оставшихся:

def createHiddenFields(shownFields: Iterable[Field]):Set[Field] = { 
    val toClass = (_:Field).getClass.asInstanceOf[Class[Field]] 
    (allFieldTypes -- (shownFields map toClass)) map (_.newInstance) 
} 

К сожалению, вы должны помнить, чтобы сохранить allFieldClasses уточненный при добавлении новых подклассов Field - компилятор не предупредит вас, что ваш набор не завершен, но я не вижу этого.

+0

Я попробую это. – Basilevs

+0

Я принимаю это, поскольку он сначала упоминает списки типов и, кажется, фактически протестирован с компилятором() – Basilevs

+0

asInstanceOf [Класс [Поле]], часть была обязательной. – Basilevs

6

Это тривиально, чтобы найти первый элемент коллекции, который имеет данный тип:

shownFields.find(_.isInstanceOf[Field1]) 

но все равно возвращает экземпляр Option[Field], а не Option[Field1] - что вы хотите для сильных типирование. сборный метод поможет здесь:

showFields.collect{case x : Field1 => x} 

Который возвращает Iterable[Field1], то вы можете использовать headOption, чтобы выбрать первый элемент из итератора как Option[Field1], либо Some[Field1] если он присутствует, или None иначе:

showFields.collect{case x : Field1 => x}.headOption 

Чтобы сделать его более эффективным, а не вычислить все Field1 в списке, я бы также сделать его ленивым, с помощью метода view:

showFields.view.collect{case x : Field1 => x}.headOption 

и затем установить значение по умолчанию, если он не найден, используйте getOrElse метод, которые обеспечивают параметры:

showFields.view.collect{case x : Field1 => x}.headOption getOrElse (new Field1) 

Update

Я только что прочитал назад на вопрос, если кажется, что вы хотите hiddenFields чтобы содержать новый экземпляр каждого подтипа поля, для которого нет элемента в showFields.

Чтобы найти все типы представлены в Iterable:

val shownFieldTypes = showFields.map(_.getClass).toSet 

(преобразующие его к набору сил уникальных значений)

Если затем есть набор полей вы заинтересованы в:

val allFieldTypes = Set(classOf[Field1], classOf[Field2], ...) 

Вы можете вычесть, чтобы найти недостающие:

val hiddenFieldTypes = allFieldTypes -- shownFieldTypes 

Загвоздка в том, что вы потом застрять с помощью newInstance, и отражение не всегда хочется ... Так что:

val protoHiddenFields = Set(new Field1, new Field2, ...) 
val allFieldTypes = protoHiddenFields.map(_.getClass) 
val hiddenFieldTypes = allFieldTypes -- shownFieldTypes 
val hiddenFields = protohiddenFields.filter(hiddenFieldTypes contains _.getClass) 

Красота этого подхода заключается в том, что ваши прототипы могут быть инициализированы с помощью параметры конструктора, если вам так хочется

+0

Скорее длинный только для первого типа последовательности. BTW, мои классы не являются классами case (классы case с конструкциями без аргументов устарели). – Basilevs

+0

Но я не имею в виду классы case в любом месте ... В классах классов есть метод unapply в сопутствующем объекте, который упрощает их использование при сопоставлении с образцом, но это, конечно, не единственное, что можно использовать в шаблоне совпадение! –

+0

Прохладный. Я тоже попробую. – Basilevs

Смежные вопросы