Подтвердить что ты не робот

Как вернуть дополнительную информацию из методов?

Общий вопрос заключается в том, как вернуть дополнительную информацию из методов, помимо фактического результата вычисления. Но я хочу, чтобы эту информацию можно было молча игнорировать.

Возьмем, например, метод dropWhile на Iterator. Возвращаемым результатом является мутировавший итератор. Но, возможно, иногда меня может заинтересовать количество удаленных элементов.

В случае dropWhile эта информация может быть сгенерирована извне путем добавления индекса к итератору и вычисления количества удаленных шагов после этого. Но в целом это невозможно.

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

Итак, вопрос в том, есть ли какой-нибудь умный способ сбора такой дополнительной информации?

Может быть через параметры Option[X => Unit] с функциями обратного вызова, которые по умолчанию равны None? Есть ли что-то более умное?

4b9b3361

Ответ 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, набивающей счетчик через вычисление.