I've worked out a functioning Vigenère encryption in Scala, and while it doesn't seem like my code is too hideous, I'm sure I could make it more functional and Scala-esque. Primarily, it seems like:
- I've made the full key with a for-expression / zipWithIndex, I'm wondering if this is the best way to do this though. Would it be better to do this inside the encipher() function? Using an anonymous function? The way I have it now works, but I'm curious if there's a way to do so that'd be more in line with Scala best practices.
- The actual encryption portion, specifically the output for
case (c, i)
, is sort of messy/convoluted, and it seems like there might be a better way to do it.
Of course, I'll be grateful for any feedback. Definitely still getting my feet wet with Scala, and any help getting into a more functional state of mind would be appreciated.
object Vigenere {
// alphabet for use throughout object
private val alphabet = ('A' to 'Z').toList
//empty array to be filled with fillTabula() function
private var tabulaRecta = Array.ofDim[Char](26, 26)
/* fills the tabula recta array
** creates 26 rows of alphabets, each alphabet shifted
** left by one letter each iteration */
def fillTabula() = {
for (row <- 0 to 25) {
for (col <- 0 to 25) {
tabulaRecta(row)(col) = alphabet((row + col) % 26)
}
}
}
/* create the full key by repeating the key until its as long
** as the plaintext. */
def makeFullkey(key: String, text:String): String = {
val fkey = for (c <- text.zipWithIndex) yield c match {
case (c, i) => key(i % key.length)
}
return fkey.mkString
}
def encipher(key: String, text:String): String = {
// fill the tabula recta
fillTabula()
val fkey = makeFullkey(key, text)
/* to encipher, zip the plaintext to return each letter with
** its index. if it's a space, just add space to ciphertext.
** if it's not a space, find the corresponding row in the
** tabula recta, then go to the appropriate column to
** return the ciphertext character.
** the second case here is a mess, there's got to be a nicer way. */
val ciphertext = for (c <- text.zipWithIndex) yield c match {
case (' ', _) => ' '
case (c, i) => tabulaRecta(alphabet.indexOf(fkey(i)))(alphabet.indexOf(c))
}
return ciphertext.mkString
}
def main(args: Array[String]) {
val Array(text, key) = args
println("Plaintext: " + text)
println("Key: " + key)
println("Ciphertext: " + encipher(key.toUpperCase, text.toUpperCase))
}
}
Edit: I realized that I could make the fullkey using zipWithIndex as well, and also found a better place to use toUpperCase. I've changed the code and updated my question accordingly.