簡體   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