5
\$\begingroup\$

I've just started teaching myself Scala to get my feet wet with functional programming, and I'm messing with around with some classical cryptography. The following code produces monoalphabetic simple substitution encryption, but I'm still trying to wrap my head around both Scala itself and functional style, so I'm sure this could be more concise and FP-oriented. For example, I'd love to get rid of var accumText and use a val instead, but I haven't been able to figure out how to make that work. Any advice on getting this more Scala-esque and FP-oriented would be greatly appreciated!

object SimpSub {

  // alphabet array
  private val alphabet = ('A' to 'Z').toList

  def main(args: Array[String]) {
    // split text into char list
    val text = args(0).toUpperCase.toList
    val key = args(1).toUpperCase.toList

    def buildCipherAlphabet(): List[Char] = {
      // build the cipher alphabet by concatenating the key to 
      // existing alphabet and removing duplicate letters
      return (key ::: alphabet).distinct
    }

    def encipher(): List[Char] = {
      // get the cipher alphabet
      val newAlph = buildCipherAlphabet()
      var accumText: String = "" 
      for (c <- text) {
        // add in whitespace
        if (c == ' ')
            accumText += c
        else
          // add the letter of the cipher alphabet corresponding to the current character
          // in the plaintext
          accumText += newAlph(alphabet.indexOf(c)) 
      }
      val cipherText = accumText.toList
      return cipherText
    }

    val cipherText = encipher()
    println("Plaintext: " + args(0))
    println("Key: " + args(1))
    print("Ciphertext: ")
    for (c <- cipherText)
      print(c)
    print("\n")
  }
}
\$\endgroup\$
4
\$\begingroup\$

encipher() deserves to be a full function with two parameters rather than just a closure within main(). It should take a key parameter first, because you can think of enciphering with a key as a transformation to be applied to the text. (For that matter, I suggest swapping your args(0) and args(1) as well.) The function would be more usable if it accepts and returns strings instead of lists.

Instead of writing args(0) twice and args(1) twice, you should assign text and key more appropriately, by not munging them prematurely with .toUpperCase.toList. I also suggest using destructuring to assign both values at once.

With the changes above, main() can look very clean.

As for the encipher() function, you can simplify it by using

  • for-comprehensions instead of repeated concatenation to an empty string
  • pattern matching instead of if-else.
object SimpSub {
  private val alphabet = ('A' to 'Z').toList

  def encipher(key: String, text: String): String = {
    // Build the cipher alphabet by concatenating the key to
    // the alphabet and removing duplicate letters
    val cipherAlphabet = (key.toUpperCase.toList ::: alphabet).distinct

    val cipherText = for (c <- text.toUpperCase) yield c match {
      case ' ' => ' '
      case _   => cipherAlphabet(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, text))
  }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Wow, that really cleaned up a lot. I definitely need to get in the mindset of using for-comprehensions more. Thanks! \$\endgroup\$ – djc Jul 8 '15 at 20:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.