尝试在没有var的情况下编写下面的代码块是否可能(甚至是值得的)?它适用于var。这不是面试,这是我第一次尝试scala(来自java)。
问题:让人们尽可能靠近剧院前面,同时将每个请求(例如琼斯,4张票)保存在一个剧院区域。剧院部分从正面开始,大小分别为6,6,3,5,5 ......等等。我试图通过将所有潜在的票证请求组合在一起,然后为每个部分选择最合适的组来实现这一目标。
以下是课程。 SeatingCombination是SeatingRequest(只是ID)和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,因为我的教科书告诉我试图避免它们,我不知道我不知道的。
答案 0 :(得分:5)
您可以使用foldLeft
在sections
上以正在运行的状态进行迭代(再次,在您的状态内部迭代添加部分中的所有ID):
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
}
顺便说一句,您可以使用exists
和map.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 :(得分:2)
只需将var
更改为val
就可以了:)
我想,你可能会问有关摆脱可变地图的问题,而不是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)
答案 2 :(得分: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(_ || _)
}