Different ways to create random strings in Scala

http://alvinalexander.com/scala/creating-random-strings-in-scala

When it comes to generating random strings with the scala.util.Randomclass, I haven’t figured out yet how to properly use the nextStringmethod. I’ve tried using it in several different ways, but I always get a string of question marks as output, like this:
scala> val r = new scala.util.Random(31)
r: scala.util.Random = scala.util.Random@7d49fa1e

scala> r.nextString(10)
res0: String = ??????????
This happens whether I give Random a seed value or not, and whether I call nextString as a static method or not:
scala> Random.nextString(5)
res1: String = ?????
Update: The solution to this problem is shown in the Comments section below. I’ll update this post when I have more time, but for now I’ll just say that the correct answer is to use this code (and see the comment below):
Random.alphanumeric.take(10).mkString

The Random alphanumeric method

Frankly I haven’t really needed this capability, so this unusual behavior hasn’t bothered me enough for me to take time to look into it. Plus, I could always use the alphanumeric method as follows to get a random string:
scala> val x = Random.alphanumeric
x: scala.collection.immutable.Stream[Char] = Stream(Q, ?)

scala> x take 10 foreach println
Q
n
m
x
S
Q
R
e
P
B
(Note that the alphanumeric method returns a Stream, so you need to coerce the Stream to give you some output, as shown in that example. I don’t think I have any tutorials out here about Scala Streams just yet, but I do cover them in detail in the Scala Cookbook.)

Generating a random string as an exercise

On one particularly cold night in Alaska back in December (it’s January now as I write this) I got bored and decided to write my own random string method. As I started to write the code, I realized there were several different ways to tackle the problem. For me this is a cool thing about Scala; it’s like the old Perl slogan, “There’s more than one way to do it.”
Without any further discussion, I’ll just say that the following Scala code demonstrates several different ways to generate a random string. I start with a “Java-esque” approach, and then show several other ways to create a random string, including some recursive and tail recursive approaches.
import scala.annotation.tailrec

/**
 * Examples of different ways to write "random string" methods in Scala.
 * See the main method for examples of how each method is called.
 * Created by Alvin Alexander, http://alvinalexander.com
 */
object RandomStringExamples {
  
  def main(args: Array[String]) {
    println("1:  " + randomString(10))
    println("2:  " + randomStringArray(10))
    println("3:  " + randomStringRecursive(10).mkString)
    println("3:  " + randomStringRecursive2(10).mkString)
    println("4:  " + randomStringTailRecursive(10, Nil).mkString)
    println("5:  " + randomStringRecursive2Wrapper(10))
    println("6:  " + randomAlphaNumericString(10))
    println("6:  " + randomAlphaNumericString(10))
    println("6:  " + randomAlphaNumericString(10))
    println("x2: " + x2(10, ('a' to 'z') ++ ('A' to 'Z')))
  }

  // 1 - a 'normal' java-esque approach
  def randomString(length: Int) = {
    val r = new scala.util.Random
    val sb = new StringBuilder
    for (i <- 1 to length) {
      sb.append(r.nextPrintableChar)
    }
    sb.toString
  }

  // 2 - similar to #1, but using an array
  def randomStringArray(length: Int) = {
    val r = new scala.util.Random
    val a = new Array[Char](length)
    val sb = new StringBuilder
    for (i <- 0 to length-1) {
      a(i) = r.nextPrintableChar
    }
    a.mkString
  }

  // 3 - recursive, but not tail-recursive
  def randomStringRecursive(n: Int): List[Char] = {
    n match {
      case 1 => List(util.Random.nextPrintableChar)
      case _ => List(util.Random.nextPrintableChar) ++ randomStringRecursive(n-1)
    }
  }

  // 3b - recursive, but not tail-recursive
  def randomStringRecursive2(n: Int): String = {
    n match {
      case 1 => util.Random.nextPrintableChar.toString
      case _ => util.Random.nextPrintableChar.toString ++ randomStringRecursive2(n-1).toString
    }
  }

  // 4 - tail recursive, no wrapper
  @tailrec
  def randomStringTailRecursive(n: Int, list: List[Char]):List[Char] = {
    if (n == 1) util.Random.nextPrintableChar :: list
    else randomStringTailRecursive(n-1, util.Random.nextPrintableChar :: list)
  }

  // 5 - a wrapper around the tail-recursive approach
  def randomStringRecursive2Wrapper(n: Int): String = {
    randomStringTailRecursive(n, Nil).mkString
  }
  
  // 6 - random alphanumeric
  def randomAlphaNumericString(length: Int): String = {
    val chars = ('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')
    randomStringFromCharList(length, chars)
  }
  
  // 7 - random alpha
  def randomAlpha(length: Int): String = {
    val chars = ('a' to 'z') ++ ('A' to 'Z')
    randomStringFromCharList(length, chars)
  }

  // used by #6 and #7
  def randomStringFromCharList(length: Int, chars: Seq[Char]): String = {
    val sb = new StringBuilder
    for (i <- 1 to length) {
      val randomNum = util.Random.nextInt(chars.length)
      sb.append(chars(randomNum))
    }
    sb.toString
  }

  def x(length: Int, chars: Seq[Char]): String = {
    val list = List.range(1, length)
    val arr = new Array[Char](length)
    list.foreach{ e => arr(e) = chars(util.Random.nextInt(chars.length)) }
    list.mkString
  }
  
  // create a fake list so i can use map (or flatMap)
  def x2(length: Int, chars: Seq[Char]): String = {
    val tmpList = List.range(0, length)
    val charList = tmpList.map{ e => chars(util.Random.nextInt(chars.length)) }
    return charList.mkString
  }

}
I’m sure there are many other ways to generate a random string in Scala, but if nothing else, rather than solve a specific problem, I thought I’d just share all these different potential approaches.

Comments

Popular posts from this blog

CEF Python