实现DISTINCT以便理解

时间:2014-06-27 14:24:42

标签: sql scala for-comprehension

his Coursera course的倒数第二次讲座中,奥德斯基教授提出了以下for理解作为可爱案例研究的最后一步:

def solutions(target: Int): Stream[Path] =
  for {
    pathSet <- pathSets
    path <- pathSet
    if path.endState contains target
  } yield path

在之前的一次演讲中,他在for理解和SQL之间进行了一些类比。

我正在寻找的是yieldpath DISTINCT endState pathSets Map的方法。

有没有办法从相同理解的过滤条款中回溯到已经产生的项目?

另一种方法可能是在endState语句之前将pathfor转换为StreamStream,然后将其转换回{{ 1}}在返回之前。但是,这似乎会失去使用Set的懒惰计算优势。

同一个案例研究的早期方法完成了类似的目标,但它已经是一个递归函数,而这个函数似乎不需要递归。

看起来我可以使用可变endState来跟踪得到的{{1}},但这感觉不满意,因为到目前为止该课程已成功避免使用可变性。

1 个答案:

答案 0 :(得分:1)

  

有没有办法从相同理解的过滤条款中回溯到已经产生的项目?

你理解的东西或多或少像是

pathSets flatMap {
  pathSet => pathSet filter {
   path => path.endState contains target
  }
} map {path => path}

具有身份功能的最后一张地图是你的收益。我不记得规范是否允许该地图在身份识别功能时被省略。

无论如何,我希望这更清楚地说明为什么没有“回到”那个结构。

你可以写一个懒惰的,递归的distinctBy函数

implicit class DistinctStream[T](s: Stream[T]) {
  def distinctBy[V](f: T => V): Stream[T] = {
    def distinctBy(remainder: Stream[T], seen:Set[V]): Stream[T] =
      remainder match {
        case head #:: tail => 
          val value = f(head)
          if (seen contains value) distinctBy(tail, seen)
          else Stream.cons(head, distinctBy(tail, seen + value))
        case empty => empty
     }

    distinctBy(s, Set())  
  }
}

并像这样使用

def solutions(target: Int): Stream[Path] =
(for {
 pathSet <- pathSets
 path <- pathSet
 if path.endState contains target
} yield path) distinctBy (_.endState)

是的,现在有递归。但是因为Stream的map,flatMap和filter函数都已经是懒惰的递归函数了。