Scala:如何以功能方式更改此代码?
Scala: how to change this code in functional way?
例如我有这个列表:
val list = List(
Map("a" -> "John", "b" -> 20, "c" -> true),
Map("a" -> "Derp", "b" -> 10, "c" -> false),
Map("b" -> 8, "c" -> true),
Map("a" -> "asdf", "b" -> 50, "c" -> true)
)
我有处理上述列表的代码,我想以最有效的方式将其更改为功能样式:
var result = ""
breakable {
for (m <- list) {
if (m.get("a").isEmpty) {
result = "Error A"
break()
}
if (m.get("b").isEmpty) {
result = "Error B"
break()
}
}
}
在上面的代码中,如果 "a" 不存在,则 if 检查仅在退出循环之前进行一次。如果 "b" 不存在,则检查两次。
我尝试将其更改为功能代码:
val result = (for {
m <- list
a = m.get("a").isEmpty
b = m.get("b").isEmpty
if a || b
} yield {
if (a)
"Error A"
else
"Error B"
}).headOption.getOrElse("")
不好看好像"a"不存在,if检查会做两次,如果"b"不存在,会做三次。
还有.headOption.getOrElse("")
开销。如果能直接得到字符串结果就好了
谁能提供更好的解决方案?
list.view
.map(m => if (m.get("a").isEmpty) "Error A"
else if (m.get("b").isEmpty) "Error B"
else "")
.find(_.nonEmpty)
.getOrElse("")
注意视图使其成为非严格的,以便它在第一个失败元素之后停止。
将 if 语句作为部分函数稍微更整洁,如果 "a" 或 "b" 都不在映射中则未定义,并且使用 contains
list.collectFirst{case m if !(m contains "a") => "Error A"
case m if !(m contains "b") => "Error B"}
.getOrElse("")
有索引
list.view
.zipWithIndex
.map{case (m,i) => if (m.get("a").isEmpty) "Error A:"+ i
else if (m.get("b").isEmpty) "Error B:" + i
else ""}
.find(_.nonEmpty)
.getOrElse("")
//> res1: String = Error A:2
并使用 collectFirst
list.zipWithIndex
.collectFirst{case (m, i) if !(m contains "a") => "Error A:" + i
case (m, i) if !(m contains "b") => "Error B:" + i}
.getOrElse("")
仅作为附录,另一个可行的解决方案是使用递归:
def getError(list: List[Map[String, Any]]): String = {
if (list.nonEmpty) {
val m = list.head
if (m.get("a").isEmpty)
"Error A"
else if (m.get("b").isEmpty)
"Error B"
else
getError(list.tail)
} else ""
}
例如我有这个列表:
val list = List(
Map("a" -> "John", "b" -> 20, "c" -> true),
Map("a" -> "Derp", "b" -> 10, "c" -> false),
Map("b" -> 8, "c" -> true),
Map("a" -> "asdf", "b" -> 50, "c" -> true)
)
我有处理上述列表的代码,我想以最有效的方式将其更改为功能样式:
var result = ""
breakable {
for (m <- list) {
if (m.get("a").isEmpty) {
result = "Error A"
break()
}
if (m.get("b").isEmpty) {
result = "Error B"
break()
}
}
}
在上面的代码中,如果 "a" 不存在,则 if 检查仅在退出循环之前进行一次。如果 "b" 不存在,则检查两次。
我尝试将其更改为功能代码:
val result = (for {
m <- list
a = m.get("a").isEmpty
b = m.get("b").isEmpty
if a || b
} yield {
if (a)
"Error A"
else
"Error B"
}).headOption.getOrElse("")
不好看好像"a"不存在,if检查会做两次,如果"b"不存在,会做三次。
还有.headOption.getOrElse("")
开销。如果能直接得到字符串结果就好了
谁能提供更好的解决方案?
list.view
.map(m => if (m.get("a").isEmpty) "Error A"
else if (m.get("b").isEmpty) "Error B"
else "")
.find(_.nonEmpty)
.getOrElse("")
注意视图使其成为非严格的,以便它在第一个失败元素之后停止。
将 if 语句作为部分函数稍微更整洁,如果 "a" 或 "b" 都不在映射中则未定义,并且使用 contains
list.collectFirst{case m if !(m contains "a") => "Error A"
case m if !(m contains "b") => "Error B"}
.getOrElse("")
有索引
list.view
.zipWithIndex
.map{case (m,i) => if (m.get("a").isEmpty) "Error A:"+ i
else if (m.get("b").isEmpty) "Error B:" + i
else ""}
.find(_.nonEmpty)
.getOrElse("")
//> res1: String = Error A:2
并使用 collectFirst
list.zipWithIndex
.collectFirst{case (m, i) if !(m contains "a") => "Error A:" + i
case (m, i) if !(m contains "b") => "Error B:" + i}
.getOrElse("")
仅作为附录,另一个可行的解决方案是使用递归:
def getError(list: List[Map[String, Any]]): String = {
if (list.nonEmpty) {
val m = list.head
if (m.get("a").isEmpty)
"Error A"
else if (m.get("b").isEmpty)
"Error B"
else
getError(list.tail)
} else ""
}