我正在尝试以功能样式重写函数。我知道功能不好。但是如何避免使用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)
我正在寻找更好的解决方案。提前谢谢!
猩猩
答案 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的确切版本