我有一个像这样的功能
def runOnce(request: Request): Future[Result] = {
}
当我调用此runOnce函数时,如果它尚未运行,我希望它运行一些方法并返回该结果。如果它已经运行,我只是想让它返回原始结果(进入的请求将是相同的)。
如果我没有像这样的参数
,我可以这样做lazy val hydratedModel = hydrateImpl(request)
future for efficient filtering
def fetchHydratedModel(): Future[HydratedModelRequest] = {
hydratedModel
}
第一种情况怎么做?
答案 0 :(得分:4)
这个问题的一般解决方案是功能记忆;对于纯函数(一个没有副作用的函数 - 它不适用于非纯函数),对于同一组参数值,函数调用的结果应始终相同。因此,优化是在第一次调用时缓存该值并将其返回以用于后续调用。
您可以使用以下内容实现此目的(具有单个参数的纯函数的memoization类,更新 - 请参阅下面的注释 - 以使其成为线程安全的):
/** Memoize a pure function `f(A): R`
*
* @constructor Create a new memoized function.
* @tparam A Type of argument passed to function.
* @tparam R Type of result received from function.
* @param f Pure function to be memoized.
*/
final class Memoize1[A, R](f: A => R) extends (A => R) {
// Cached function call results.
private val result = scala.collection.mutable.Map.empty[A, R]
/** Call memoized function.
*
* If the function has not been called with the specified argument value, then the
* function is called and the result cached; otherwise the previously cached
* result is returned.
*
* @param a Argument value to be passed to `f`.
* @return Result of `f(a)`.
*/
def apply(a: A) = synchronized(result.getOrElseUpdate(a, f(a)))
}
/** Memoization companion */
object Memoize1 {
/** Memoize a specific function.
*
* @tparam A Type of argument passed to function.
* @tparam R Type of result received from function.
* @param f Pure function to be memoized.
*/
def apply[A, R](f: A => R) = new Memoize1(f)
}
假设您正在记忆的功能是hydrateImpl
,您可以按如下方式定义和使用runOnce
(请注意,它变为val
而不是def
}):
val runOnce = Memoize1(hydrateImpl)
runOnce(someRequest) // Executed on first call with new someRequest value, cached result subsequently.
更新:关于线程安全。
在回复user1913596
的评论时,答案是" no&#34 ;; scala.collection.mutable.Map.getOrElseUpdate
不是线程安全的。但是,同步访问权限相当简单,我相应地更新了原始代码(将调用嵌入sychronized(...)
)。
锁定访问的性能损失应该通过改进的执行时间来抵消(假设f
是非常重要的)。
答案 1 :(得分:0)
根据您的设置,可能有更好的方法来执行此操作,但一个简单的解决方案是执行以下操作
private var model: Option[Future[HydratedModelRequest]] = None
def runOnce(request: Request): Future[Request] = {
if (model.isEmpty) {
model = hydrateImpl(request)
}
model.get
}
如果request
对于每次调用确实相同,则另一种选择是隐式地要求请求并且懒惰地补充。
implicit val request: Request
lazy val hydratedRequest: Future[HydratedModelRequest] = hydrateImpl(request)