Как вернуть дополнительную информацию из методов?
Общий вопрос заключается в том, как вернуть дополнительную информацию из методов, помимо фактического результата вычисления. Но я хочу, чтобы эту информацию можно было молча игнорировать.
Возьмем, например, метод dropWhile
на Iterator
. Возвращаемым результатом является мутировавший итератор. Но, возможно, иногда меня может заинтересовать количество удаленных элементов.
В случае dropWhile
эта информация может быть сгенерирована извне путем добавления индекса к итератору и вычисления количества удаленных шагов после этого. Но в целом это невозможно.
Простое решение - вернуть кортеж с фактическим результатом и дополнительной информацией. Но тогда мне нужно обрабатывать кортеж, когда я вызываю метод, даже если меня не интересует дополнительная информация.
Итак, вопрос в том, есть ли какой-нибудь умный способ сбора такой дополнительной информации?
Может быть через параметры Option[X => Unit]
с функциями обратного вызова, которые по умолчанию равны None
? Есть ли что-то более умное?
Ответы
Ответ 1
Только мои два цента здесь...
Вы можете объявить это:
case class RichResult[+A, +B](val result: A, val info: B)
с неявным преобразованием в A
:
implicit def unwrapRichResult[A, B](richResult: RichResult[A, B]): A = richResult.result
Тогда:
def someMethod: RichResult[Int, String] = /* ... */
val richRes = someMethod
val res: Int = someMethod
Ответ 2
Это определенно не более умно, но вы можете просто создать метод, который опускает дополнительную информацию.
def removeCharWithCount(str: String, x: Char): (String, Int) =
(str.replace(x.toString, ""), str.count(x ==))
// alias that drops the additional return information
def removeChar(str: String, x: Char): String =
removeCharWithCount(str, x)._1
Ответ 3
Вот мой пример (с некоторыми изменениями с более реалистичным примером):
package info {
trait Info[T] { var data: Option[T] }
object Info {
implicit def makeInfo[T]: Info[T] = new Info[T] {
var data: Option[T] = None
}
}
}
Тогда предположим, что ваш оригинальный метод (и прецедент) реализован следующим образом:
object Test extends App {
def dropCounterIterator[A](iter: Iterator[A]) = new Iterator[A] {
def hasNext = iter.hasNext
def next() = iter.next()
override def dropWhile(p: (A) => Boolean): Iterator[A] = {
var count = 0
var current: Option[A] = None
while (hasNext && p({current = Some(next()); current.get})) { count += 1 }
current match {
case Some(a) => Iterator.single(a) ++ this
case None => Iterator.empty
}
}
}
val i = dropCounterIterator(Iterator.from(1))
val ii = i.dropWhile(_ < 10)
println(ii.next())
}
Чтобы предоставить и получить доступ к информации, код будет слегка изменен:
import info.Info // line added
object Test extends App {
def dropCounterIterator[A](iter: Iterator[A]) = new Iterator[A] {
def hasNext = iter.hasNext
def next() = iter.next()
// note overloaded variant because of extra parameter list, not overriden
def dropWhile(p: (A) => Boolean)(implicit info: Info[Int]): Iterator[A] = {
var count = 0
var current: Option[A] = None
while (hasNext && p({current = Some(next()); current.get})) { count += 1 }
info.data = Some(count) // line added here
current match {
case Some(a) => Iterator.single(a) ++ this
case None => Iterator.empty
}
}
}
val i = dropCounterIterator(Iterator.from(1))
val info = implicitly[Info[Int]] // line added here
val ii = i.dropWhile((x: Int) => x < 10)(info) // line modified
println(ii.next())
println(info.data.get) // line added here
}
Обратите внимание, что по какой-либо причине это происходит, и мне пришлось аннотировать тип функции, переданной в dropWhile
.
Ответ 4
Вы хотите dropWhileM
с помощью монады State
, набивающей счетчик через вычисление.