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 <- translations) {
println(" Orientation: " + bitmap2d.name)
for (row <- bitmap2d.rowsByColumns)
{
for (pixel <- row)
{
val value = if (pixel) "1" else "0"
print(value);
}
println()
}
}
}
private def createTranslations() = {
//generate all 7 translations
val bitmap2dRaws =
for (i <- 0 to 7)
yield translateBasedOnBitsForXYR(rowsByColumnsAndUp, i)
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
}
private def translationSideUp(bits: Int) = {
val flipX = ((bits & 1) == 1)
val flipY = ((bits & 2) == 2)
((flipX || flipY) && (!(flipX && flipY)))
}
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 translateBasedOnBitsForXYR(rowsByColumns: List[List[Boolean]], bits: Int) = {
require (((bits >= 0) && (bits < 8)), "bits must contain a value between 0 (inclusive) and 8 (exclusive)")
var result = rowsByColumns;
if ((bits & 1) == 1) {
result = translateAroundXAxis(result)
}
if ((bits & 2) == 2) {
result = translateAroundYAxis(result)
}
if ((bits & 4) == 4) {
result = translateRotate90DegreesRight(result)
}
(translationDescription(bits).mkString("+") , result, translationSideUp(bits))
}
private def translateAroundXAxis(rowsByColumns: List[List[Boolean]]) = {
if (rowsByColumns.size > 1) {
rowsByColumns.reverse
}
else {
rowsByColumns
}
}
private def translateAroundYAxis(rowsByColumns: List[List[Boolean]]) = {
if (rowsByColumns.head.size > 1) {
for (row <- rowsByColumns)
yield row.reverse
}
else {
rowsByColumns
}
}
private def translateRotate90DegreesRight(rowsByColumns: List[List[Boolean]]) = {
val width = rowsByColumns.head.size
val height = rowsByColumns.size
val linearized = //need non-recursive random access
(
for {
row <- rowsByColumns
pixel <- row
} yield pixel
).toArray
var result = List[List[Boolean]]()
for (i <- 0 to (width - 1)) {
var tempRow = List[Boolean]()
for (j <- 0 to (height - 1)) {
tempRow = linearized((width * (j + 1)) - 1 - i) :: tempRow
}
result = tempRow :: result
}
result
}
}
value
, only used once? Wouldn'tprint(if (pixel) "1" else "0")
be at least just as readable? (Also, I'd suggest adding some line breaks for the extra-wide lines.) \$\endgroup\$