3

при работе по заданию 3 недели курса «Принципы функционального программирования в Scala» @ Coursera я узнал, что, когда я реализую функцию объединения, как показано на видео-курс:Coursera progfun1: производительность Scala объединение

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 пуст (проверьте реализацию union в классе Empty).

2

я отправил объяснение here

Вот его содержание:

Некоторые обозначения:

Корень: Корневой элемент дерева. Left/Right: Либо левый/правый дерево, если мы говорим о союзе, элемент, если мы говорим о «вкл влево»

А. Значение (левого союза (правый союз (включая другие элем)))

Первое: Вы вкл текущий посещаемый узел в другой (это исследовать дерево, идти вниз правый лист, и добавить свой пункт в Другое нет необходимости называть объединение в нем.)

Второе: Вы повторяете этот шаг с правым поддеревом.

В-третьих: повторите этот шаг с левым поддеревом.

GLOBAL MEANING: Каждый раз, когда вы добавляете свой текущий элемент в другой, а затем вы пытаетесь идти вправо. Если вы можете, вы добавляете правильный элемент в другой, и снова, пока не сможете пойти правильно. Затем вы пытаетесь идти влево ... Вы могли бы? Иди прямо! Вы не можете? Не может пойти и налево? BACKTRACK.

Вы можете видеть это как «приоритетное движение». Каждый раз, когда вы добавляете свой предмет, тогда по предпочтению вы идете вправо, затем влево, затем возвращаетесь и повторяетесь! Делая это, вы исследуете только одно целое дерево, и для каждого узла вы добавляете его в другое!

B. Значение ((левый накидной правый) объединения других) вкл элем (или левого союза правого союза другой)

Просто лол. Вкратце, вы хотите добавить текущий элемент, который у вас есть, что вы можете добавить СЕЙЧАС в oher, НА ПОСЛЕДНИЙ ШАГ ВОЗМОЖНО. Но это не худшая часть. Когда вы вызываете (левый союз справа), вы теперь добавляете левый элемент в правое поддерево, тем же самым неэффективным способом, каким вы это делали раньше. Это означает: вы еще не включили elem в другие, но вам придется включить left.item вправо. И затем, поскольку вы назовете (left.left union left.right), нужно включить left.left.item влево. Right .. Каждый раз, когда вы делаете A.union (B), вы удаляете элемент A путем копирования это полностью (а не умная копия, такая как неизменяемый набор, возвращаемый методом inc), а затем добавьте ее в B. Но так как удаление элемента A требует вызова A.left.union (A.right), вы сначала должны скопировать A.left/A.right ... И так далее. Если вы можете визуализировать дерево, это похоже на сбор каждого левого брата на его правого брата и каждый раз, когда вы хотите добавить только один предмет к другому.

НЕКОТОРЫЕ ПРИМЕЧАНИЯ:

Если вы можете сказать, что empty.union (то) = что вы можете сказать, что NonEmpty.union (что: TweetSet) = если пусто, то это то (((объединение ...) ..) other incl elem). В этом проблема с методами и таким пустым/неэлементным шаблоном, вы не можете централизовать эти два базовых случая в одном методе, и здесь многие из нас реализуют первый в пустом, но забывают другое в NonEmpty. ВСЕГДА убедитесь, что если A.f (b) является симметричным (= b.f (A)), у вас есть оба базовых варианта. Идентифицируйте и идите прямо к основному футляру. Затем сделайте рекурсию из этого в ваше глобальное решение. для «left union right union other incl elem», базовый корпус - другой, включая elem, так как вы не хотите, чтобы ваша замена была в конце «Empty incl n1 incl n2 incl ...». Так что сосредоточьтесь на нем, (в частности, на элементе). наконец, но что более важно: Интуиция !!! Используйте действительно простой случай, например, если у вас есть трудности с моим объяснением здесь, представьте тот же аргумент для метода «copy», который вы могли бы написать как (левое копирование справа), включая elem или (левая копия (справа, включая elem)). С простым примером, подобным этому, вы можете использовать замены более легко и быстро, чтобы понять, почему некоторые решения значительно хуже других! Надеюсь, это поможет! Если у вас есть замечания, скажите мне!

Смежные вопросы