Scala:将过程代码转换为功能代码
Scala: Convert procedural code to functional
我编写了与下面给出的代码类似的代码。它按照(有点令人困惑的)要求工作,但有些东西告诉我它可以在 Scala 中使用 'pattern matching' 或类似的东西以不同的方式编写,以使其看起来更 功能 。有什么建议吗?
def xyzMethod(map: util.HashMap[Any, List[SomeObject]]) : Option[xyzObject] = {
var myObject: Option[xyzObject] = None
val cCount: Int = map.get("c").size
if (cCount != 10) {
val bCount: Int = map.get("b").size
if (bCount + cCount == 20) {
myObject = buildXyz("b")
} else {
val aCount: Int = map.get("a").size
if (aCount != 0) {
val dCount: Int = map.get("d").size
if (dCount > 10) {
myObject = buildXyz("a")
}
}
}
}
myObject
}
根据要求,这里有一些测试用例:
Test Case 1:
map =>
"c" => Objectc1, Objectc2... Objectc10
This should return None
-----------
Test Case 2:
map =>
"c" => Objectc1, Objectc2
"b" => Objectb1, Objectb2..... Objectb18
This should return buildXyz("b")
-----------
Test Case 3:
map =>
"c" => Objectc1
"b" => Objectb1, Objectb2
This should return None
-----------
Test Case 4:
map =>
"c" => Objectc1
"b" => Objectb1, Objectb2
"a" => Objecta1
"d" => Objectd1
This should return None
-----------
Test Case 5:
map =>
"c" => Objectc1
"b" => Objectb1, Objectb2
"a" => Objecta1
"d" => Objectd1, Objectd2......Objectd10, Objectd11
This should return buildXyz("a")
首先var
可以换成val
def xyzMethod1(map: java.util.HashMap[Any, List[SomeObject]]): Option[xyzObject] = {
val cCount: Int = map.get("c").size
val myObject: Option[xyzObject] = if (cCount != 10) {
val bCount: Int = map.get("b").size
if (bCount + cCount == 20) {
buildXyz("b")
} else {
val aCount: Int = map.get("a").size
if (aCount != 0) {
val dCount: Int = map.get("d").size
if (dCount > 10) {
buildXyz("a")
} else {
None
}
} else {
None
}
}
} else {
None
}
myObject
}
总的来说,这个巨大的 if-else 看起来有点复杂。我们可以看到只有三个可能的结果 buildXyz("a")
、buildXyz("b")
和 None
因此,只有三个 if 分支
看起来会好得多
def xyzMethod1(map: java.util.HashMap[Any, List[SomeObject]]): Option[xyzObject] = {
val aCount: Int = map.get("a").size
val bCount: Int = map.get("b").size
val cCount: Int = map.get("c").size
val dCount: Int = map.get("d").size
val myObject: Option[xyzObject] = if (cCount != 10 && bCount + cCount == 20) {
buildXyz("b")
} else if (cCount != 10 && aCount != 0 && dCount > 10) {
buildXyz("a")
} else {
None
}
myObject
}
使用几个辅助方法看起来会更好:
def isBuildA(map: java.util.HashMap[Any, List[SomeObject]]): Boolean = {
val aCount: Int = map.get("a").size
val cCount: Int = map.get("c").size
val dCount: Int = map.get("d").size
cCount != 10 && aCount != 0 && dCount > 10
}
def isBuildB(map: java.util.HashMap[Any, List[SomeObject]]): Boolean = {
val bCount: Int = map.get("b").size
val cCount: Int = map.get("c").size
cCount != 10 && bCount + cCount == 20
}
def xyzMethod1(map: java.util.HashMap[Any, List[SomeObject]]): Option[xyzObject] = {
if (isBuildA(map)) {
buildXyz("a")
} else if (isBuildB(map)) {
buildXyz("b")
} else {
None
}
}
然后java地图可以转换为scala地图。因为我们只关心计数,所以我们可以立即将列表映射到大小。然后还更改助手以使其更具功能性。
def isBuildA(map: scala.collection.Map[Any, Int]): Boolean = {
map.exists(v => v._1 == "c" && v._2 != 10) &&
map.exists(v => v._1 == "a" && v._2 != 0) &&
map.exists(v => v._1 == "d" && v._2 > 10)
}
def isBuildB(map: scala.collection.Map[Any, Int]): Boolean = {
map.exists(v => v._1 == "c" && v._2 != 10) &&
map.filterKeys(k => k == "b" || k == "c").values.sum == 20
}
def xyzMethod1(map: java.util.HashMap[Any, List[SomeObject]]): Option[xyzObject] = {
import scala.collection.JavaConverters._
val map1 = map.asScala.mapValues(_.size)
if (isBuildA(map1)) {
buildXyz("a")
} else if (isBuildB(map1)) {
buildXyz("b")
} else {
None
}
}
您发布的代码没有通过所有测试,但是这个通过了。
import scala.collection.immutable.HashMap
//just enough stubs to make everything compile
case class XyzObject(x:String)
class SomeObject
def buildXyz(xyz:String) = Option(XyzObject(xyz))
def xyzMethod(map: HashMap[Any, List[SomeObject]]) :Option[XyzObject] = {
val cCount: Int = map.getOrElse("c", Nil).size
if (cCount == 10) None
else if (map.getOrElse("b",Nil).size + cCount == 20) buildXyz("b")
else if (map.getOrElse("a",Nil).isEmpty ||
map.getOrElse("d",Nil).size < 11) None
else buildXyz("a")
}
测试套件:
// Test Case 1:
xyzMethod(HashMap("c" -> List.fill(10)(new SomeObject)))
//This should return None
//Test Case 2:
xyzMethod(HashMap("c" -> List.fill(2)(new SomeObject)
,"b" -> List.fill(18)(new SomeObject)))
//This should return Some(XyzObject("b"))
//Test Case 3:
xyzMethod(HashMap("c" -> List.fill(1)(new SomeObject)
,"b" -> List.fill(2)(new SomeObject)))
//This should return None
//Test Case 4:
xyzMethod(HashMap("c" -> List.fill(1)(new SomeObject)
,"b" -> List.fill(2)(new SomeObject)
,"a" -> List.fill(1)(new SomeObject)
,"d" -> List.fill(1)(new SomeObject)))
//This should return None
//Test Case 5:
xyzMethod(HashMap("c" -> List.fill(1)(new SomeObject)
,"b" -> List.fill(2)(new SomeObject)
,"a" -> List.fill(1)(new SomeObject)
,"d" -> List.fill(11)(new SomeObject)))
//This should return Some(XyzObject("a"))
我编写了与下面给出的代码类似的代码。它按照(有点令人困惑的)要求工作,但有些东西告诉我它可以在 Scala 中使用 'pattern matching' 或类似的东西以不同的方式编写,以使其看起来更 功能 。有什么建议吗?
def xyzMethod(map: util.HashMap[Any, List[SomeObject]]) : Option[xyzObject] = {
var myObject: Option[xyzObject] = None
val cCount: Int = map.get("c").size
if (cCount != 10) {
val bCount: Int = map.get("b").size
if (bCount + cCount == 20) {
myObject = buildXyz("b")
} else {
val aCount: Int = map.get("a").size
if (aCount != 0) {
val dCount: Int = map.get("d").size
if (dCount > 10) {
myObject = buildXyz("a")
}
}
}
}
myObject
}
根据要求,这里有一些测试用例:
Test Case 1:
map =>
"c" => Objectc1, Objectc2... Objectc10
This should return None
-----------
Test Case 2:
map =>
"c" => Objectc1, Objectc2
"b" => Objectb1, Objectb2..... Objectb18
This should return buildXyz("b")
-----------
Test Case 3:
map =>
"c" => Objectc1
"b" => Objectb1, Objectb2
This should return None
-----------
Test Case 4:
map =>
"c" => Objectc1
"b" => Objectb1, Objectb2
"a" => Objecta1
"d" => Objectd1
This should return None
-----------
Test Case 5:
map =>
"c" => Objectc1
"b" => Objectb1, Objectb2
"a" => Objecta1
"d" => Objectd1, Objectd2......Objectd10, Objectd11
This should return buildXyz("a")
首先var
可以换成val
def xyzMethod1(map: java.util.HashMap[Any, List[SomeObject]]): Option[xyzObject] = {
val cCount: Int = map.get("c").size
val myObject: Option[xyzObject] = if (cCount != 10) {
val bCount: Int = map.get("b").size
if (bCount + cCount == 20) {
buildXyz("b")
} else {
val aCount: Int = map.get("a").size
if (aCount != 0) {
val dCount: Int = map.get("d").size
if (dCount > 10) {
buildXyz("a")
} else {
None
}
} else {
None
}
}
} else {
None
}
myObject
}
总的来说,这个巨大的 if-else 看起来有点复杂。我们可以看到只有三个可能的结果 buildXyz("a")
、buildXyz("b")
和 None
因此,只有三个 if 分支
def xyzMethod1(map: java.util.HashMap[Any, List[SomeObject]]): Option[xyzObject] = {
val aCount: Int = map.get("a").size
val bCount: Int = map.get("b").size
val cCount: Int = map.get("c").size
val dCount: Int = map.get("d").size
val myObject: Option[xyzObject] = if (cCount != 10 && bCount + cCount == 20) {
buildXyz("b")
} else if (cCount != 10 && aCount != 0 && dCount > 10) {
buildXyz("a")
} else {
None
}
myObject
}
使用几个辅助方法看起来会更好:
def isBuildA(map: java.util.HashMap[Any, List[SomeObject]]): Boolean = {
val aCount: Int = map.get("a").size
val cCount: Int = map.get("c").size
val dCount: Int = map.get("d").size
cCount != 10 && aCount != 0 && dCount > 10
}
def isBuildB(map: java.util.HashMap[Any, List[SomeObject]]): Boolean = {
val bCount: Int = map.get("b").size
val cCount: Int = map.get("c").size
cCount != 10 && bCount + cCount == 20
}
def xyzMethod1(map: java.util.HashMap[Any, List[SomeObject]]): Option[xyzObject] = {
if (isBuildA(map)) {
buildXyz("a")
} else if (isBuildB(map)) {
buildXyz("b")
} else {
None
}
}
然后java地图可以转换为scala地图。因为我们只关心计数,所以我们可以立即将列表映射到大小。然后还更改助手以使其更具功能性。
def isBuildA(map: scala.collection.Map[Any, Int]): Boolean = {
map.exists(v => v._1 == "c" && v._2 != 10) &&
map.exists(v => v._1 == "a" && v._2 != 0) &&
map.exists(v => v._1 == "d" && v._2 > 10)
}
def isBuildB(map: scala.collection.Map[Any, Int]): Boolean = {
map.exists(v => v._1 == "c" && v._2 != 10) &&
map.filterKeys(k => k == "b" || k == "c").values.sum == 20
}
def xyzMethod1(map: java.util.HashMap[Any, List[SomeObject]]): Option[xyzObject] = {
import scala.collection.JavaConverters._
val map1 = map.asScala.mapValues(_.size)
if (isBuildA(map1)) {
buildXyz("a")
} else if (isBuildB(map1)) {
buildXyz("b")
} else {
None
}
}
您发布的代码没有通过所有测试,但是这个通过了。
import scala.collection.immutable.HashMap
//just enough stubs to make everything compile
case class XyzObject(x:String)
class SomeObject
def buildXyz(xyz:String) = Option(XyzObject(xyz))
def xyzMethod(map: HashMap[Any, List[SomeObject]]) :Option[XyzObject] = {
val cCount: Int = map.getOrElse("c", Nil).size
if (cCount == 10) None
else if (map.getOrElse("b",Nil).size + cCount == 20) buildXyz("b")
else if (map.getOrElse("a",Nil).isEmpty ||
map.getOrElse("d",Nil).size < 11) None
else buildXyz("a")
}
测试套件:
// Test Case 1:
xyzMethod(HashMap("c" -> List.fill(10)(new SomeObject)))
//This should return None
//Test Case 2:
xyzMethod(HashMap("c" -> List.fill(2)(new SomeObject)
,"b" -> List.fill(18)(new SomeObject)))
//This should return Some(XyzObject("b"))
//Test Case 3:
xyzMethod(HashMap("c" -> List.fill(1)(new SomeObject)
,"b" -> List.fill(2)(new SomeObject)))
//This should return None
//Test Case 4:
xyzMethod(HashMap("c" -> List.fill(1)(new SomeObject)
,"b" -> List.fill(2)(new SomeObject)
,"a" -> List.fill(1)(new SomeObject)
,"d" -> List.fill(1)(new SomeObject)))
//This should return None
//Test Case 5:
xyzMethod(HashMap("c" -> List.fill(1)(new SomeObject)
,"b" -> List.fill(2)(new SomeObject)
,"a" -> List.fill(1)(new SomeObject)
,"d" -> List.fill(11)(new SomeObject)))
//This should return Some(XyzObject("a"))