3

“斯卡拉函数式编程原则” @ coursera在球场上的第3周的分配工作时,我发现,当我实现的功能结合如图所示的视频课程:coursera progfun1:斯卡拉工会性能

override def union(that: TweetSet): TweetSet = { 
    left union(right) union(that) incl(elem) 
    } 

它在执行过程中花费的时间太长,但是当我实现它是这样的:

override def union(that: TweetSet): TweetSet = { 
    right.union(left.union(that)).incl(elem) 
    } 

它在执行过程中花费较少的时间,我得到满分。

问题是我无法弄清这两个实现之间有什么区别,它是如何比另一个更快的呢?

用于分配(与所用的数据结构的实施方式)中给出的代码是:

package objsets 

import TweetReader._ 

/** 
* A class to represent tweets. 
*/ 
class Tweet(val user: String, val text: String, val retweets: Int) { 
    override def toString: String = 
    "User: " + user + "\n" + 
    "Text: " + text + " [" + retweets + "]" 
} 

/** 
* This represents a set of objects of type `Tweet` in the form of a binary search 
* tree. Every branch in the tree has two children (two `TweetSet`s). There is an 
* invariant which always holds: for every branch `b`, all elements in the left 
* subtree are smaller than the tweet at `b`. The elements in the right subtree are 
* larger. 
* 
* Note that the above structure requires us to be able to compare two tweets (we 
* need to be able to say which of two tweets is larger, or if they are equal). In 
* this implementation, the equality/order of tweets is based on the tweet's text 
* (see `def incl`). Hence, a `TweetSet` could not contain two tweets with the same 
* text from different users. 
* 
* 
* The advantage of representing sets as binary search trees is that the elements 
* of the set can be found quickly. If you want to learn more you can take a look 
* at the Wikipedia page [1], but this is not necessary in order to solve this 
* assignment. 
* 
* [1] http://en.wikipedia.org/wiki/Binary_search_tree 
*/ 
abstract class TweetSet { 

    /** 
    * This method takes a predicate and returns a subset of all the elements 
    * in the original set for which the predicate is true. 
    * 
    * Question: Can we implment this method here, or should it remain abstract 
    * and be implemented in the subclasses? 
    */ 
    def filter(p: Tweet => Boolean): TweetSet = ??? 

    /** 
    * This is a helper method for `filter` that propagetes the accumulated tweets. 
    */ 
    def filterAcc(p: Tweet => Boolean, acc: TweetSet): TweetSet 

    /** 
    * Returns a new `TweetSet` that is the union of `TweetSet`s `this` and `that`. 
    * 
    * Question: Should we implment this method here, or should it remain abstract 
    * and be implemented in the subclasses? 
    */ 
    def union(that: TweetSet): TweetSet = ??? 

    /** 
    * Returns the tweet from this set which has the greatest retweet count. 
    * 
    * Calling `mostRetweeted` on an empty set should throw an exception of 
    * type `java.util.NoSuchElementException`. 
    * 
    * Question: Should we implment this method here, or should it remain abstract 
    * and be implemented in the subclasses? 
    */ 
    def mostRetweeted: Tweet = ??? 

    /** 
    * Returns a list containing all tweets of this set, sorted by retweet count 
    * in descending order. In other words, the head of the resulting list should 
    * have the highest retweet count. 
    * 
    * Hint: the method `remove` on TweetSet will be very useful. 
    * Question: Should we implment this method here, or should it remain abstract 
    * and be implemented in the subclasses? 
    */ 
    def descendingByRetweet: TweetList = ??? 

    /** 
    * The following methods are already implemented 
    */ 

    /** 
    * Returns a new `TweetSet` which contains all elements of this set, and the 
    * the new element `tweet` in case it does not already exist in this set. 
    * 
    * If `this.contains(tweet)`, the current set is returned. 
    */ 
    def incl(tweet: Tweet): TweetSet 

    /** 
    * Returns a new `TweetSet` which excludes `tweet`. 
    */ 
    def remove(tweet: Tweet): TweetSet 

    /** 
    * Tests if `tweet` exists in this `TweetSet`. 
    */ 
    def contains(tweet: Tweet): Boolean 

    /** 
    * This method takes a function and applies it to every element in the set. 
    */ 
    def foreach(f: Tweet => Unit): Unit 
} 

class Empty extends TweetSet { 
    def filterAcc(p: Tweet => Boolean, acc: TweetSet): TweetSet = ??? 

    /** 
    * The following methods are already implemented 
    */ 

    def contains(tweet: Tweet): Boolean = false 

    def incl(tweet: Tweet): TweetSet = new NonEmpty(tweet, new Empty, new Empty) 

    def remove(tweet: Tweet): TweetSet = this 

    def foreach(f: Tweet => Unit): Unit =() 
} 

class NonEmpty(elem: Tweet, left: TweetSet, right: TweetSet) extends TweetSet { 

    def filterAcc(p: Tweet => Boolean, acc: TweetSet): TweetSet = ??? 


    /** 
    * The following methods are already implemented 
    */ 

    def contains(x: Tweet): Boolean = 
    if (x.text < elem.text) left.contains(x) 
    else if (elem.text < x.text) right.contains(x) 
    else true 

    def incl(x: Tweet): TweetSet = { 
    if (x.text < elem.text) new NonEmpty(elem, left.incl(x), right) 
    else if (elem.text < x.text) new NonEmpty(elem, left, right.incl(x)) 
    else this 
    } 

    def remove(tw: Tweet): TweetSet = 
    if (tw.text < elem.text) new NonEmpty(elem, left.remove(tw), right) 
    else if (elem.text < tw.text) new NonEmpty(elem, left, right.remove(tw)) 
    else left.union(right) 

    def foreach(f: Tweet => Unit): Unit = { 
    f(elem) 
    left.foreach(f) 
    right.foreach(f) 
    } 
} 

trait TweetList { 
    def head: Tweet 
    def tail: TweetList 
    def isEmpty: Boolean 
    def foreach(f: Tweet => Unit): Unit = 
    if (!isEmpty) { 
     f(head) 
     tail.foreach(f) 
    } 
} 

object Nil extends TweetList { 
    def head = throw new java.util.NoSuchElementException("head of EmptyList") 
    def tail = throw new java.util.NoSuchElementException("tail of EmptyList") 
    def isEmpty = true 
} 

class Cons(val head: Tweet, val tail: TweetList) extends TweetList { 
    def isEmpty = false 
} 


object GoogleVsApple { 
    val google = List("android", "Android", "galaxy", "Galaxy", "nexus", "Nexus") 
    val apple = List("ios", "iOS", "iphone", "iPhone", "ipad", "iPad") 

    lazy val googleTweets: TweetSet = ??? 
    lazy val appleTweets: TweetSet = ??? 

    /** 
    * A list of all tweets mentioning a keyword from either apple or google, 
    * sorted by the number of retweets. 
    */ 
    lazy val trending: TweetList = ??? 
    } 

object Main extends App { 
    // Print the trending tweets 
    GoogleVsApple.trending foreach println 
} 

回答

2

我发现了一个说明here.

基本上当我们做

left union(right) union(that) incl(elem) 

第一left union (right) 被处理,然后union(that)被处理, 因此我们将第二个union左侧的树放大,这会花费更多时间来完成递归,因为当union的左侧参数为空时递归结束(请检查Empty类中的union的实现)。

2

我发布了一个解释here

下面是它的内容:

一些符号:

根:树的根元素。 左/右:要么,如果我们讲一个联盟,元素,如果我们讲的左/右树“含左”

A的含义(左联盟(右联盟(其他含ELEM)))

第一:您即可享受在其他当前访问节点(此发掘树,再往右边叶子,你的项目添加到其他没有必要要求工会在里面。)

二:你重复该步骤与正确的子树。

第三步:用左子树重复该步骤。

全局意义:每一次,您将您当前的elem添加到其他,然后您尝试去正确。如果可以的话,你可以将正确的元素添加到其他元素,并再次添加,直到你不能走向正确。然后,你试着向左走......你可以吗?再往右走!你不能?不能左转?原路返回。

你可以看到这是一个“优先运动”。每次你添加你的物品,那么首选你去正确的,然后离开,然后回去重复!通过这样做,您只探索整棵树,并将每个节点添加到其他节点!((左联盟右)工会等),包括ELEM(或其他左向右工会联盟)

B.含义

只是笑。简而言之,你想添加你现有的物品,你可以现在添加,在最后一步可能。但这不是最糟糕的部分。当你打电话(离开工会的权利),你现在正在将左边的项目添加到右边的子树,通过你以前做的同样低效率的方式。这意味着:您还没有将elem添加到其他,但您必须将left.item包含在右侧。然后,因为您会打电话给(left.left union left.right),所以必须将left.left.item包括到left.right ..每次您做A.union(B)时,通过复制来删除A的一个项目它完全(而不是像一个包含方法返回的不可变集合那样的智能副本),然后将它添加到B.但是由于删除A的项目需要调用A.left.union(A.right),您将首先复制A.left/A.right ...等等。如果你可以想象一棵树,就像把每个左兄弟收集到它的右兄弟那样,并且每次你只想添加一个物品给另一个。

一些注意事项:

如果你可以说一个empty.union(即)=,你可以说,NonEmpty.union(即:TweetSet)=如果是空的,则此则(((工会...)..)其他包括elem)。这就是方法和Empty/NonEmpty模式的问题,你不能在一个方法中集中这两个基本情况,在这里,我们很多人在空的情况下实现了第一个,但在NonEmpty中却忘了另一个。总是要确定,如果A.f(b)是对称的(= b.f(A)),那么你就可以实现两种基本情况。 识别并直接进入基本情况。然后,从它递归到你的全局解决方案。对于“left union right union other elem”,基本情况是其他elem,因为您不希望您的替换结束“Empty incl n1 incl n2 incl ...”。所以直接关注它(其他包括elem)。最后,但更重要的是:直觉!例如,如果你在我的解释中遇到困难,想象一下你可以写成(copy right)(包括elem)或(left copy(right incl elem))的“copy”方法。用这样一个简单的例子,你可以更容易地使用替换,并快速了解为什么某些解决方案比其他解决方案明显更糟! 希望它会帮助一些!如果你有话,告诉我!