【问题标题】:Port Haskell code using Control.Parallel to Scala使用 Control.Parallel 将 Haskell 代码移植到 Scala
【发布时间】:2013-04-02 18:10:19
【问题描述】:

下面的 Haskell 代码使用 parpseq 进行一些多核数字运算,作为玩具来显示正在使用的多个内核。在 Scala 中表达这一点的最简单和最惯用的方式是什么? Futures and Promises 看起来很有希望(咳咳),我一直在看scalaz.concurrent,例如this example,但我找不到文档来解释这一切。

import Control.Parallel

main = a `par` b `par` c `pseq` print (a + b + c)
  where
      a = ack 3 10
      b = fac 42
      c = fib 35

fac 0 = 1
fac n = n * fac (n-1)

ack 0 n = n+1
ack m 0 = ack (m-1) 1
ack m n = ack (m-1) (ack m (n-1))

fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

【问题讨论】:

  • 不应该强制 a、b 和 c 以实际执行并行工作吗?
  • 这不是必须的,至少在使用ghc -O2 -threaded Par.hs --make -fforce-recomp进行编译时
  • @jozefg:必须至少在打印总和后立即对它们进行评估。
  • @NiklasB。是的,我不确定 GHC 是否会将它们评估为 WHNF,然后在打印期间在单个核心上进行全面评估。
  • 哦,对了...我很愚蠢..谢谢你的澄清

标签: scala haskell concurrency multicore scalaz


【解决方案1】:

您可以像这样在 Scala 中翻译您的示例:

import concurrent.{Await, Future, future}
import concurrent.ExecutionContext.Implicits.global
import concurrent.duration.Duration

object Main extends App {

  val fac: Int => Int = {
    case 0 => 1
    case n => n * fac(n-1)
  }

  val ack: (Int, Int) => Int = {
    case (0, n) => n + 1
    case (m, 0) => ack (m-1, 1)
    case (m, n) => ack (m-1, ack(m, n-1))
  }

  val fib: Int => Int = {
    case 0 => 0
    case 1 => 1
    case n => fib(n-1) + fib(n-2)
  }

  val fa = future { ack(3, 10) }
  val fb = future { fac(42) }
  val fc = future { fib(35) }

  val x = for (((a, b), c) <- fa zip fb zip fc) yield (a + b + c)

  val result = Await.result(x, Duration.Inf) //awaiting synchronously after the result
  println(s"Value is: $result")

}

future { fib(3, 10) } 位将创建一个异步计算,该计算将在不同的执行线程上运行并返回一个Future 对象。然后,您可以使用 Future.sequence 将所有未来组合成一个大未来,该未来将提供所有结果的列表。

我们可以将后一个future的结果映射成结果之和,从而得到最终值。

有了这个最终的未来,我们可以做几件事。我们可以进一步组合它,或者我们可以在其上附加回调,或者我们可以同步等待指定的持续时间。在我的示例中,我在结果之后以同步方式等待无限长的时间。

【讨论】:

  • 谢谢!在将 ackfibfac 重写为尾递归并使用 BigInts 之后,这对我有用。
  • @jaybee 我很高兴。我有一种感觉,你可能需要 BigInt,但我没有专注于数值计算。
  • 顺便说一句,你为什么用val来介绍fac等人,而不是def?我很好奇选择一个而不是另一个的含义。
  • def 会在对象Main 中创建一个fac 方法。通过使用valfac 现在是一个函数,即Function1 特征的一个实例(实际上,因为我使用的是case 语法,它是PartialFunction 的一个实例)。你可以选择任何一种方式,但我使用了带有模式匹配的val 方法以更接近功能性感觉。
【解决方案2】:

我建议您使用期货,因为它们是自 2.10 以来标准库的一部分并且非常易于使用。我不会移植你的代码,而是给你一个例子,让你有个想法。

// not tailrec, will stack overflow for larger numbers
def fib(x: Int): Int = x match {
  case 0 | 1 => x
  case x => fib(x-1) + fib(x-2)
}

import scala.concurrent._
import ExecutionContext.Implicits.global

// create all the futures
val futures = Seq(future(fib(3)), future(fib(4)), future(fib(5)))

// make a Future[Seq[Int]] out of Seq[Future[Int]] and sum the ints
val sumFuture = Future.sequence(futures).map(_.sum)

// if the future is completed successfully print the result
sumFuture.onSuccess {
  case x => println(x)
}

【讨论】:

  • 非常感谢。我已经按照这些思路进行了一些工作,但使用 Await.result 等待功能完成。
猜你喜欢
  • 2011-03-27
  • 1970-01-01
  • 2015-08-20
  • 2011-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-14
相关资源
最近更新 更多