滤波器算法中缺少逻辑

时间:2014-05-20 17:07:25

标签: algorithm scala recursion

我正在尝试从课程scala课程中解决第三项任务。我已经完成了一些但是我认为在某个特定功能方面我忽略了这一点。我必须实现过滤器函数,该函数将返回给定满足给定谓词的推文集中的所有推文的子集。我已经实现了一些功能,我认为这将有助于我,但测试失败

注意请不要给我烘焙代码,因为这会违反课程荣誉代码。我想要的只是帮助我调试逻辑并帮助我查看我搞砸的地方以及测试失败的原因。

  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)
  }
}

我认为关于这一点的主要错误是filterAcc,因为当没有任何情况适用时它返回empty但是我不确定我还能做什么。一切都失败了吗?如果是的话我应该怎么回事呢?

更新 我也试图用这种方式解决这个问题:

    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))


  }

此方法现在确保如果没有匹配条件,则仍然进行递归调用,但同时累加器不会增加。测试仍然失败。我的逻辑在这里有什么缺陷?

感谢

@lpiepiora和@Ende Neu拼命试图告诉我,acc的开头应该是空的。我最终仍然使用了一个联盟,因为我的思绪只是堆积了这个想法而我无法解决它。这是最后一段代码:

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))


  }

2 个答案:

答案 0 :(得分:6)

这对我来说也很棘手。

您的方法中的一个问题是您没有正确使用累加器,事实上您正在传递集合联合,累加器应该只存储与给定谓词p匹配的结果,如您可以在forwhile循环中使用累加器,应将其初始化为起始值(例如Int 0String为空字符串,等等)。

例如,假设你有一个List[Int],并且你想要计算正整数的数量:

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)
}

此累加器的起始值为0,例如,filterAcc函数中的累加器应采用相同的方法。

您尝试做的是拥有集合的联合,然后将它们用作可以迭代的标准集合,我想问题的关键是要处理像这样的全功能数据结构,那是没有集合的集合,而是一堆以某种方式连接的对象。如果你还记得在7.1附近的讲座3.1中的视频那么,Odersky表示containsinclude操作不会修改这三个结构,而是创建一个新结构,我的理解这就是问题的精神。

回到代码,您可以认为将其视为可以递归的集合,我的方法是使用自定义tailhead函数,如果{ {1}}存在,您可以继续迭代到下一个对象,并将满足谓词的tail元素添加到累加器。每次迭代时,都会创建一个新的三个结构,避开已经检查的部分,使用head方法确实知道NonEmpty是左边还是三个。

请注意,此方法(isEmptytail)可以(在我的实现中)是递归的,非常棘手的部分是head总是返回新的三个对象(如图所示)当Odersky将结构定义为纯函数时,在视频上。

我可以给出的另一个建议是尝试使用自下而上的策略,即使用tail(或left.isEmpty)来检查左边(或右边)的最后一个元素三个结束,检查是否必须使用right.isEmpty将元素添加到累加器中,然后使用head构建一个新的三元素,而不检查元素。

这对我来说非常棘手,我不知道我是否正确地解释了自己,或者这是否足以让你开始,不幸的是,像我这样对纯粹功能数据结构的经验很少的人很难解释它没有显示代码,祝你好运。

答案 1 :(得分:2)

你的班级&#34;空&#34;为filterAcc函数返回错误的值! (因此,太多不必要的代码和我遇到了同样的问题)

如果你考虑一下 - tweetSet二叉树总是以空类结束 - 那么filterAcc在空返回时会是什么?

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

提示,提示 - &gt;注意累加器也传递给Empty class

上的filterAcc