2016-03-03 105 views
2

这可能是一个天真的问题。我有一个案例类“书”,定义如下:理解scala中的列表/集合

case class Book(title : String, authors : List[String]) 

在我的主要方法,我定义了几本书记录如下:

val books = List(
    Book(title = "Book1", authors = List("Author1", "Author2")), 
    Book(title = "Book2", authors = List("Author3", "Author4")), 
    Book(title = "Book3", authors = List("Author2", "Author5")), 
    Book(title = "Book4", authors = List("Author6", "Author3")), 
    Book(title = "Book5", authors = List("Author7", "Author8")), 
    Book(title = "Book6", authors = List("Author5", "Author9")) 
) 

我写一个查询检索的名字作家谁已撰写多本书籍,以及我的查询如下:

val authorsWithMoreThanTwoBooks = 
     (for { 

     b1 <- books 
     b2 <- books 
     if b1.title != b2.title 
     a1 <- b1.authors 
     a2 <- b2.authors 
     if a1 == a2 
     } yield a1) 

println(authorsWithMoreThanTwoBooks) 

这将打印List(Author2, Author3, Author2, Author5, Author3, Author5)(两次出现的作者这是相当期待的名字,因为每对图书将TA ken两次,如(b1,b2)和(b2,b1))。

当然,我可以用distinct来解决这个问题,但另一种方式是创建一个列表中未记录,但在设置

输出后
val books = Set(
    Book(title = "Book1", authors = List("Author1", "Author2")), 
    Book(title = "Book2", authors = List("Author3", "Author4")), 
    Book(title = "Book3", authors = List("Author2", "Author5")), 
    Book(title = "Book4", authors = List("Author6", "Author3")), 
    Book(title = "Book5", authors = List("Author7", "Author8")), 
    Book(title = "Book6", authors = List("Author5", "Author9")) 
) 

for表达和printlnSet(Author5, Author2, Author3)

为什么会发生这种行为?为什么Set上的for表达式会生成另一个Set而不是List?如果有需要的话,是否有可能获得相关作者的List重复值?

+0

如何设置可以包含重复? – Nyavro

+0

他们不能,那就是我的意思。我错过了什么吗? –

+0

我的问题是,为什么我们从'books'的后一个定义中的for表达式获取Set,而不是List。相应地更新我的问题。 –

回答

2

在Scala中,for表达式实际上是由编译器翻译成的高阶函数组合:

  • 使用for能产生一些被翻译成的地图flatMap,和组合withFilter

  • 使用for不产生任何东西被翻译成withFilter的foreach

如果你不熟悉这个,也许你应该看看它的地方(here's一个选项)。

所以,你的结构

for { 
    b1 <- books 
    b2 <- books 
    if b1.title != b2.title 
    a1 <- b1.authors 
    a2 <- b2.authors 
    if a1 == a2 
} yield a1 

被翻译成

books.flatMap(b1 => books 
    .filter(b2 => b1.title != b2.title) 
    .flatMap(b2 => b1.authors 
    .flatMap(a1 => b2.authors 
    .filter(a2 => a1 == a2) 
    .map(a2 => a1)))) 

Flatmapping 2 Sets生成SetLists的平面映射Set也生成Set。实际上这比它看起来更多(它回到类别理论,因为Set在这种情况下是monad,flatMap是其自然转换之一,但这只是一个离题)。

,无论如何,我不知道你的最后一个问题是什么意思,但如果你想保持原来的books集合作为一个Set和使用for表达得到重复,你可以简单地调用之前书本上的.toList对他们进行操作。这样整个表达与List而不是Set工作。

P.S.

这表明for表达式的本质更接近于函数式编程结构,如monads和functor(分别使用flatMap和map进行操作),而不是传统的for循环。与典型的命令式编程构造的经典循环不同,对于Scala中的表达式来说,它们是完全功能性的构造,因为它们是一系列高阶函数map,flatMap,filterforeach。请注意,这意味着您可以使用不仅包含集合的表达式,而且还支持那些支持这些函数的任何内容(例如,您可以使用OptionFuture)。这根本不是一个天真的问题,如果到目前为止你还没有意识到这一点,了解这一点很重要。当然,你不需要能够在半夜将任何给定的表达式转换为地图和flatMaps的链接,但是您应该意识到它将这些功能“隐藏起来”。