簡體   English   中英

List [Int] => Int沒有字符串轉換?

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

我想出了以下內容來轉換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)

有沒有辦法編寫這個函數而不需要求助於字符串轉換步驟?

不是很漂亮,我不相信它更有效率。 這是基本的大綱。

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

UPDATE

只是為了咯咯笑,我想我會把一些代碼插入到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 ms內調用20次)時間:4.015 us 95%CI 3.957 us - 4.073 us(n = 19)垃圾:109.9 ns(n = 2次測量掃描)

fStream的基准測試(在305.6 ms內進行20次調用)時間: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創建似乎並不是更好。 我沒想到。

帶走

數字到字符串和字符串到數字的轉換是相當有效的。

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

要更有效地找到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))
}

因為你從Int開始,所以不應該有舍入問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM