W ramach nauki Scali zrobiłem zadanie z https://www.hackerrank.com/challenges/brainf-k-interpreter-fp/problem, polegające na stworzeniu interpretera Brainfucka.
Popełniłem kawałek kodu, co do którego mam wątpliwości:
- Czy używanie return/ifów jest "scalowe" ?
- Czy rzucają się w oczy inne rzeczy, które nie pasują do podejścia funkcyjnego?
import scala.annotation.tailrec
object Solution {
def executeBrainFuck(input: List[Byte], code: Array[Char]) = {
def incDV(k : Int) = if (k==255) 0 else k+1
def decDV(k : Int) = if (k==0) 255 else k-1
@tailrec
def _executeBF(
opCounter: Int, // operations counter
data: Map[Int, Int], // data segment
input: List[Byte], // input segment
code: Array[Char], // code segment
codePointer: Int, // pointer to the next instruction
dataPointer: Int, // pointer to data cell
brackets: Map[Int, Int] // opening/closing brackets information
) :Unit = {
if (opCounter == 100000 ) {
println("\nPROCESS TIME OUT. KILLED!!!")
return None
}
var newDataPointer = dataPointer
var newCodePointer = codePointer + 1
var newInput = input
var newData = data
// update data/input/pointers as per the operation semantics
code(codePointer) match {
case '>' => newDataPointer += 1
case '<' => newDataPointer -= 1
case '+' => newData = data.updated(dataPointer,incDV(data.getOrElse(dataPointer, 0)))
case '-' => newData = data.updated(dataPointer,decDV(data.getOrElse(dataPointer, 0)))
case '.' => print(data.getOrElse(dataPointer,0).toChar)
case '[' => if (data.getOrElse(dataPointer, 0) == 0) newCodePointer = brackets(codePointer)
case ']' => if (data.getOrElse(dataPointer, 0) != 0) newCodePointer = brackets(codePointer)
case ',' => if (!newInput.isEmpty) {
newData = data.updated(dataPointer,newInput.head)
newInput = newInput.tail
}
} // opcode
if (newCodePointer<code.length)
_executeBF(opCounter + 1, newData, newInput, code, newCodePointer, newDataPointer, brackets)
}
// For given BF code returns list of pairs:
// (index of a bracket, index of the corresponding bracket (either closing or opening) )
def pairBrackets(code: List[Char]): List[(Int, Int)] = {
// 1. assign index for each character
// 2. filter everything but square brackets
// 3. compute pairs
code
.zipWithIndex
.filter(x => x._1 == ']' || x._1 == '[')
.foldLeft((List[(Int, Int)](), List[(Char, Int)]())) {
// acc(1) - accumulator for holding pairs
// acc(2) - stack used for construction
case (acc, e) =>
// e is ( _1 =bracket, _2 = bracket's index)
e._1 match {
// push opening bracket to the stack
case '[' => (acc._1, List(e) ++ acc._2)
// pop a bracket from the stack and accumulate: A:B, as A-B and B-A
case _ => (List[(Int, Int)]((acc._2.head._2, e._2),(e._2,acc._2.head._2)) ++ acc._1, acc._2.tail)
}
}._1 // return list of pairs
}
// prepare brackets information
val brackets = pairBrackets(code.toList).toMap[Int,Int]
// execute the code
_executeBF(0, Map[Int,Int](), input, code, 0, 0, brackets)
}
def main(args: Array[String]) {
// parse HackerRank input
val lines = io.Source.stdin.getLines.toList
val nm = lines.take(1)(0).split(" ")
val n = nm(0).toInt
val m = nm(1).toInt
// input data for BF interpreter
val input = lines.drop(1).head.toList.filter(_ != '$').map(_.toByte)
// code to be executed, skip all the garbage
val code = lines.drop(2).take(m).map(_.toCharArray).flatten.filter("]><+-.,[".toCharArray.contains(_)).toArray
// call the interpreter
executeBrainFuck(input,code)
}
}