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

Attempting to eliminate var (and imperative style) from my Piece class

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

Problem

I've been cooking with gas since I got Daniel C Sobral's help on my last question. I am now re-reading Odersky's "Programming in Scala, 2nd Edition" (finished my first reading about this time last year).

I am eager to understand how to alter my mental modeling of problems to more fully embrace the functional programming style. However, I have spent hours looking at the code below attempting to figure out how to eliminate the var references. I am sure my imperative past is overshadowing and blinding me to functional possibilities.

I think I have retained overall "referential transparency" at each method; i.e. none of the var-ness escapes the local scope of the method (or function) within which it is defined. However, I would like to understand how I might achieve a higher level of functional programming purity, even if it is slightly unreasonable, within each method. I am more looking for ways I need to change my problem solving approaches to be more myopically functional in nature.

Specifically, how might I approach eliminate each instance of var.

Thank you for any guidance.

```
case class Bitmap2d(name: String, rowsByColumns: List[List[Boolean]], faceUp: Boolean) {
//require(rowsByColumns != null) //Assumed that if null was allowed as parameter, an Option would be used
require(validateRectangular, "all rows must have same length")

def validateRectangular: Boolean = {
rowsByColumns.forall(_.size / rowsByColumns.head.size == 1)
}
}

class Piece(name: String, charRep: Char, rowsByColumnsAndUp: List[List[Boolean]]) {
val translations = createTranslations()

def printTranslations() = {
println("Name: " + name + " Char: " + charRep)
for (bitmap2d = 0) && (bits 1) {
rowsByColumns.reverse
}
else {
rowsByColumns
}
}
private def translateAroundYAxis(rowsByColumns: List[List[Boolean]]) = {
if (rowsByColumns.head.size > 1) {
for (row <- rowsByColumns)
yield row.reverse
}
else {
rowsByCo

Solution

One good technique at eliminating vars is recursion -- it can certainly be used in this example. Alternatively, you can identify a common pattern, such as fold, traversal, etc. For example:

var bitmaps = Set[List[List[Boolean]]]()
var result = List[Bitmap2d]()
for (
  bitmap2dRaw <- bitmap2dRaws
  if (!bitmaps.contains(bitmap2dRaw._2))
)
{
  bitmaps += bitmap2dRaw._2;
  result = Bitmap2d(bitmap2dRaw._1, bitmap2dRaw._2, bitmap2dRaw._3) :: result
}
result.reverse


Recursion:

def getResult(bitmap2dRaws: ???, bitmap: Set[List[List[Boolean]]], result: List[Bitmap2d]): List[Bitmap2d] = bitmap2dRaws match {
  case Seq(bitmap2dRaw, rest: _*) if (!bitmaps.contains(bitmap2dRaw._2) =>
    getResult(rest, bitmaps + bitmap2dRaw._2, Bitmap2d(bitmap2dRaw._1, bitmap2dRaw._2, bitmap2dRaw._3) :: result)
  case Seq(bitmap2dRaw, rest: _*) =>
    getResult(rest, bitmaps, result)
  case _ => result.reverse
}
getResult(bitmap2dRaws, Set[List[List[Boolean]], Nil)


Fold:

bitmap2dRaws.foldLeft((Set[List[List[Boolean]]], List[Bitmap2d])) {
  case ((bitmaps, result), bitmap2dRaw) if (!bitmaps.contains(bitmap2dRaw._2) =>
    (bitmaps + bitmap2dRaw._2, Bitmap2d(bitmap2dRaw._1, bitmap2dRaw._2, bitmap2dRaw._3) :: result)
  case ((bitmaps, result), _) => (bitmaps, result)
}._2.reverse


You can use the same techniques for the var inside translateRotate90DegreesRight as well.

In other places you might use Option:

private def translationDescription(bits: Int) = {
  var result = List[String]()
  if ((bits & 1) == 1) {
    result = "FlipX" :: result
  }
  if ((bits & 2) == 2) {
    result = "FlipY" :: result
  }
  if ((bits & 4) == 4) {
    result = "Rotate" :: result
  }
  result.reverse
}


becomes:

private def translationDescription(bits: Int) = {
  val flipX = if ((bits & 1) == 1) Some("FlipX") else None
  val flipY = if ((bits & 2) == 2) Some("FlipY") else None
  val rotate = if ((bits & 4) == 4) Some("Rotate") else None
  List(flipX, flipY, rotate).flatten // if this doesn't work, try flatMap(x => x)
}


Finally (unless I missed something), the var inside translateBasedOnBitsForXYR can be avoided simply by using multiple val, and if statements like this:

val xTranslation = if ((bits & 1) == 1) translateAroundXAxis(rowsByColumns) else rowsByColumns


and so on.

Code Snippets

var bitmaps = Set[List[List[Boolean]]]()
var result = List[Bitmap2d]()
for (
  bitmap2dRaw <- bitmap2dRaws
  if (!bitmaps.contains(bitmap2dRaw._2))
)
{
  bitmaps += bitmap2dRaw._2;
  result = Bitmap2d(bitmap2dRaw._1, bitmap2dRaw._2, bitmap2dRaw._3) :: result
}
result.reverse
def getResult(bitmap2dRaws: ???, bitmap: Set[List[List[Boolean]]], result: List[Bitmap2d]): List[Bitmap2d] = bitmap2dRaws match {
  case Seq(bitmap2dRaw, rest: _*) if (!bitmaps.contains(bitmap2dRaw._2) =>
    getResult(rest, bitmaps + bitmap2dRaw._2, Bitmap2d(bitmap2dRaw._1, bitmap2dRaw._2, bitmap2dRaw._3) :: result)
  case Seq(bitmap2dRaw, rest: _*) =>
    getResult(rest, bitmaps, result)
  case _ => result.reverse
}
getResult(bitmap2dRaws, Set[List[List[Boolean]], Nil)
bitmap2dRaws.foldLeft((Set[List[List[Boolean]]], List[Bitmap2d])) {
  case ((bitmaps, result), bitmap2dRaw) if (!bitmaps.contains(bitmap2dRaw._2) =>
    (bitmaps + bitmap2dRaw._2, Bitmap2d(bitmap2dRaw._1, bitmap2dRaw._2, bitmap2dRaw._3) :: result)
  case ((bitmaps, result), _) => (bitmaps, result)
}._2.reverse
private def translationDescription(bits: Int) = {
  var result = List[String]()
  if ((bits & 1) == 1) {
    result = "FlipX" :: result
  }
  if ((bits & 2) == 2) {
    result = "FlipY" :: result
  }
  if ((bits & 4) == 4) {
    result = "Rotate" :: result
  }
  result.reverse
}
private def translationDescription(bits: Int) = {
  val flipX = if ((bits & 1) == 1) Some("FlipX") else None
  val flipY = if ((bits & 2) == 2) Some("FlipY") else None
  val rotate = if ((bits & 4) == 4) Some("Rotate") else None
  List(flipX, flipY, rotate).flatten // if this doesn't work, try flatMap(x => x)
}

Context

StackExchange Code Review Q#10696, answer score: 2

Revisions (0)

No revisions yet.