繁体   English   中英

如何忠实于Scala中的函数式表达

[英]How to stay true to functional style in Scala for expressions

当我需要将一个对象的多个参数收集到一个列表中时,我一直在努力寻找一种方法来忠实于表达式的功能风格。

例如,假设我有一个Notification对象,它同时具有fromId(通知来自的用户ID)和objectOwnerId(创建原始对象的用户的ID)。 这些在Facebook样式通知中可能会有所不同(“ X也评论了Y的帖子”)。

我可以使用for表达式收集userIds,如下所示

val userIds = for { notification <- notifications } yield notification.fromId

但是说我想将fromIds和objectOwnerIds都收集到一个列表中,有没有办法在没有vars用户的情况下在单个for表达式中做到这一点?

我过去做过这样的事情:

var ids = List()
for {
    notification <- notifications
    ids = ids ++ List(notification.fromId, notification.objectOwnerId)
}
ids = ids.distinct

但感觉必须有更好的方法。 使用var以及在我完成收集后需要调用unique都是很丑的。 我可以通过一些条件来避免出现区别,但是我正在尝试学习执行操作的正确函数方法。

在此先感谢您的帮助!

在这种情况下,存在foldLeft:

(notifications foldLeft Set.empty[Id]) { (set, notification) =>
  set ++ Seq(notification.fromId, notification.ownerId)
}

或简称:

(Set.empty[Id] /: notifications) { (set, notification) =>
  set ++ Seq(notification.fromId, notification.ownerId)
}

一组不包含重复项。 折叠后,您可以根据需要将集合转换为另一个集合。

val userIds = for { 
  notification <- notifications 
  id <- List(notification.fromId, notification.objectOwnerId)
} yield id

如有需要,请在之后应用distinct 如果ID只能在单个通知被复制,你可以申请distinct第二发生器来代替。

当然,不仅仅是产生fromId,而是产生一个元组

val idPairs:List[(String, String)] = for(notification <- notifications) yield(notification.fromId, notification.objectOwnerId)

好吧,这是我对以下内容的回答:

如何从[Y(x1,x2),Y(x3,x4)]映射到[x1,x2,x3,x4]?

使用flatMap (请参阅Collection.Traversable ,但请注意,它实际上是在上flatMap )。

case class Y(a: Int, b: Int)
var in = List(Y(1,2), Y(3,4))
var out = in.flatMap(x => List(x.a, x.b))

> defined class Y
> in: List[Y] = List(Y(1,2), Y(3,4))
> out: List[Int] = List(1, 2, 3, 4)

另外,因为for..yieldfiltermapflatMap的一个 (还请参见“ flatMap的糖吗?” ,它指出效率不如可能:有一个额外的map ):

var out = for { i <- in; x <- Seq(i.a, i.b) } yield x

但是,我可能会选择其他答案之一 ,因为这不能直接解决要解决的最终问题。

快乐的编码。

您还可以使用Stream将对转换为单个项目的流:

def toStream(xs: Iterable[Y]): Stream[Int] = {
  xs match {
    case Y(a, b) :: t => a #:: b #:: toStream(t)
    case _ => Stream.empty
  }
}

就像pst所说的那样,这并不能解决您获得唯一值的最终问题,但是一旦有了流,它就变得微不足道了:

val result = toStream(ys).toList.removeDuplicates

或对先前使用flatten的建议进行了一些修改-添加一个将Y转换为List的函数:

def yToList(y: Y) = List(y.a, y.b)

然后,您可以执行以下操作:

val ys = List(Y(1, 2), Y(3, 4))
(ys map yToList flatten).removeDuplicates

我同意Dave的解决方案,但另一种方法是将其折叠在列表上,并随即生成ID到User对象的映射。 折叠中要应用的功能将查询两个用户的数据库,并将它们添加到要累积的地图中。

简单map呢? 无论如何, for yield AFAIK都会转换为一系列flatMapmap 您的问题可以简单地通过以下方式解决:

notifications.map(n => (n.fromId, n.objectOwnerId)).distinct

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM