1

我正在尝试以功能样式重写函数。我知道功能不好。但是如何避免使用 var 字段watermarkTime

该函数为具有连续增加时间戳的行设置第“1”天。如果时间戳小于上一行的时间戳,则设置天“2”。

case class Row(time: String, name: String, var day: Int)

val rows = List(new Row("09:18:52", "3_0711-082757_01001", 0),
                new Row("09:23:18", "3_0711-082757_01002", 0),
                new Row("09:33:43", "3_0711-082757_01004", 0),
                new Row("10:20:00", "3_0711-082757_01011", 0),
                new Row("05:03:38", "3_0711-082757_02001", 0),  // set secound day
                new Row("05:07:51", "3_0711-082757_02002", 0),
                new Row("05:13:02", "3_0711-082757_02003", 0),
                new Row("05:19:16", "3_0711-082757_02004", 0),
                new Row("10:54:27", "3_0711-082757_02015", 0),  // set first day 
                new Row("11:00:38", "3_0711-082757_02016", 0),
                new Row("11:07:28", "3_0711-082757_02017", 0))


def setDayFP(rows: List[Row]): List[Row] = {

  var watermarkTime = 0

  for (row <- rows) {

    val newTime = row.time.replaceAll(":","").toInt 

    if (watermarkTime < newTime) {
      watermarkTime = newTime
      row.day = 1
    }  else  {
      row.day = 2
    }
  }
  return rows 
}

这是结果(按日期和名称排序):

Row(09:18:52,3_0711-082757_01001,1)
Row(09:23:18,3_0711-082757_01002,1)
Row(09:33:43,3_0711-082757_01004,1)
Row(10:20:00,3_0711-082757_01011,1)
Row(10:54:27,3_0711-082757_02015,1)
Row(11:00:38,3_0711-082757_02016,1)
Row(11:07:28,3_0711-082757_02017,1)
Row(05:03:38,3_0711-082757_02001,2)
Row(05:07:51,3_0711-082757_02002,2)
Row(05:13:02,3_0711-082757_02003,2)
Row(05:19:16,3_0711-082757_02004,2)

我正在寻找更好的解决方案。提前致谢!

庞戈

4

5 回答 5

3

下面的解决方案

  • 有一个不可变的列表类并使用复制而不是字段更新

  • 使用foldLeft而不是循环

  • 使用元组作为折叠的聚合器来携带watermarkTime

但是,我个人会坚持使用你的 for 循环和var watermarkTime,因为foldLeft它不会使其更具可读性。不过,我会保留day: Option[Int]和复制。

case class Row(time: String, name: String, day: Option[Int] = None)

val rows = List(new Row("09:18:52", "3_0711-082757_01001"),
                new Row("09:23:18", "3_0711-082757_01002"),
                new Row("09:33:43", "3_0711-082757_01004"),
                new Row("10:20:00", "3_0711-082757_01011"),
                new Row("05:03:38", "3_0711-082757_02001"),
                new Row("05:07:51", "3_0711-082757_02002"),
                new Row("05:13:02", "3_0711-082757_02003"),
                new Row("05:19:16", "3_0711-082757_02004"),
                new Row("10:54:27", "3_0711-082757_02015"),
                new Row("11:00:38", "3_0711-082757_02016"),
                new Row("11:07:28", "3_0711-082757_02017"))


def setDayFP(rows: List[Row]): List[Row] = {
  rows.foldLeft((0, List[Row]()))
               {case ((watermarkTime, resultRows), row) =>

    val newTime = row.time.replaceAll(":","").toInt

    val (newWatermarkTime, day) = 
      if (watermarkTime < newTime)
        (newTime, 1)
      else
        (watermarkTime, 2)

    (newWatermarkTime, row.copy(day = Some(day)) :: resultRows)
  }._2.reverse
}

setDayFP(rows).foreach(println)
于 2012-07-19T14:53:02.607 回答
2

这是我的解决方案:

case class Row(time: String, name: String, day: Int)

def setDay(rs: List[Row], wt: String = ""): List[Row] = rs match {
  case x::xs => if(wt < x.time) x.copy(day=1) :: setDay(xs,x.time)
                else x.copy(day=2) :: setDay(xs,wt)
  case Nil => Nil
}

将其用作setDay(rows).

于 2012-07-19T15:57:07.587 回答
2

只是为了好玩:

case class Row(time: String, name: String, day: Int) {
  def timeAsInt = time.replaceAll(":", "").toInt
}

val rows = List(new Row("09:18:52", "3_0711-082757_01001", 0),
                new Row("09:23:18", "3_0711-082757_01002", 0),
                new Row("09:33:43", "3_0711-082757_01004", 0),
                new Row("10:20:00", "3_0711-082757_01011", 0),
                new Row("05:03:38", "3_0711-082757_02001", 0),  // set secound day
                new Row("05:07:51", "3_0711-082757_02002", 0),
                new Row("05:13:02", "3_0711-082757_02003", 0),
                new Row("05:19:16", "3_0711-082757_02004", 0),
                new Row("10:54:27", "3_0711-082757_02015", 0),  // set first day 
                new Row("11:00:38", "3_0711-082757_02016", 0),
                new Row("11:07:28", "3_0711-082757_02017", 0))


def setDayFP(rows: List[Row]): List[Row] = {
  lazy val rowStream:Stream[Row] = for((row, watermarkTime) <- rows.toStream.zip(watermarkStream)) yield
    row.copy(day = if(watermarkTime < row.timeAsInt) 1 else 2) 


  lazy val watermarkStream:Stream[Int] = 0 #:: rowStream.zip(watermarkStream).map{ case (row, watermarkTime) =>
    math.max(watermarkTime, row.timeAsInt)
  }

  rowStream.toList
}

setDayFP(rows)
于 2012-07-19T16:03:59.317 回答
1

我试了一下,我认为这foldLeft不是去这里的路。这是我的解决方案:

def sdfp(rows: List[Row]): List[Row] = {
  var watermark = 0
  rows.map(row => {
    val time = row.time.replaceAll(":","").toInt
    if(watermark < time) {
      watermark = time
      row.day = 1
    } else {
      row.day = 2
    }
    row
  }
}

map接受一个函数并将其应用于集合的所有元素。

编辑

我找到了解决这个问题的另一种方法:

def sdfp2(rows: List[Row]): List[Row] = {
  var watermark = 0
  for {
    row <- rows
    val time = row.time.replaceAll(":","").toInt
    val after = watermark < time
  } yield {
    if(after) { watermark = row; row.day = 1 } else row.day = 2
    row
  }
}
于 2012-07-19T15:18:33.077 回答
0

根据您想要实现的功能,您至少有两种方法:

第一个解决方案是在一组可变数据结构上使用 foldLeft,这不是最实用的方法。这是您尝试以命令式样式执行的操作,我不明白为什么您需要在 setDayFP 末尾返回列表,因为您没有更改它。

  def foldLeftExample(rows:List[Row]) = {
    rows.foldLeft(0){
      case(previousWaterMarkTime,currentRow) => 
        val newTime = currentRow.time.replaceAll(":","").toInt
        if(previousWaterMarkTime<newTime){
          currentRow.day=1
          newTime
        }
        else{
          currentRow.day=2
          previousWaterMarkTime
        }
    }
  }

折叠时,您将类型 A 的初始值传递给集合和一个接受 Tuple[A,YourCollectionType] 并返回 A 的函数。如果重构它,您会发现它看起来会更好:

/**
   * Updates the row and returns the new waterMark time
   *
   */
  def updateRow(row:Row,previousWaterMarkTime:Int):Int = {
    val newTime = currentRow.time.replaceAll(":","").toInt
    if(previousWaterMarkTime<newTime){
      currentRow.day=1
      newTime
    }
    else{
      currentRow.day=2
      previousWaterMarkTime
    }
  }

  def foldLeftExample(rows:List[Row]) = {
    rows.foldLeft(0){
      case(previousWaterMarkTime,currentRow) => updateRow(currentRow,previousWaterMarkTime)
    }
  }

然而,正如我所说,这不是最好的方法,因为您正在改变 currentRow。

对于 FP 的新手来说,最好的方法更难理解……哎呀!有人在我面前发过。将我的答案作为您的答案与 mhs 的确切版本之间的中间版本

于 2012-07-19T14:54:40.643 回答