 # Base 6 conversion as fold?

#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())
}
``````

#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))
}
``````

#4

@tarsa, I think what you’re suggesting is that my tail recursive function implementation is not so bad. right?

#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.

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

#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
}
``````