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」方法。用這樣一個簡單的例子,你可以更容易地使用替換,並快速瞭解爲什麼某些解決方案比其他解決方案明顯更糟! 希望它會幫助一些!如果你有話,告訴我!