Любой чистый способ объединить find и instanceof в Scala? - PullRequest
4 голосов
/ 15 апреля 2011

Я хочу найти в некоторых Iterable некоторые элементы, которые соответствуют какому-либо данному типу, так и проверяют предикат, принимающий этот тип в качестве аргумента.

Я написал этот метод, используя программирование в императивном стиле, которое, кажется, соответствует моим ожиданиям. Есть ли какой-нибудь способ написать это более "по-скальски"?

def findMatch[T](it: Iterable[_], clazz: Class[T], pred: T => Boolean): Option[T] = {
  val itr = it.iterator
  var res: Option[T] = None
  while (res.isEmpty && itr.hasNext) {
    val e = itr.next()
    if (clazz.isInstance(e) && pred(clazz.cast(e))) {
      res = Some(clazz.cast(e))
    }
  }
  res
}

Ответы [ 4 ]

18 голосов
/ 15 апреля 2011

Вы можете использовать collect, если хотите find, а затем map.

scala> val it: Iterable[Any] = List(1,2,3,"4")            
it: Iterable[Any] = List(1, 2, 3, 4)

scala> it.view.collect{case s: String => s}.headOption
res1: Option[String] = Some(4)
1 голос
/ 15 апреля 2011

Вы можете работать с экзистенциальным типом X forSome{typeX} вместо использования _ в качестве параметра типа. Это позволит вам написать его с указанным методом find и использовать метод map для типа Option:

def findMatch[T](it: Iterable[X forSome {type X}], clazz: Class[T], pred: T => Boolean): Option[T] = {
    it.find{ e => clazz.isInstance(e) && pred(clazz.cast(e))}.map{clazz.cast(_)}
}
1 голос
/ 15 апреля 2011

Если вы поделите свою проблему на подзадачи, вам будет проще найти более идиоматическую версию.Вы хотите

  1. найти все экземпляры T в вашем Iterable[Any]
  2. и привести их к T, чтобы компилятор был доволен
  3. найти первыйсоответствующий элемент

Для первого пункта вы можете легко использовать метод filter для Iterator.Таким образом, у вас есть

it.iterator.filter(x => clazz.isInstance(x))

, который возвращает вам Iterator[Any], который содержит только T с.Теперь давайте убедим компилятор:

it.iterator.filter(x => clazz.isInstance(x)).map(x => x.asInstanceOf[T])

Хорошо, теперь у вас есть Iterator[T] - так что вам просто нужно найти первый элемент, выполняющий ваш предикат:

def findMatch[T](it: Iterable[Any], clazz: Class[T], pred: T => Boolean): Option[T] = 
  it.iterator.filter(x => clazz.isInstance(x))
             .map(x => x.asInstanceOf[T])
             .find(pred)
0 голосов
/ 15 апреля 2011

Вы можете использовать Iterable метод find * и сопоставление с шаблоном с защитой:

scala> val it: Iterable[Any] = List(1,2,3,"4")
it: Iterable[Any] = List(1, 2, 3, 4)

scala> it.find { _ match {
  case s: String if s == "4" => true
  case _ => false
}}.asInstanceOf[Option[String]]
res0: Option[String] = Some(4)

. Для ознакомления с сопоставлением с шаблоном посмотрите:.labs.oreilly.com / ch03.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...