在Scala中,是否可以通过在ListMap上迭代来将部分函数集合构建为一个函数



昨天,@Krzysztof Atłasik帮助我找到了如何通过使用偏函数来减少匹配中的冗余,那么过去的样子是:

i match {
case x if x == 0 ⇒
romanNumeral
case x if x >= 1000 ⇒
this.roman(i - 1000, s"${romanNumeral}M")
case x if x >= 900 ⇒
this.roman(i - 900, s"${romanNumeral}CM")
// etc.

现在看起来像:

object RomanNumerals {
def roman(i: Int)(implicit romanNumeral: String = ""): String =
this.tryRoman(romanNumeral)
.orElse(this.tryRoman(1000, "M", romanNumeral))
.orElse(this.tryRoman(900, "CM", romanNumeral))
.orElse(this.tryRoman(500, "D", romanNumeral))
.orElse(this.tryRoman(400, "CD", romanNumeral))
.orElse(this.tryRoman(100, "C", romanNumeral))
.orElse(this.tryRoman(90, "XC", romanNumeral))
.orElse(this.tryRoman(50, "L", romanNumeral))
.orElse(this.tryRoman(40, "XL", romanNumeral))
.orElse(this.tryRoman(10, "X", romanNumeral))
.orElse(this.tryRoman(9, "IX", romanNumeral))
.orElse(this.tryRoman(5, "V", romanNumeral))
.orElse(this.tryRoman(4, "IV", romanNumeral))
.orElse(this.tryRoman(1, "I", romanNumeral))
.apply(i)
private def tryRoman(romanNumeral: String = ""): PartialFunction[Int, String] = {
case value if value == 0 => romanNumeral
}
private def tryRoman(
upperGuard: Int,
token: String,
romanNumeral: String
): PartialFunction[Int, String] = {
case value if value >= upperGuard =>
this.roman(value - upperGuard)(s"$romanNumeral$token")
}
}

好吧,它更简洁,也更干燥,但我想我想更进一步。

我把我所有的值都放在了一个ListMap中,比如:

val romanNumeralByValue: ListMap[Int, String] = ListMap(
1000 → "M",
900 → "CM",
500 → "D",
400 → "CD",
100 → "C",
90 → "XC",
50 → "L",
40 → "XL",
10 → "X",
9 → "IX",
5 → "V",
4 → "IV",
1 → "I"
)

现在,我正试图弄清楚如何将这个映射转换成一系列的奇偶函数。

我以为会是这样的:

def roman(i: Int)(implicit romanNumeral: String = ""): String = {
romanNumeralByValue.reduce(tryRoman){
case (keyvalue, accumulator) ⇒
accumulator
.orElse(this.tryRoman(keyvalue._1, keyvalue._2, romanNumeral))
}.apply(i)
}

但这不会编译。

有什么办法让它发挥作用吗?

谢谢!

以下是如何构建函数的基本思想
(您仍然需要根据您的用例进行调整(

def checkLowerLimit(lowerLimit: Int, result: String): PartialFunction[Int, String] = {
case value if (value >= lowerLimit) => result
}
val limits: ListMap[Int, String] = ListMap(
10 -> "ten",
0 -> "zero"
)
val foo: PartialFunction[Int, String] =
limits.map((checkLowerLimit _).tupled).reduce {
(acc, f) => acc.orElse(f)
}

你可以测试如下:

foo(11)
// res: String = "ten"
foo(10)
// res: String = "ten"
foo(3)
// res: String = "zero"
foo(-1)
// scala.MatchError: -1 (of class java.lang.Integer)

编辑

将该技术应用于问题。

import scala.collection.immutable.ListMap
import scala.collection.mutable.StringBuilder  
object RomanNumerals {
private val romanNumeralByValue: ListMap[Int, String] = ListMap(
1000 → "M",
900 → "CM",
500 → "D",
400 → "CD",
100 → "C",
90 → "XC",
50 → "L",
40 → "XL",
10 → "X",
9 → "IX",
5 → "V",
4 → "IV",
1 → "I"
)
private val tryRomanStep: (Int, String) => PartialFunction[Int, (Int, String)] =
(upperLimit, result) => {
case value if (value >= upperLimit) =>
upperLimit -> result
}
private val tryRoman: PartialFunction[Int, (Int, String)] =
romanNumeralByValue.map(tryRomanStep.tupled).reduce {
(acc, f) => acc.orElse(f)
}
def roman(i: Int): String = {
@annotation.tailrec
def loop(remainingValue: Int, acc: StringBuilder): String =
if (remainingValue == 0)
acc.result()
else {
val (removedValue, newToken) = tryRoman(remainingValue)
loop(remainingValue - removedValue, acc.append(newToken))
}
loop(
remainingValue = i,
acc = new StringBuilder()
)
}
}

相关内容

  • 没有找到相关文章