3

bei Arbeiten an der Zuordnung der Woche 3 des Kurses ‚Functional Programming Principles in Scala‘ @ Coursera fand ich heraus, dass, wenn ich die Funktion Union umzusetzen, wie im Video-Kurs gezeigt:Coursera progfun1: scala Vereinigung Leistung

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

es dauert zu lange während der Ausführung, aber wenn ich es auf diese Weise implementieren:

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

es weniger Zeit, während der Ausführung dauert und ich volle Punktzahl.

das Problem ist, dass ich nicht herausfinden kann, was ist der Unterschied zwischen diesen beiden Implementierungen, wie ist man schneller als die anderen?

der Code für die Zuordnung (mit den Implementierungen der verwendeten Datenstrukturen) gegeben ist:

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 
} 

Antwort

2

I here.

im Grunde eine Erklärung gefunden, wenn wir

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

zuerst tun left union (right) wird verarbeitet, dann wird union(that) verarbeitet, also machen wir den Baum auf der linken Seite des zweiten union größer, was mehr Zeit braucht, um die Rekursion zu beenden, da die Rekursion endet, wenn das linke Argument von union leer ist (überprüfen Sie die Implementierung von union in der Klasse Empty).

2

gab ich eine Erklärung here

Hier ist sein Inhalt:

Einige Notation:

Root: Das Wurzelelement des Baumes. Links/Rechts: Entweder die linke/rechte Baum, wenn wir von einer Gewerkschaft, Element sprechen, wenn wir von der „inkl links“

A. Bedeutung der (linke Vereinigung (rechte Vereinigung (andere incl Elem))) sprechen

Erstens: Sie inkl den aktuellen besuchten Knoten in anderen (dies den Baum erkunden, das richtige Blatt nach unten gehen, und fügen Sie Ihren Artikel andere Keine Notwendigkeit Vereinigung in nennen.)

Zweitens: Sie wiederholen dieser Schritt mit dem rechten Teilbaum.

Drittens: Sie wiederholen diesen Schritt mit dem linken Teilbaum.

GLOBALE BEDEUTUNG: Jedes Mal, fügen Sie Ihr aktuelles Element zu anderen hinzu, und dann versuchen Sie, nach rechts zu gehen. Wenn Sie können, fügen Sie das richtige Element zu anderen hinzu, und wieder, bis Sie nicht nach rechts gehen können. Dann versuchst du nach links zu gehen ... Könntest du? Geh wieder nach rechts! Du kannst nicht? Kann ich auch nicht links gehen? BACKTRACK.

Sie können das als eine "priorisierte Bewegung" sehen. Jedes Mal, wenn Sie Ihren Gegenstand hinzufügen, gehen Sie nach rechts, dann nach links, dann gehen Sie zurück und wiederholen Sie! Indem Sie dies tun, erkunden Sie nur einmal den gesamten Baum und für jeden Knoten fügen Sie es zu anderen hinzu!

B. Bedeutung von ((links Gewerkschaft rechts) union andere) inkl Elem (oder linke Gewerkschaft rechts Vereinigung andere)

Nur lol. Kurz gesagt, Sie möchten den aktuellen Artikel hinzufügen, den Sie JETZT hinzufügen können, im letzten Schritt, MÖGLICH. Aber das ist nicht der schlechteste Teil. Wenn Sie anrufen (linke Vereinigung rechts), fügen Sie jetzt das linke Element dem rechten Teilbaum auf die gleiche ineffiziente Weise hinzu, die Sie zuvor gemacht haben. Was bedeutet: Sie haben elem noch nicht zu anderen hinzugefügt, aber Sie müssen left.item nach rechts einbeziehen. Und dann, da du anrufst (left.left union left.right), musst du left.left.item nach left.right einfügen. Jedes Mal, wenn du A.union (B) machst, entfernst du einen Gegenstand von A durch Kopieren es vollständig (und nicht eine intelligente Kopie wie die unveränderliche Menge von einer Incl-Methode zurückgegeben) und fügen Sie es dann zu B. Aber seit das Entfernen des Elements von A muss A.left.union (A.rechts) aufrufen, werden Sie zuerst haben A.left/A.right zu kopieren ... Und so weiter. Wenn Sie sich einen Baum vorstellen können, ist es so, als würde man jeden linken Bruder zu seinem rechten Bruder sammeln und jedes Mal, wenn Sie nur einen Gegenstand hinzufügen möchten.

EINIGE HINWEISE:

Wenn Sie sagen, dass ein empty.union (das) = ​​das, können Sie sagen, dass NonEmpty.union: Vereinigung (das TweetSet) = wenn das leer ist, dann diese dann (((...) ..) andere incl elem). Das ist das Problem mit Methoden und solchen Empty/NonEmpty-Mustern, Sie können diese beiden Basisfälle nicht in einer einzigen Methode zentralisieren, und hier implementieren viele von uns die erste in leer, aber vergessen die andere in NonEmpty. Stellen Sie IMMER sicher, dass, wenn A.f (b) symmetrisch ist (= b.f (A)), Sie beide Basisfälle implementiert haben Identifizieren Sie und gehen Sie direkt zu Ihrem Basisfall. Führen Sie dann die Rekursion von dieser zu Ihrer globalen Lösung durch. für "linker gewerkschaft rechter gewerkschaft other incl elem" ist das basis case ein anderes incl elem, da du deine ersetzung am ende nicht haben solltest "Empty incl n1 incl n2 incl ...". Konzentriere dich also darauf (andere inkl. Elem) direkt. zuletzt, aber noch wichtiger: Intuition !!! Benutze einen wirklich einfachen Fall, zum Beispiel, wenn du Schwierigkeiten mit meiner Erklärung hast, stelle dir das gleiche Argument für die "Kopier" -Methode vor, die du schreiben könntest als (linkes Kopierrecht) incl elem oder (linke Kopie (rechts incl elem)). Mit einem einfachen Beispiel wie diesem können Sie die Substitutionen einfacher und schneller verwenden, um zu sehen, warum manche Lösungen dramatisch am schlimmsten sind als andere! Hoffe es wird einigen helfen! Wenn Sie Bemerkungen haben, sagen Sie es mir!