在Scala中将Char或String转换为Unicode值?

32

我正在解决“Scala for the Impatient”书中的一些练习题之一:

编写一个 for 循环来计算字符串中所有字母的 Unicode 码的乘积。例如,"Hello" 中字符的乘积为 9415087488 L。

下一个问题是不使用 for 循环完成相同的任务 - 提示我们应该在 Scaladoc 中检查 StringOps

我在 Scaladoc 的 RichCharStringOps 部分中进行了检查,也许我正在错误地阅读或寻找错误的地方,但我找不到任何可以让我匹配他们输出的东西。到目前为止,我尝试了以下方法:

scala> x.foldLeft(1)(_ * _.toInt)
res0: Int = 825152896

scala> x.foldLeft(1)(_ * _.getNumericValue)
res5: Int = 2518992

scala> x.foldLeft(1)(_ * _.intValue())
res6: Int = 825152896

scala> var x = 1
x: Int = 1

scala> for (c <- "Hello") x *= c.toInt

scala> x
res12: Int = 825152896

如何在for循环和非for循环的情况下进行匹配,使得它们的输出不匹配?

谢谢!


如果您分两步完成,还有另一种方法。提示:最后一种方法只有一个隐式参数。 - Daniel C. Sobral
10个回答

31
当你执行 x.foldLeft(1)(_ * _.toInt) 时,结果类型将被推断为 Int,但 9415087488 太大了,Int 无法存储它。因此,你需要告诉 Scala 使用 Long 来存储它。
scala> val x = "Hello"
x: java.lang.String = Hello

scala> x.foldLeft(1L)(_ * _.toInt)
res1: Long = 9415087488

scala> var x: Long = 1
x: Long = 1

scala> for (c <- "Hello") x *= c.toInt

scala> x
res7: Long = 9415087488

5
谢谢,我也感谢。奇怪的是,在我刚刚下载的PDF版本中,给出了错误的值(825152896)。 - Markus
奇怪的是,使用“for(fi <-”Hello“)i *= fi.toChar”我得到了与书中相同的值。嗯。 - user2850243
@Dubysa,i变量的类型是什么?你需要将i定义为Long才能得到正确的结果,否则Scala会推断表达式为Int(一个Int乘以一个Char将变成Int),这将导致溢出。 - Brian Hsu
好的,我的 REPL 没看出有什么问题(一直知道我的笔记本很聪明:D),我已经将其发布为答案。 - user2850243
你也可以将其写为(x :\ 1L)(_ * _)。 - Nathan Brown

13
如果您将String的每个RichChar转换为.toLong,也可以起到同样的作用。例如,这样做: str.map (_.toLong).product - 可以正常工作,无需使用foldLeft或循环。
这是循环变体:
def product(str: String): Long = {
    var prod: Long = 1
    for (ch <- str) prod *= ch
    prod
}

8

在StringOps中有一个特殊的“product”方法,它可以将集合中的元素相乘。但是由于字符串由char元素组成,所以它使用Char类型。当我们尝试计算“Hello”.product时,会出现溢出的情况。因此,我通过“Hello”.map(_.toLong)将字符串转换为长Unicode值的向量,并使用以下代码计算其元素的乘积:

scala> "Hello".map(_.toLong).product
res79: Long = 9415087488

谢谢!我也怀疑是溢出了。但是不确定如何请求将其映射到Long型。 - asgs
Rihad的答案中的for循环和这个答案中的map只是符号变体。我相信编译器会将for循环转换为map。使用哪种方式只是一种口味问题。 - airfoyle

5
这里有另外一种方法:
scala> (for (c <- "Hello") yield c.toLong).product
res36: Long = 9415087488

3
我发现最直接的方法是:
"Hello".foldLeft(1L)((x:Long, y:Char) => x*y)

该方法需要两个参数:一个Long类型和一个委托函数,该函数接受一个Long类型和一个Char类型的参数,并返回一个Long类型。您可以直接传递匿名函数,如下所示,或者在其他地方定义该函数并将其作为参数传递,如下所示:
def multiply(x:Long, y:Char) = {
    x*y
}
"Hello".foldLeft(1L)(multiply)

2

我认为将集合转换为中间映射是低效的,因为在这种情况下,集合被迭代了两次:一次创建长整数映射,第二次将所有元素相乘。不必要的Long临时集合也是不需要的。我支持

"Hello".foldLeft(1L)(_ * _)

1
这个练习要求不使用for循环,所以我采用了递归:
def unicode_rec(s: String): Int = 
  if(s == "") 1 
  else s.head.toInt * unicode_rec(s.tail)

递归函数也是我解决问题的第一直觉。实际上,你可以省略s.head.toInt中的.toInt部分,因为.head返回一个_Char_。此外,为了从新版书籍(9415087488L)中获得非溢出结果,函数的返回类型应该是_Long_或_BigInt_ :-) - Boregore

0

另一种变体:

"Hello".aggregate(1L)({(prod,ch) => prod * ch.toLong}, {(p1,p2)=>p1*p2})

0

继续使用同一本书进行进展。 可能看起来有点像Java风格,但它完成了它的工作:

scala> "Hello".map( x => x.toLong).reduce( (a, b) => a * b)
val res45: Long = 9415087488

1st - 这个解决方案是一年前发布的。 2nd - 825152896 是错误的结果。请重新阅读问题以及8年前发布的所有其他答案。 - jwvh
第一:不是相同的解决方案。第二:825152896 是书本本身的产物 :). 已更改为使用 long / unicode。 - Witold Kaczurba
重新第一:当然是的。请查看R Sun发布的“第九种方法”。 - jwvh

0

所以在这里我们把一堆东西放在了一起 :)

    // one way
    val longs = for (c <- str) yield c.toLong
    println(longs.product)

    // 2nd way
    println(str.foldLeft(1L)(_ * _.toInt))

    // 3rd way
    var x = 1L
    for (c <- str) yield x *= c.toInt
    println(x)

    // 4th way
    var product = 1L
    for (c <- str) {
      product *= c.toInt
    }
    println(product)

    // 5th way
    println(str.map(_.toLong).product)

    // 6th way
    println(str.foldLeft(1L)(_ * _))

    // 7th way
    println(str.map(_.toLong).reduceLeft(_ * _)) // my IDE warns `Replace reduce with product` 

    // 8th way
    println(str.map(_.toLong).scanLeft(1L)(_ * _).last)

    // 9th way
    println(str.map(_.toLong).reduce((x, y) => (x * y))) // my IDE warns `Replace reduce with product`

   // using recursion
   def exercise9(str: String): Long = {
    var product = str.head.toLong
    if (str.tail.length > 0) {
        product *= exercise9(str.tail)
    }
    product
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接