我想出了以下内容来转换List[Int] => Try[BigDecimal]
:
import scala.util.Try
def f(xs: List[Int]): Try[BigDecimal] =
Try { xs.mkString.toInt }.map ( BigDecimal(_) )
示例:
scala> f(List(1,2,3,4))
res4: scala.util.Try[BigDecimal] = Success(1234)
scala> f(List(1,2,3,55555))
res5: scala.util.Try[BigDecimal] = Success(12355555)
有没有办法在不诉诸字符串转换步骤的情况下编写此函数?
答案 0 :(得分:6)
不是很漂亮,我不相信它更有效率。这是基本大纲。
val pwrs:Stream[BigInt] = 10 #:: pwrs.map(_ * 10)
List(1,2,3,55555).foldLeft(0:BigInt)((p,i) => pwrs.find(_ > i).get * p + i)
这里的错误处理更加充实。
import scala.util.Try
def f(xs: List[Int]): Try[BigDecimal] = Try {
lazy val pwrs: Stream[BigDecimal] = 10 #:: pwrs.map(_ * 10)
xs.foldLeft(0: BigDecimal) {
case (acc, i) if i >= 0 => pwrs.find(_ > i).get * acc + i
case _ => throw new Error("bad")
}
}
<强> 更新 强>
只是为了咯咯笑,我想我会把一些代码插入Rex Kerr的方便的基准测试/分析工具Thyme。
代码
import scala.util.Try
def fString(xs: List[Int]): Try[BigInt] = Try { BigInt(xs.mkString) }
def fStream(xs: List[Int]): Try[BigInt] = Try {
lazy val pwrs: Stream[BigInt] = 10 #:: pwrs.map(_ * 10)
xs.foldLeft(0: BigInt) {
case (acc, i) if i >= 0 => pwrs.find(_ > i).get * acc + i
case _ => throw new Error("bad")
}
}
def fLog10(xs: List[Int]): Try[BigInt] = Try {
xs.foldLeft(0: BigInt) {
case (acc, i) if i >= 0 =>
math.pow(10, math.ceil(math.log10(i))).toInt * acc + i
case _ => throw new Error("bad")
}
}
fString()
略微简化了凯文的原始问题。 fStream()
是我提议的非字符串实现。 fLog10
与Alexey建议的增强功能相同。
您注意我使用BigInt
代替BigDecimal
。我发现两个非字符串方法在结果的第37位左右遇到了一个错误。某种舍入错误或其他问题,但BigInt
没有问题,这就是我使用的内容。
测试设置
// create a List of 40 Ints and check its contents
val lst = List.fill(40)(util.Random.nextInt(20000))
lst.min // 5
lst.max // 19858
lst.mkString.length // 170
val th = ichi.bench.Thyme.warmed(verbose = print)
th.pbenchWarm(th.Warm(fString(lst)), title="fString")
th.pbenchWarm(th.Warm(fStream(lst)), title="fStream")
th.pbenchWarm(th.Warm(fLog10(lst)), title="fLog10")
<强>结果
fString的基准测试(在345.6毫秒内调用20次)时间:4.015 us 95% CI 3.957 us - 4.073 us(n = 19)垃圾:109.9 ns(n = 2次扫描 测量的)
fStream的基准测试(305个调用时间为305.6毫秒)时间:7.118 us 95% CI 7.024 us - 7.213 us(n = 19)垃圾:293.0 ns(n = 3次扫描 测量的)
fLog10基准测试(20次调用382.8 ms)时间:9.205 us 95% CI 9.187 us - 9.222 us(n = 17)垃圾:73.24 ns(n = 2次扫描 测量的)
所以我对非字符串算法的效率是正确的。奇怪的是,使用math._
来避免Stream
创建并不会更好。我没想到。
<强>外卖强>
数字到字符串和字符串到数字的转换效率相当高。
答案 1 :(得分:1)
import scala.util.{Try, Success}
import scala.annotation.tailrec
def findOrder(i: Int): Long = {
@tailrec
def _findOrder(i: Int, order: Long): Long = {
if (i < order) order
else _findOrder(i, order * 10)
}
_findOrder(i, 1)
}
def f(xs: List[Int]): Try[BigDecimal] = Try(
xs.foldLeft(BigDecimal(0))((acc, i) => acc * findOrder(i) + i)
)
答案 2 :(得分:1)
更有效地找到10的正确功效(在{jwvh的答案中将pwrs.find(_ > i).get
替换为nextPowerOf10(i)
):
def nextPowerOf10(x: Int) = {
val n = math.ceil(math.log10(x))
BigDecimal(math.pow(10, n))
}
由于您从Int
开始,因此不存在舍入问题。