如何在Scala的功能样式中重写此功能?

时间:2012-07-19 14:22:08

标签: scala functional-programming

我正在尝试以功能样式重写函数。我知道功能不好。但是如何避免使用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)

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

猩猩

5 个答案:

答案 0 :(得分:3)

以下解决方案

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

  • 使用foldLeft代替循环

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

但是,我个人会坚持你的for-loop和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)

答案 1 :(得分: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)

答案 2 :(得分: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)

答案 3 :(得分: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
  }
}

答案 4 :(得分: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的初始值传递给集合,并将函数传递给元组[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 ..和oops,最好的方法更难理解!有人把它贴在我面前。把我的答案作为你的中间版本,以及mhs的确切版本