2016-10-03 44 views
2

我有一些关于Scala懒惰评估的问题。 这是示例代码:懒惰的评估步骤:过滤列表

val people=List(("Mark", 32), ("Bob", 22), ("Jane", 8), 
     ("Jill", 21), ("nick", 50), ("Nancy", 42), 
     ("Mike", 19), ("Sara", 12), ("Paula", 42), 
     ("John", 21)) 

def isOlderThan17(person: (String,Int)) = { 
    println(s"isOlderThan 17 called for $person") 
    val(_,age) = person 
    age > 17 
} 

def nameStartsWithJ(person: (String, Int)) = { 
    println(s"isNameStartsWithJ called for $person") 
    val (name,_) = person 
    name.startsWith("J") 
} 

println(people.view.filter(p => isOlderThan17(p)) 
        .filter(p => nameStartsWithJ(p)) 
        .last) 

输出:

isOlderThan 17 called for (Mark,32) 
isNameStartsWithJ called for (Mark,32) 
isOlderThan 17 called for (Bob,22) 
isNameStartsWithJ called for (Bob,22) 
isOlderThan 17 called for (Jane,8) 
isOlderThan 17 called for (Jill,21) 
isNameStartsWithJ called for (Jill,21) 
isOlderThan 17 called for (Mark,32)  //Here is the problem. 
isNameStartsWithJ called for (Mark,32) 
isOlderThan 17 called for (Bob,22) 
isNameStartsWithJ called for (Bob,22) 
isOlderThan 17 called for (Jane,8) 
isOlderThan 17 called for (Jill,21) 
isNameStartsWithJ called for (Jill,21) 
isOlderThan 17 called for (nick,50) 
isNameStartsWithJ called for (nick,50) 
isOlderThan 17 called for (Nancy,42) 
isNameStartsWithJ called for (Nancy,42) 
isOlderThan 17 called for (Mike,19) 
isNameStartsWithJ called for (Mike,19) 
isOlderThan 17 called for (Sara,12) 
isOlderThan 17 called for (Paula,42) 
isNameStartsWithJ called for (Paula,42) 
isOlderThan 17 called for (John,21) 
isNameStartsWithJ called for (John,21) 
(John,21) 

为什么评估必须(从“标记”回来了)“吉尔”后重新被发现?为什么它没有继续评估,直到列表结束?

回答

1

为什么在发现“Jill”后必须重新启动评估(从“标记”重新开始) ?

因为每个filter都会产生一个仅包含过滤项目的新集合。使用流来避免这种情况:

println(people.toStream 
       .filter(p => isOlderThan17(p)) 
       .filter(p => nameStartsWithJ(p)) 
       .last) 

Stream vs Views vs Iterators

流是一个懒惰的名单确实如此。事实上,在斯卡拉,Stream是一个列表 ,其尾部是一个懒惰的val。一旦计算出来,一个值将保持计算结果,并重新使用 。或者,如你所说,这些值被缓存。

在这种情况下,你只组成的过滤器,所以你可能也只是将二者结合起来谓词和做过滤只有一次:

println(people.filter(p => isOlderThan17(p) && nameStartsWithJ(p)) 
       .last) 
+0

如果OP只想找到最后一个元素'Stream'不是非常有效的内存。 –

+0

这种情况下的视图不会将过滤器方法组合成一个过滤器方法吗?我不认为每个过滤器都会在这个例子中产生一个新的集合。 – Samar

+0

过滤器总是会产生一个新的集合,只要查看'filter(p:(A)⇒Boolean):List [A]'的签名即可。 –

2

这似乎是关系到last在执行view。如果你做这种方式也不会重新启动:

people.view 
     .filter(isOlderThan17(_)) 
     .filter(nameStartsWithJ(_)) 
     .fold(None)((x, y) => Some(y)) 

看起来last试图访问head两次,这在你的情况下,意味着你需要找到的people.view.filter(isOlderThan17(_))第一要素两次,所以view必须重新计算它两次。

UPDATE

这里是last的定义TraversableLike

def last: A = { 
    var lst = head 
    for (x <- this) 
     lst = x 
    lst 
    } 

的第一个元素的确被访问两次。