简体   繁体   English

List [Int] => Int没有字符串转换?

[英]List[Int] => Int without String Conversion?

I came up with the following to convert a List[Int] => Try[BigDecimal] : 我想出了以下内容来转换List[Int] => Try[BigDecimal]

import scala.util.Try

def f(xs: List[Int]): Try[BigDecimal] =
  Try { xs.mkString.toInt }.map ( BigDecimal(_) )

Example: 例:

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)

Is there a way to write this function without resorting to a String conversion step? 有没有办法编写这个函数而不需要求助于字符串转换步骤?

Not very pretty, and I'm not convinced it's much more efficient. 不是很漂亮,我不相信它更有效率。 Here's the basic outline. 这是基本的大纲。

val pwrs:Stream[BigInt] = 10 #:: pwrs.map(_ * 10)
List(1,2,3,55555).foldLeft(0:BigInt)((p,i) => pwrs.find(_ > i).get * p + i)

Here it is a little more fleshed out with error handling. 这里有一个更加充实的错误处理。

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")
  }
}

UPDATE UPDATE

Just for giggles, I thought I'd plug some code into Rex Kerr's handy benchmarking/profiling tool, Thyme . 只是为了咯咯笑,我想我会把一些代码插入到Rex Kerr的方便的基准测试/分析工具Thyme中

the code 编码

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() is a slight simplification of Kevin's original question. fString()是凯文原始问题的略微简化。 fStream() is my proposed non-string implementation. fStream()是我提出的非字符串实现。 fLog10 is the same but with Alexey's suggested enhancement. fLog10与Alexey建议的增强功能相同。

You'll note that I'm using BigInt instead of BigDecimal . 你会注意到我使用的是BigInt而不是BigDecimal I found that both non-string methods encountered a bug somewhere around the 37th digit of the result. 我发现两个非字符串方法在结果的第37位左右遇到了一个错误。 Some kind of rounding error or something, but there was no problem with BigInt so that's what I used. 某种舍入错误或其他什么,但BigInt没有问题,所以这就是我使用的。

test setup 测试设置

// 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")

results 结果

Benchmark for fString (20 calls in 345.6 ms) Time: 4.015 us 95% CI 3.957 us - 4.073 us (n=19) Garbage: 109.9 ns (n=2 sweeps measured) fString的基准测试(在345.6 ms内调用20次)时间:4.015 us 95%CI 3.957 us - 4.073 us(n = 19)垃圾:109.9 ns(n = 2次测量扫描)

Benchmark for fStream (20 calls in 305.6 ms) Time: 7.118 us 95% CI 7.024 us - 7.213 us (n=19) Garbage: 293.0 ns (n=3 sweeps measured) fStream的基准测试(在305.6 ms内进行20次调用)时间:7.118 us 95%CI 7.024 us - 7.213 us(n = 19)垃圾:293.0 ns(n = 3次测量扫描)

Benchmark for fLog10 (20 calls in 382.8 ms) Time: 9.205 us 95% CI 9.187 us - 9.222 us (n=17) Garbage: 73.24 ns (n=2 sweeps measured) fLog10的基准测试(20次调用382.8 ms)时间:9.205 us 95%CI 9.187 us - 9.222 us(n = 17)垃圾:73.24 ns(测量n = 2次扫描)

So I was right about the efficiency of the non-string algorithm. 所以我对非字符串算法的效率是正确的。 Oddly, using math._ to avoid Stream creation doesn't appear to be better. 奇怪的是,使用math._来避免Stream创建似乎并不是更好。 I didn't expect that. 我没想到。

takeaway 带走

Number-to-string and string-to-number transitions are reasonably efficient. 数字到字符串和字符串到数字的转换是相当有效的。

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)
)

To find the correct power of 10 more efficiently (replace pwrs.find(_ > i).get with nextPowerOf10(i) in @jwvh's answer): 要更有效地找到10的正确功率nextPowerOf10(i)在@jwvh的答案中用nextPowerOf10(i)替换pwrs.find(_ > i).get ):

def nextPowerOf10(x: Int) = {
  val n = math.ceil(math.log10(x))
  BigDecimal(math.pow(10, n))
}

Since you start with an Int , there should be no rounding issues. 因为你从Int开始,所以不应该有舍入问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM