在Scala中,函数作为“一等公民”出现。可以通过使用函数可组合的特性,使得代码编写保持良好的DRY原则。以下示例:
1. Song类型,为歌曲名、歌手、热度等信息的封装。
case class Song(id: Long, name: String, singers: List[String], hot: Int) { }
2. SongDemo1 ,存在代码重复
object SongDemo1 { type songFilter = Song => Boolean def findSingerSongs: Set[String] => songFilter = singerNames => song => song.singers.exists(singerNames) def removeSingerSongs: Set[String] => songFilter = singerNames => song => !song.singers.exists(singerNames) def fingTopHotSongs: Int => songFilter = hot => song => song.hot >= hot def findLowHotSongs: Int => songFilter = hot => song => song.hot < hot def main(args: Array[String]) { //过滤器,过滤掉歌手名为刘德华的歌曲. val exculdeSingerFilter = removeSingerSongs(Set("刘德华")) } }
在SongDemo1 中,定义了根据歌手名、热度过滤歌曲的几类过滤器,分别返回包含(不包含)指定歌手、热度大于(小于)给定值的歌曲列表。
可以发现,其中定义过滤器的代码有重复:(findSingerSongs、removeSingerSongs),(fingTopHotSongs、findLowHotSongs),不符合DRY原则。可以根据函数的组合特征进行改进:
3. SongDemo2
object SongDemo2 { type SongFilter = Song => Boolean /** * 给定一个谓词函数predicate,返回和predicate对立的结果. * * @param predicate * @tparam T * @return */ def reverse[T](predicate: T => Boolean) = (a: T) => !predicate(a) def findSingerSongs: Set[String] => SongFilter = singerNames => song => song.singers.exists(singerNames) def removeSingerSongs = findSingerSongs.andThen(reverse(_)) type HotChecker = Int => Boolean def hotFilter: HotChecker => SongFilter = f => song => f(song.hot) def fingTopHotSongs: Int => SongFilter = hot => hotFilter(_ >= hot) def findLowHotSongs: Int => SongFilter = hot => hotFilter(_ < hot) def main(args: Array[String]) { //过滤器,过滤掉歌手名为刘德华的歌曲. val exculdeSingerFilter = removeSingerSongs(Set("刘德华")) } }
在SongDemo2 中,通过引入reverse函数(给定一个谓词函数predicate,返回和predicate对立的结果),
然后使用函数的可组合特性,重写removeSingerSongs 。
通过引入hotFilter函数(接收一个谓词函数,检查歌曲热度是否Ok,返回SongFilter类型),
进而使用hotFilter来表示fingTopHotSongs、findLowHotSongs函数。