Найти значение применяемой функции в Scala - PullRequest
2 голосов
/ 25 ноября 2011

У меня проблема в том, что я пытался найти лучшее решение для использования существующей библиотеки коллекций Scala, но я не могу что-то придумать.

Учитывая набор функций, мне нужно найти первый результат функции для некоторого ввода, который удовлетворяет предикату.Вот простая реализация:

def findResult[A, B](t: Traversable[Function1[A, B]], value: A, p: B => Boolean): Option[B] = {
  var result: Option[B] = None
  breakable {
    for (e <- t) {
      val r = e(value)
      if (p(r)) { result = Some(r); break }
    }
  }
  result
}


// test
val f1 = (s: String) => if (s == "a") "aa" else null
val f2 = (s: String) => if (s == "b") "bb" else null
val l = List(f1, f2)

findResult(l, "b", (v: Any) => v != null) must equal(Some("bb"))

Есть ли лучший способ сделать это с помощью API коллекций?

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

Ответы [ 3 ]

4 голосов
/ 25 ноября 2011

Я собирался просто прокомментировать ответ Тенши, но потом решил расширить его до альтернативного подхода. Обратите внимание, что если вы используете map в строгом Traversable, то весь список будет отображен до того, как произойдет какое-либо обнаружение. Это означает, что вы закончите выполнять немного больше работы.

Вместо этого вы можете просто найти:

def findResult[A, B](t: Traversable[Function1[A, B]], value: A, p: B => Boolean) =
    t find (fn => p(fn(value)))

Вместо этого будет возвращена функция , которая удовлетворяет предикату p для value. Если вам вместо этого нужен результат, вам нужно только применить функцию к значению снова (при условии, что функция является ссылочно прозрачной). Это, конечно, поэтому будет выполнять немного больше работы, но, вероятно, будет немного меньше дополнительной работы, чем техника Тенши. Обратите внимание, что техника, которую вы придумали самостоятельно, выполняет нет дополнительную работу.

[обновление] Если вы действительно не хотите выполнять любую дополнительную работу, то вам следует использовать представление коллекции . Я должен был найти это, но я думаю, что у меня есть справка об этом. Теперь, прямо украдя код Тенши и добавив .view, вот несколько копипаст из моей интерактивной сессии:

def f1(x: Int): Int = { println("f1"); x }
f1: (x: Int)Int
def f2(x: Int): Int = { println("f2"); x+1 }
f2: (x: Int)Int
def f3(x: Int): Int = { println("f3"); x+2 }
f3: (x: Int)Int
val fs = List(f1 _, f2 _, f3 _)
fs: List[(Int) => Int] = List(, , )
(fs.view map (f => f(1))) find (_ == 2)
f1
f2
res8: Option[Int] = Some(2)

Как видите, f1 и f2 выполнены, но не f3. Это связано с тем, что как только результат f2(1) оказался равным == 2, функция find смогла остановиться. Это часть волшебства взглядов: ленивое отображение. Фактически, операции map и find слиты воедино благодаря представлениям! Или так мне сказали.

def findResult[A, B](t: Traversable[Function1[A, B]], value: A, p: B => Boolean) =
    t.view map (f => f(value)) find p
def even(x: Int) = x % 2 == 0

findResult(fs, 1, even)
f1
f2
res13: Option[Int] = Some(2)

Итак, вот оно. Один из камней, который я нашел в документации, на которую я ссылался выше, был такой:

[По состоянию на Scala 2.8] Все коллекции, кроме потоков и представлений, являются строгими. Единственный способ перейти от строгой к ленивой коллекции - это метод view. Единственный способ вернуться назад - через force.

2 голосов
/ 25 ноября 2011

Вы можете использовать просмотр :

def findResult[A, B](t: Traversable[Function1[A, B]], value: A, p: B => Boolean) = {
  t.view.map(_(value)).find(p(_))
}
2 голосов
/ 25 ноября 2011

Сочетание map и find должно работать:

def findResult[A, B](t: Traversable[Function1[A, B]], value: A, p: B => Boolean) =
    t map (fn => fn(value)) find p
...