HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Alien Numbers - how Scala-ish is my solution?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
numbersscalaishalienhowsolution

Problem

I'm trying to solve an old GCJ. It's a very simple puzzle, but I'm trying to sharpen my Scala-fu.

Basically, you're getting a list of triple number srcLanguage dstLanguage, where number is an integer given in the numeral system of srcLanguage. You should translate it to the numeral system of dstLanguage.

A numeral system is simply a string of all possible digits, in ascending order. The decimal numeral system is represented by 0123456789, the binary numeral system is 01, and the hexadecimal one 0123456789ABCDEF.

For example:

3 0123456789 01 -> 11
3 0123       AB -> BB


Here's how I implemented it in Scala:

case class Langs(num:String,srcLang:String,dstLang:String)
object Langs {def fromLine(line:String):Langs = {
    val ar = line.split(" ");return Langs(ar(0),ar(1),ar(2))}}
object Translate {
  def lang2int(lang:String,num:String):Long = {
    var b = BigDecimal(0)
    val dmap = (lang.toList.zipWithIndex).toMap
    val digitsList = num map dmap
    val valueList = digitsList.reverse.zipWithIndex map (
        x => x._1 -> math.pow(dmap.size,x._2))
    return valueList.map(x=>x._1*x._2).sum.toLong
  }
  def int2lang(lang:String,_num:Long):String = {
    var num = _num
    val dmap = (lang zip (0.toLong to lang.size)).map(_.swap).toMap
    val sb = StringBuilder.newBuilder
    while (num > 0) {
        sb.append(dmap(num % dmap.size))
        num = num/dmap.size
    }
    sb.reverse.toString
  } 
  def lang2lang(l:Langs):String = int2lang(l.dstLang,lang2int(l.srcLang,l.num))
}

object mymain {
  def main(args : Array[String]) : Unit = {
    val s = "A-large-practice"
    val basef = new java.io.FileInputStream("~/Downloads/"+s+".in")
    val f = new java.util.Scanner(basef)
    val out = new java.io.FileWriter(s+".out")
    val n = f.nextInt
    f.nextLine
    for (i <- 1 to n) {
      val nl = f.nextLine
      val l = Langs.fromLine(nl)
      out.write("Case #"+i+": "+Translate.lang2lang(l)+"\n")
    }
    out.close
  }
}

Solution


  • You should definitely use scala.io.Source for File-IO



  • I wouldn't consider String splitting a responsibility of a general-purpose class. This should be done in the main loop



  • For tuples you can write map{ case (one,two) => ... }, which is often clearer than using x._1 and x._2



  • You don't need to write return if it's the last statement of the block



  • You can use pattern matching when defining vals: val Array(x, y, z) = line.split(" ")



Here is my attempt:

case class Langs(num:String, srcLang:String, dstLang:String)

object Langs {
  def fromLine(line:String):Langs = {
    val Array(num, srcLang, dstLang) = line.split(" ")
    Langs(num, srcLang, dstLang)
  }
}

object Translate {
  def lang2int(lang:String,num:String):Long = {
    val dmap = lang.toList.zipWithIndex.toMap
    val digitsList = num map dmap
    val valueList = digitsList.reverse.zipWithIndex map {
      case (one, two) => one -> math.pow(dmap.size, two)}
    valueList.map{case (one,two) => one*two}.sum.toLong
  }

  def int2lang(lang:String, num:Long):String = {
    val dmap = (0.toLong to lang.size zip lang).toMap
    Iterator.iterate(num)( _/dmap.size).takeWhile(_ > 0).map(n => 
      dmap(n % dmap.size)).mkString.reverse
  } 

  def lang2lang(l:Langs):String = int2lang(l.dstLang,lang2int(l.srcLang,l.num))
}


Eliminating the while loop isn't that straight-forward, maybe someone else has an idea how to avoid that Iterator train-wreck.

[Edit]

I asked in another forum for a better solution for int2lang, and got this answer:

def int2lang(lang: String, num: Long): String = {
  val dmap = (0L to lang.size) zip lang toMap
  val size = dmap.size
  def loop(num: Long, l: List[Char]): List[Char] =
    if (num == 0) l else loop(num/size, dmap(num%size) :: l)
  loop(num, Nil).mkString
}


The nice thing about this is that the reverse is gone.

Code Snippets

case class Langs(num:String, srcLang:String, dstLang:String)

object Langs {
  def fromLine(line:String):Langs = {
    val Array(num, srcLang, dstLang) = line.split(" ")
    Langs(num, srcLang, dstLang)
  }
}

object Translate {
  def lang2int(lang:String,num:String):Long = {
    val dmap = lang.toList.zipWithIndex.toMap
    val digitsList = num map dmap
    val valueList = digitsList.reverse.zipWithIndex map {
      case (one, two) => one -> math.pow(dmap.size, two)}
    valueList.map{case (one,two) => one*two}.sum.toLong
  }

  def int2lang(lang:String, num:Long):String = {
    val dmap = (0.toLong to lang.size zip lang).toMap
    Iterator.iterate(num)( _/dmap.size).takeWhile(_ > 0).map(n => 
      dmap(n % dmap.size)).mkString.reverse
  } 

  def lang2lang(l:Langs):String = int2lang(l.dstLang,lang2int(l.srcLang,l.num))
}
def int2lang(lang: String, num: Long): String = {
  val dmap = (0L to lang.size) zip lang toMap
  val size = dmap.size
  def loop(num: Long, l: List[Char]): List[Char] =
    if (num == 0) l else loop(num/size, dmap(num%size) :: l)
  loop(num, Nil).mkString
}

Context

StackExchange Code Review Q#2502, answer score: 3

Revisions (0)

No revisions yet.