使用纯函数中的var来处理scala代码。这可能没有变量吗?

时间:2017-05-09 11:28:02

标签: scala functional-programming

尝试在没有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,因为我的教科书告诉我试图避免它们,我不知道我不知道的。

3 个答案:

答案 0 :(得分:5)

您可以使用foldLeftsections上以正在运行的状态进行迭代(再次,在您的状态内部迭代添加部分中的所有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
}

顺便说一句,您可以使用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 :(得分: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(_ || _)
}