No logic in filtering algorithm

I am trying to solve the third quest from the coursera scala course. I have completed some of them, but I think that I do not have enough sense when it comes to one specific function. I have to implement a filter function that will return a subset of all tweets from a given set of tweets that satisfy a given predicate. I have implemented some functions that I think will help me with this, but the tests do not work

Note Please do not give me the baked code, as this will violate the honor code of coursera. All I want is something that will help me debug my logic and help me see where I messed up and why the tests fail.

abstract class TweetSet { def isEmpty: Boolean /** * 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 union(that: TweetSet): TweetSet = that def isEmpty = true def filter(p: Tweet=> Boolean): TweetSet = new Empty() def filterAcc(p: Tweet => Boolean, acc: TweetSet): TweetSet = new Empty() /** * 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 union(that: TweetSet): TweetSet = (left.union(right)).union(that).incl(elem) val isEmpty = false def filter(p: Tweet => Boolean): TweetSet = filterAcc(p,left.union(right)) def filterAcc(p: Tweet => Boolean, acc: TweetSet): TweetSet = { if(acc.isEmpty) acc else if(p(elem)) {acc.incl(elem); left.filterAcc(p,acc).union(right.filterAcc(p,acc))} else new Empty } /** * 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) } } 

I think the main error in this is filterAcc, since it returns empty when none of the cases apply, but I'm not sure what else I could do. Is this where everything fails? If so, how do I get around it?

UPDATE Also I tried to solve this problem as follows:

  def filterAcc(p: Tweet => Boolean, acc: TweetSet): TweetSet = { if(acc.isEmpty) acc else if(p(elem)) {acc.incl(elem); left.filterAcc(p,acc).union(right.filterAcc(p,acc))} else left.filterAcc(p,acc).union(right.filterAcc(p,acc)) } 

This method now ensures that if none matches the condition, then the recursive call is still being made, but at the same time, the battery does not increase. Tests still fail. What is the drawback of my logic?

thanks

As @lpiepiora and @Ende Neu were desperate to tell me, the beginning of acc should be empty. I ended up using another alliance because my mind just got a stack with this idea, and I just couldn't take it off. Here is the final piece of code:

 def filterAcc(p: Tweet => Boolean, acc: TweetSet): TweetSet = { if(left.isEmpty && right.isEmpty) acc else if(p(elem)){ left.filterAcc(p,acc.incl(elem)).union(right.filterAcc(p,acc.incl(elem)))} else left.filterAcc(p,acc).union(right.filterAcc(p,acc)) } 
+7
algorithm scala recursion
source share
2 answers

It was very difficult for me.

One of the problems with your approach is that you are not using the battery correctly, in fact you are passing a union of units, the battery should only store results that match the given predicate p , since you can use the battery in a for or while , it must be initialized with the start value (for example, for Int 0 , for String an empty string, etc.).

As an example, suppose you have List[Int] and you want to count the number of positive numbers:

 val list = List(0, -1, -2, 4, 9, -7) def countPositive(list: List[Int]): Int = { def loop(list: List[Int], acc: Int): Int = list match { case Nil => acc case head :: tail => { if( head > 0) loop(tail, acc + 1) else loop(tail, acc) } } loop(list, 0) } 

The initial value for this battery is 0 , for example, the same approach should be used for the battery in the filterAcc function.

What you are trying to do is combine the sets and then use them as a standard collection in which you can iterate, I think the problem is to handle a fully functional data structure like this, there there is no collection of sets, but a bunch of objects that are somehow connected. If you remember the video in lecture 3.1 around 7.20, then where Odersky shows that the contains and include operations do not change the three structures, but create a new one, I understand that this is the spirit of the problem.

Returning to the code, you can think of it as a collection that you can repeat to, my approach was to use custom tail and head functions, if tail exists, you can continue iterating to the next object and adding head elements that satisfy the predicate for battery. Each time you repeat, you create a new three structures, avoiding the part that you have already checked, you know if NonEmpty like a left or tight triple using the isEmpty method.

Please note that these methods ( tail and head ) can (in my implementation) be recursive, the very difficult part is that tail always returns new threes objects (as shown in the video when Odersky defines a purely functional structure).

Another suggestion I can give is to try using a bottom-up strategy that always goes back to the last item on the left (or right) with left.isEmpty (or right.isEmpty ) to check where the three ends are, check if add an item to the drive using head , and then create a new three without the item that has just been tested using tail .

It was very difficult for me, and I don’t know if I explained myself correctly, or if this is enough for you to start, unfortunately, for someone like me with very little experience working with purely functional data structures, it is very difficult to explain this without showing the code, good luck.

+5
source share

Your "empty" class returns an invalid value for the filterAcc function! (Because of this, there is too much unnecessary code, and I am stuck in the same problem)

If you think about it - the tweetSet binary tree always ends up with empty classes - then what should filterAcc on empty return?

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

Hint, tooltip β†’ note that the battery is also passed to filterAcc on an empty class

+2
source share

All Articles