2017-05-09 69 views
0

是否有可能(甚至是值得)尝试编写下面的代码块而没有var?它适用于一个变种。这不是面试,这是我第一次尝试scala(来自java)。在纯函数中使用var的工作scala代码。这可能没有var?

问题:尽可能地让人们靠近剧院的前面,同时保持每个请求(例如Jones,4张门票)在一个剧院区域。从前面开始的剧院部分的大小为6,6,3,5,5 ......等等。我试图通过将所有潜在的票务请求组放在一起来完成此任务,然后选择每个部分中最适合的组。

这里是类。一个SeatingCombination是SeatingRequest(只是标识)的一个可能的组合及其ticketCount(S)的总和:

class SeatingCombination(val idList: List[Int], val seatCount: Int){} 
class SeatingRequest(val id: Int, val partyName: String, val ticketCount: Int){} 
class TheatreSection(val sectionSize: Int, rowNumber: Int, sectionNumber: Int) { 
    def id: String = rowNumber.toString + "_"+ sectionNumber.toString; 
} 

通过我们得到以下功能的时间... 1)所有可能的SeatingRequest的组合在SeatingCombination的列表中并按降序排列。 2.)所有TheatreSection按顺序列出。

def getSeatingMap(groups: List[SeatingCombination], sections: List[TheatreSection]): HashMap[Int, TheatreSection] = { 
    var seatedMap = new HashMap[Int, TheatreSection] 
    for (sect <- sections) { 
     val bestFitOpt = groups.find(g => { g.seatCount <= sect.sectionSize && !isAnyListIdInMap(seatedMap, g.idList) }) 
     bestFitOpt.filter(_.idList.size > 0).foreach(_.idList.foreach(seatedMap.update(_, sect))) 
    } 
    seatedMap 
} 

def isAnyListIdInMap(map: HashMap[Int, TheatreSection], list: List[Int]): Boolean = { 
    (for (id <- list) yield !map.get(id).isEmpty).reduce(_ || _) 
    } 

我写了没有var的程序的其余部分,但在这个迭代部分,它似乎是不可能的。也许我的实施策略是不可能的。从我读过的内容看,纯函数中的var仍然有效。但它一直困扰着我,我想不出如何去除var,因为我的教科书告诉我要尽量避免它们,而我不知道我不知道什么。

+2

它看起来像'foldLeft'工作。 – Nebril

回答

5

您可以使用foldLeftsections迭代与正在运行的状态(并再次,里面,你的状态,反复增加的部分中的所有IDS):

sections.foldLeft(Map.empty[Int, TheatreSection]){ 
    case (seatedMap, sect) => 
    val bestFitOpt = groups.find(g => g.seatCount <= sect.sectionSize && !isAnyListIdInMap(seatedMap, g.idList)) 
    bestFitOpt. 
     filter(_.idList.size > 0).toList. //convert option to list 
     flatMap(_.idList). // flatten list from option and idList 
     foldLeft(seatedMap)(_ + (_ -> sect))) // add all ids to the map with sect as value 
} 

顺便说一句,您可以简化使用existsmap.contains第二种方法:

def isAnyListIdInMap(map: HashMap[Int, TheatreSection], list: List[Int]): Boolean = { 
list.exists(id => map.contains(id)) 

}

list.exists(predicate: Int => Boolean)是一个布尔值,如果list中的任何元素的谓词为真,则为true。

map.contains(key)检查map是否定义在key

如果你想更加简洁,你不需要给一个名称谓词的说法:

list.exists(map.contains) 
1

这里是我如何能够实现不使用mutable.HashMap ,由注释使用foldLeft的建议是用来做什么的:

class SeatingCombination(val idList: List[Int], val seatCount: Int){} 
class SeatingRequest(val id: Int, val partyName: String, val ticketCount: Int){} 
class TheatreSection(val sectionSize: Int, rowNumber: Int, sectionNumber: Int) { 
    def id: String = rowNumber.toString + "_"+ sectionNumber.toString; 
} 

def getSeatingMap(groups: List[SeatingCombination], sections: List[TheatreSection]): Map[Int, TheatreSection] = { 
    sections.foldLeft(Map.empty[Int, TheatreSection]) { (m, sect) => 
    val bestFitOpt = groups.find(g => { 
     g.seatCount <= sect.sectionSize && !isAnyListIdInMap(m, g.idList) 
    }).filter(_.idList.nonEmpty) 

    val newEntries = bestFitOpt.map(_.idList.map(_ -> sect)).getOrElse(List.empty) 
    m ++ newEntries 
    } 
} 

def isAnyListIdInMap(map: Map[Int, TheatreSection], list: List[Int]): Boolean = { 
    (for (id <- list) yield map.get(id).isDefined).reduce(_ || _) 
} 
2

简单地改变varval应该这样做:) 我想,你可能会问摆脱可变地图,没有的var(代码中不需要为var)。

这样的事情通常是在scala中递归地编写或使用foldLeft,就像其他答案建议的一样。这里是一个递归版本:

@tailrec 
    def getSeatingMap(
    groups: List[SeatingCombination], 
    sections: List[TheatreSection], 
    result: Map[Int, TheatreSection] = Map.empty): Map[Int, TheatreSection] = sections match { 
    case Nil => result 
    case head :: tail => 
     val seated = groups 
     .iterator 
     .filter(_.idList.nonEmpty) 
     .filterNot(_.idList.find(result.contains).isDefined)  
     .find(_.seatCount <= head.sectionSize) 
     .fold(Nil)(_.idList.map(id => id -> sect)) 
     getSeatingMap(groups, tail, result ++ seated) 
    } 

顺便说一句,我不认为你需要测试每个ID在列表中的地图存在 - 应该足以只是看在第一位。如果不是每次检查地图以查看该组是否已经就座,您只需在分配该分区后立即将其从输入列表中删除即可。

@tailrec 
    def selectGroup(
     sect: TheatreSection, 
     groups: List[SeatingCombination], 
     result: List[SeatingCombination] = Nil 
    ): (List[(Int, TheatreSection)], List[SeatingCombination]) = groups match { 
    case Nil => (Nil, result) 
    case head :: tail 
     if(head.idList.nonEmpty && head.seatCount <= sect.sectionSize) => (head.idList.map(_ -> sect), result.reverse ++ tail) 
    case head :: tail => selectGroup(sect, tail, head :: result) 
    } 

,然后在getSeatingMap

... 
    case head :: tail => 
    val(seated, remaining) => selectGroup(sect, groups) 
    getSeatingMap(remaining, tail, result ++ seated) 
相关问题