到目前为止,我正在努力解决以下问题,需要一些建议。
def query(title: String): List[Search] // query("Terminator") => ["Terminator I", "Terminator II", "Terminator 1984", etc...]
def searchIMDB(s: Search): List[SearchResult]
def searchTMDB(s: Search): List[SearchResult]
def filterRedundantSearchResults(sr: SearchResult): Option[SearchResult]
def fetchIMDB(sr: SearchResult): List[MetaInfo]
def fetchTMDB(sr: SearchResult): List[MetaInfo]
def consolidate(infos: List[MetaInfo]): List[List[MetaInfo]]
我想构建一个像:
这样的管道query("Terminator")
-> [askIMDB, askTMDB, ...]
-> filterRedundantSearchResults (already-searched-state per query)
-> [fetchIMDB, fetchTMDB, ...]
-> consolidate (collected-meta-infos-state per query)
=> List[ TerminatorI-List[MetaInfo], TerminatorII-List[MetaInfo], ...]
到目前为止,我已将每个Pipeline-Segment实现为Actor。 我需要为每个Query创建专用的actor实例,因为像filterXXX和mergeate这样的一些actor需要维护每个查询的状态。
像askIMDB这样的函数会产生多个我希望同时处理的结果(每个结果都是一个单独的actor)。所以我没有找到任何方法在执行query()之前预构建actor 的整个图形,而且没有一种在运行时修改它的优雅方法。
我的第一次尝试是一系列演员,并在消息中传递像Transaction-ID一样,所以每个Actor都有一个Map [TransactionID-> State],但这感觉相当难看。 第二个尝试是创建一个排序管道,将演员的有向图抽象为一个流程,但到目前为止我失败了。
这是我的第一篇文章,对不起,如果我忘记了某些内容,或者问题是一般/伪编码。任何建议非常感谢。谢谢!
答案 0 :(得分:4)
我建议你看看ScalaQuery,它做同样的事情。它可以这样做,因为这是一个单子问题。实际上,由Scalaz library实现的一些Haskell解决方案(如Arrows)似乎非常接近。
这将是最好的解决方案,因为适当的抽象将在未来使更改变得更容易。
作为一个黑客,我想这样的事情:
abstract class QueryModifiers
case object Consolidate extends QueryModifiers
// create others as appropriate
class Query(title: String) {
self =>
// Create actors
def createActor(qm: QueryModifiers): Actor = {
val actor = qm match {
case Consolidate => // create a consolidator actor
case //... as needed
}
actor.start
actor
}
// The pipeline
val pipe: List[List[QueryModifiers]] = Nil
// Build the pipeline
def ->(qms: List[QueryModifiers]) = new Query(title) {
override val pipe = qms :: self.pipe
}
def ->(qm: QueryModifiers) = new Query(title) {
override val pipe = List(qm) :: self.pipe
}
def ->(c: Consolidate.type) = {
// Define the full pipeline
// Because the way pipe is built, the last layer comes first, and the first comes last
val pipeline = Consolidate :: pipe
// Create an actor for every QueryModifier, using an unspecified createActor function
val actors = pipeline map (_ map (createActor(_))
// We have a list of lists of actors now, where the first element of the list
// was the last QueryModifiers we received; so, group the layers by two, and for each
// pair, make the second element send the result to the first.
// Since each layer can contain many actors, make each member of the second
// layer send the results to each member of the first layer.
// The actors should be expecting to receive message SendResultsTo at any time.
for {
List(nextLayer, previousLayer) <- actors.iterator sliding 2
nextActor <- nextLayer
previousActor <- previousLayer
} previousActor ! SendResultsTo(nextActor)
// Send the query to the first layer
for ( firstActor <- actors.last ) firstActor ! Query(title)
// Get the result from the last layer, which is the consolidator
val results = actors.head.head !? Results
// Return the results
results
}
}
修改强>
你可以保证订购,有点诀窍。我试图在这里避免使用Scala 2.8,虽然它可以使命名和默认参数更容易。
sealed abstract class QueryModifiers
case class QMSearcher(/*...*/) extends QueryModifiers
case class QMFilter(/*...*/) extends QueryModifiers
case class QMFetcher(/*...*/) extends QueryModifiers
case object Consolidate extends QueryModifiers
class Query[NextQM] private (title: String, searchers: List[QMSeacher], filters: List[QMFilter], fetchers: List[QMFetcher]) {
// Build the pipeline
def ->[T <: NextQM](qms: List[NextQM])(implicit m: Manifest[T]) = m.toString match {
case "QMSearch" => new Query[QMFilter](title, qms, Nil, Nil)
case "QMFilter" => new Query[QMFetcher](title, seachers, qms, Nil)
case "QMFetcher" => new Query[Consolidate.type](title, searches, filters, qms)
case _ /* "Consolidate$", actually */ => error("List of consolidate unexpected")
}
// Do similarly for qm: NextQM
// Consolidation
def ->(qm: Consolidate.type) = {
// Create Searchers actors
// Send them the Filters
// Send them Fetchers
// Create the Consolidator actor
// Send it to Searchers actors
// Send Searchers the query
// Ask Consolidator for answer
}
}
object Query {
def apply(title: String) = new Query[QMSearcher](title, Nil, Nil, Nil)
}
现在,搜索者演员保留了过滤器列表,获取者列表以及对整合者的引用。他们会收听消息,告知他们这些事情以及查询。对于每个结果,他们为列表中的每个过滤器创建一个Filter actor,向每个过滤器发送一个fetchers和mergeator列表,然后将结果发送给他们。
过滤器参与者会保留一个记录器列表和一个对整合器的引用。他们听取消息告知他们这些事情,以及搜索者的结果。他们将输出(如果有的话)发送给新创建的提取者演员,他们首先被告知合并者。
Fetchers会引用整合者。他们听取一条消息,通知他们该引用,以及过滤器的结果。他们反过来将结果发送给整合者。
整合者听两条消息。来自抓取者演员的一条消息告诉他们他们积累的结果。来自Query的另一条消息要求返回该结果。
唯一剩下的就是设法让合并者知道所有结果都已经处理完毕。一种方法是: