0

在 Scala 中评估地图基本上有两种选择。

  • 当需要下一个值时,惰性求值计算作为参数传递的函数。如果函数需要一小时才能执行,那么在需要该值时等待一小时。(例如StreamIterator
  • 急切评估在定义地图时计算函数。它生成一个新列表(Vector或其他)并存储结果,使程序在那段时间很忙。
  • 我们可以在单独的线程Future中获取列表(Seq或其他),这意味着我们的线程不会阻塞,但必须存储结果。

所以我做了一些不同的事情,请在这里查看

这是前一段时间,所以我不记得我是否测试过。关键是要有一个映射同时(非阻塞)和急切地应用于一组元素,填充缓冲区(计算机中核心数量的大小,而不是更多)。这意味着:

  1. 映射的调用不会阻塞当前线程。
  2. 获取元素不会阻塞当前线程(以防之前有时间计算它并将结果存储在缓冲区中)。
  3. 可以处理无限列表,因为我们只预取几个结果(大约 8 个,取决于内核的数量)。

所以这一切听起来都很好,你可能想知道问题出在哪里。问题是这个解决方案不是特别优雅的恕我直言。假设我共享的代码在 Java 和/或 Scala 中工作,要遍历 map 生成的可迭代对象中的元素,我只需要编写:

new CFMap(whateverFunction).apply(whateverIterable)

但是我想写的是这样的:

whateverIterable.bmap(whateverFunction)

正如 Scala 中常见的那样('b' 用于缓冲),或者类似:

whateverThing.toBuffered.map(whateverFunction)

它们中的任何一个都对我有用。所以问题是,我怎样才能在 Scala 中以惯用的方式做到这一点?一些选项:

  • Monads:创建一个新的集合“缓冲”,这样我就可以使用 toBuffered 方法(应该作为隐式添加到以前的方法中)并实现map这个缓冲的东西以及其他所有东西(听起来像是相当多的工作)。
  • Implicits:创建一个隐式方法,将通常的集合或它们的超类(我不确定它应该是哪个,Iterable也许?)转换为我可以应用该.bmap方法并从中获取某些东西的其他东西,可能是可迭代的。
  • 其他:到目前为止,我可能还没有考虑很多选项。有可能某些库已经实现了这一点(我实际上对相反的情况感到惊讶,我不敢相信以前没有人想到这一点)。使用已经完成的东西通常是一个好主意。

如果有不清楚的地方,请告诉我。

4

1 回答 1

1

您正在寻找的是“pimp-my-library”模式。一探究竟:

object CFMapExtensions {
  import sanity.commons.functional.CFMap
  import scala.collection.JavaConversions._

  implicit class IterableExtensions[I](i: Iterable[I]) {
    def bmap[O](f: Function1[I, O]): Iterable[O] = new CFMap(f).apply(asJavaIterable(i))
  }

  implicit class JavaIterableExtensions[I](i: java.lang.Iterable[I]) {
    def bmap[O](f: Function1[I, O]): Iterable[O] = new CFMap(f).apply(i)
  }

  // Add an implicit conversion to a java function.
  import java.util.function.{Function => JFunction}
  implicit def toJFunction[I, O](f: Function1[I, O]): JFunction[I, O] = {
    new JFunction[I, O]() {
      def apply(t: I): O = f(t)
    }
  }
}

object Test extends App {
  import CFMapExtensions._
  List(1,2,3,4).bmap(_ + 5).foreach(println)
}
于 2014-10-29T14:55:21.240 回答