32

So I'm working through a few of the exercises in "Scala for the Impatient" and one of them is:

Write a for loop for computing the product of the Unicode codes of all letters in a string. For example, the product of the characters in "Hello" is 9415087488 L.

The next problem is to do the same, but without a for loop - it hints that we should check StringOps in Scaladoc.

I checked the RichChar and StringOps section in Scaladoc, and perhaps I'm misreading or looking in the wrong places, but I can't find anything that gets me to match their output. I've thus far tried:

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

Which does not match their output.

How do I do this, in both the for and non-for way?

Thanks!

1
  • There's another way, if you do it in two steps. Hint: the last of the two methods only have an implicit parameter. Commented Jun 23, 2012 at 16:50

10 Answers 10

31

When you do x.foldLeft(1)(_ * _.toInt), the result type will be inference to an Int, but 9415087488 is too large for an Int to store it.

So you need to tell Scala using Long to store it.

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
  • 5
    Thanks from me too. Oddly, in the pdf-Version I just downloaded, the wrong value is given (825152896).
    – Markus
    Commented Dec 16, 2012 at 21:58
  • strange, but with "for (fi <- "Hello") i *= fi.toChar" I have same value as in book. Hmmm.
    – user2850243
    Commented Jun 14, 2014 at 21:43
  • @Dubysa whats the type of i variable? Your i need to be an Long to get correct result, or Scala will inference the expression to be an Int (an Int multiple with a Char will become an Int) which will cause overflow.
    – Brian Hsu
    Commented Jun 15, 2014 at 0:12
  • well, my REPL does not see a problem in there (always knew my laptop is smart :D) I did post it as an answer.
    – user2850243
    Commented Jun 15, 2014 at 14:39
  • You can also write it as (x :\ 1L)(_ * _) Commented Jul 30, 2018 at 20:52
13

If you convert each RichChar of String .toLong it's also works. For example, this:

str.map (_.toLong).product - work's fine and without foldLeft or cycles

This is cyclic variant:

def product(str: String): Long = {
    var prod: Long = 1
    for (ch <- str) prod *= ch
    prod
}
8

There's a special "product" method in StringOps which multiplies up the elements of the collection. But it uses Char type because the string consist of char elements. And we're having overflow while trying to compute "Hello".product. So I converted the string to Vector of Long Unicode values by "Hello".map(_.toLong) and computed the product of its elements by this code:

scala> "Hello".map(_.toLong).product
res79: Long = 9415087488
2
  • Thanks! I suspected an overflow, too. But was unsure how to ask it to map it to a Long.
    – asgs
    Commented Nov 20, 2016 at 18:52
  • The for loop in Rihad's answer and the map in this answer are notational variants. The compiler converts the for loop to a map, I believe. It's a matter of taste which you use.
    – airfoyle
    Commented Jun 18, 2017 at 17:49
5

Here's another way:

scala> (for (c <- "Hello") yield c.toLong).product
res36: Long = 9415087488
3

The most straightforward way I've found to do this is:

"Hello".foldLeft(1L)((x:Long, y:Char) => x*y)

The method takes two parameters: a Long and a delegate function that takes a Long and a Char and returns a Long. You can pass an anonymous function in directly like this, or you can define the function elsewhere and pass it in, like so:

def multiply(x:Long, y:Char) = {
    x*y
}
"Hello".foldLeft(1L)(multiply)
2

I'd think the conversion to intermediate map is inefficient because in this case the collection is iterated twice: once to create map of longs and second time to multiply all elements. Unnecessary temporary collection of Long's also is not needed. I give my vote to

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

The exercise required to don't use for loop, so I've gone recursive:

def unicode_rec(s: String): Int = 
  if(s == "") 1 
  else s.head.toInt * unicode_rec(s.tail)
1
  • Recursive function was also my first intuition for a solution. You can actually leave out the .toInt part of s.head.toInt since .headreturns a Char. Also, to get the non-overflow result from the newer edition of the book (9415087488L), return type of the function should be Long or BigInt :-)
    – Boregore
    Commented Mar 13, 2019 at 15:21
0

Another variant:

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

So here we have in bunch together :)

    // 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
  }
0

Also progressing wit the same book. Might look like a bit java-ish flavoured, but it does its job:

scala> "Hello".map( x => x.toLong).reduce( (a, b) => a * b)
val res45: Long = 9415087488
3
  • 1st - This solution was posted a year ago. 2nd - 825152896 is the wrong result. Re-read the question and all the other answers from 8 years ago.
    – jwvh
    Commented Mar 2, 2021 at 5:46
  • 1st - not the same solution. 2nd: 825152896 was the product from the book itself :). CHanged to use long / unicode. Commented Mar 2, 2021 at 6:08
  • Re-1st: Sure it is. See "9th way" posted by R Sun.
    – jwvh
    Commented Mar 2, 2021 at 8:37

Not the answer you're looking for? Browse other questions tagged or ask your own question.