【问题标题】:Idiomatic Scala - Performing Functions on Tuple Values惯用的 Scala - 对元组值执行函数
【发布时间】:2013-01-30 23:43:28
【问题描述】:

各位, 我已经定义了以下地图和结构类型:

type Mergeable = { def mergeFrom(data: Array[Byte]): com.google.protobuf.GeneratedMessageLite }
  val dispatchMap = Map(
    1 -> (ZombieSighting.defaultInstance.asInstanceOf[Mergeable], "akka://UlyssesAgenda/user/ServerCore/DispatchTarget")
  )

基本上我正在做的是定义一个映射,上面写着“当我从网络中读取 protobuf 消息类型 1 时,从字节中创建一个 ZombieSighting,并将其分派给在指定字符串中找到的参与者”。

所以,这就是我今天的代码,它创建一条消息、一个参与者,并将消息发送给参与者:

val dispatchInfo = dispatchMap.get(messageType)
val theMessage = dispatchInfo.map { _._1.mergeFrom(theBytes) }.get
val actorPath = dispatchInfo.map { _._2 } 
val targetActor = actorPath.map { SocketServer.system.actorFor(_) }
targetActor.map { _ ! theMessage }

当我看到这个时,我能想到的就是这看起来有多少行非函数式编程,我不禁想到有一种更优雅的方法可以做到这一点。我知道我可以编写将元组作为参数并返回修改后的元组的函数,但我认为这对我来说并没有什么干净的惯用 scala。

我的直觉告诉我,我可以在“dispatchMap.get(messageType)”上运行一个 map 语句,它会给我一个基于 tuple._2 的 Akka actor 实例和一个基于 tuple._1 的合并 protobuf 对象。

有人可以建议一种方法来拉皮条此代码并减少行数并使其更具功能性吗?例如

val (targetActor, theMessage) = ????
targetActor ! theMessage

编辑:这是我第一次尝试重构。这是“Scala 方式”吗?

val (msg, actor) = dispatchMap.get(messageType).map {
    case (m:Mergeable, path:String) => 
        (m.mergeFrom(theBytes), SocketServer.system.actorFor(path) )
}.get
actor ! msg

编辑2:这是下面评论者建议的版本,但我不知道这是否真正地道:

  def dispatchMessage: IO.Iteratee[Unit] =
    repeat {
    for {
        (messageType, byteString) <- readMessage
    } yield {
        for (
            (protoMessage, actorPath) <- dispatchMap.get(messageType);
            theMessage = protoMessage.mergeFrom(byteString.toArray);
            targetActor = SocketServer.system.actorFor(actorPath) )
        yield 
            targetActor ! theMessage
    }
}

?

【问题讨论】:

  • 基本上,在不知道类型的情况下,几乎不可能诊断出故障。编译器诊断可能会有所帮助,但您也没有包含它们。也许熟悉 Protocol Buffers 的人可能会弄清楚。
  • 我的问题不在于协议缓冲区。我的问题只是弄清楚如何以 Scala 风格的方式从我的 dispatchMap 对象中获取有用的信息。我已经弄清楚了所有 protobuf 的东西。
  • 当然,但是如果没有大量源自协议缓冲区的细节,就不可能解释或分析您的代码。还有……repeat是什么?
  • 对不起,我没有提供一堆额外的细节。我不想混淆这个问题。我唯一的问题是如何优化地图理解,我认为人们不需要血淋淋的细节

标签: scala collections map tuples


【解决方案1】:

你的代码可能是这样写的:

val akkaResponseFuture =
  for ((bytes, actorPath) <- dispatchMap.get(messageType);
       theMessage         <- bytes.mergeFrom(theBytes);
       targetActor        =  SocketServer.system.actorFor(actorPath))
  yield
    targetActor ! theMessage

这很可能是不对的。我没有尝试创建工作代码就做到了,因为您的示例不是独立的。此外,theBytes 也没有定义。

但我很确定您可以使用 for 理解来澄清和简化它。

【讨论】:

  • @KevinHoffman:无意冒犯,但您的原始问题很难阅读,但评论中的多行代码是不可能的。你会用这个回复修改你的问题吗?
  • 我在过去的 5 分钟里试图弄清楚如何将代码放入评论中。我将编辑原件 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-21
  • 1970-01-01
  • 2021-07-22
  • 1970-01-01
  • 2015-02-07
  • 2013-09-23
相关资源
最近更新 更多