jimka
March 9, 2019, 12:32pm
#1
I have a suspicion that I can more elegantly express the function `longToCube`

as some type of `fold`

operation. I’m basically dividing and moding by 6, 24 times. Each time I perform the mod 6, I use that to grab a character by index out of the array `cubeColors`

. My function accumulates these into a list by successive consing, using tail call, and finally reverses the list and calls `mkString`

I’d appreciate suggestions.

```
type Cube = String
val cubeColors = Array('W', 'G', 'Y', 'B', 'O', 'R')
def cubeToLong(cube:Cube):Long = {
// convert from string to base 6
cube.foldLeft(0L)((acc:Long,ch:Char)=>(acc * 6 + cubeColors.indexOf(ch)))
}
def longToCube(n:Long):Cube = {
def recur(residue:Long,digit:Int, acc:List[Char]):Cube = {
if ( digit == 0)
acc.mkString
else {
recur(residue / 6, digit - 1, cubeColors((residue % 6).toInt)::acc)
}
}
recur(n, 24, List())
}
```

tarsa
March 9, 2019, 3:27pm
#3
Folds are done on collections, so you would need to create an artifical collection beforehand. Is it worth it?

In your case you can have succint code without folds:

```
type Cube = String
val cubeColors = Array('W', 'G', 'Y', 'B', 'O', 'R')
val colorToDigit: Array[Char] = {
Array.tabulate[Char](128) { digitAscii =>
(cubeColors.indexOf(digitAscii.toChar) + '0').toChar
}
}
val pad: String = "0" * 24
def cubeToLong(cube: Cube): Long =
java.lang.Long.parseLong(cube.map(color => colorToDigit(color)), 6)
def longToCube(n: Long): Cube = {
val digits = (pad + java.lang.Long.toString(n, 6)).takeRight(24)
digits.map(digit => cubeColors(digit - '0'))
}
```

or in more object oriented form:

```
class Cube(val raw: Long) extends AnyVal {
def toDigits: String =
(Cube.pad + java.lang.Long.toString(raw, 6)).takeRight(24)
def toColors: String =
toDigits.map(digit => Cube.cubeColors(digit - '0'))
override def toString: String =
toColors
}
object Cube {
val cubeColors = Array('W', 'G', 'Y', 'B', 'O', 'R')
val colorToDigit: Array[Char] = {
Array.tabulate[Char](128) { digitAscii =>
(cubeColors.indexOf(digitAscii.toChar) + '0').toChar
}
}
val pad: String = "0" * 24
def fromColors(colors: String): Cube =
fromDigitsBase6(colors.map(color => colorToDigit(color)))
def fromDigitsBase6(digits: String): Cube =
new Cube(java.lang.Long.parseLong(digits, 6))
}
```

jimka
March 9, 2019, 3:53pm
#4
@tarsa , I think what you’re suggesting is that my tail recursive function implementation is not so bad. right?

jimka
March 9, 2019, 3:56pm
#5
BTW, I could implement a function similar to foldRight/foldLeft on Long so that I can treat a long like a sequence of digits of a given base? That would have the benefit of making the function cubeToLong look much more similar to is inverse function, longToCube.

tarsa
March 9, 2019, 4:14pm
#6
Well, your tail recursive function definitely does the job.

If you first convert Long to series of digits (base 6) then you don’t need fold anymore - simple map suffices (like in my version).

1 Like

jimka
March 9, 2019, 4:41pm
#7
Not sure if it is more readable, but here is what I get when I try to make a fold-like function which maps across the digits of a number from right to left, in a specified base.

```
def foldRightDigits[T](num:Long, digits:Int, base:Int)(initial:T,f:(T,Int)=>T):T = {
def recur(digits:Int,acc:T,num:Long):T = {
if (digits == 0)
acc
else
recur(digits-1, f(acc, (num % base).toInt), num / base)
}
recur(digits, initial, num)
}
def cubeToLong(cube:Cube):Long = {
// convert from string to base 6
cube.foldLeft(0L)((acc:Long,ch:Char)=>(acc * 6 + cubeColors.indexOf(ch)))
}
def longToCube(n:Long):Cube = {
foldRightDigits(n, 24, 6)(List(),(acc:List[Char],digit:Int) => cubeColors(digit)::acc).mkString
}
```