Scala: iterate through every character of a String and use pattern matching

7.6k Views Asked by At

I think what I'm trying to do is pretty obvious. For every character in a string1, print something using patter matching. (I have string2 there because I will use pattern matching of string1 to do something to string 2 and return String 2)

For some reason my code only prints out "()".

Also how do I make sure that my code returns a String. When I put the code in terminal it says: (string1: String)String => Unit , how do I make it say (string1: String)String => String

def stringPipeline(string1: String) = (string2: String) => {

  for(c <- string1) {
    c match {
      case 'u' => "Upper Case"
      case 'r' => "reverse"
      case _ => "do something"
    }
  }
}

EDIT:

I would just like to point out what I wanted to do with string2:

def stringPipeline(string1: String) = (string2: String) => { 
    for(c <- string1) yield { 
        c match { 
            case 'U' => string2.toUpperCase 
            case 'r' => string2.reverse } } } 

But it's return a vector/list of strings. I want all those cases to work on the same string2 object. So if I test the method on "hello", it should return "OLLEH".

Thanks

4

There are 4 best solutions below

5
Giorgio On BEST ANSWER

If you paste your definition in the Scala REPL you will see that the type of the function you have defined is

stringPipeline: (string1: String)String => Unit

i.e. a function that takes a string string1 as input and returns a closure taking a second string string2 as input, and returning Unit, which is like void in Java and has only the value ().

So why does the returned closure have Unit as return type? Its body contains only one expression:

for(c <- string1) {
    c match {
      case 'u' => "Upper Case"
      case 'r' => "reverse"
      case _ => "do something"
    }
}

for evaluates its internal expression

c match {
    case 'u' => "Upper Case"
    case 'r' => "reverse"
    case _ => "do something"
}

for each possible c in string1 and then returns (). In other words, for is not letting the values produced inside it through.

If you want to print the strings "Upper Case" you need to write

    case 'u' => println("Upper Case")

Then the return value of the closure will still be (), but its evaluation will print the strings in the match expression as a side effect.

On a side note, why did you introduce the parameter string2 if you do not use it?

EDIT

Since the output of the function inside the loop is used as input for the next cycle of the loop, you need a fold function, i.e. something like this:

def stringPipeline(string1: String) = (string2: String) => {
    string1.foldLeft(string2)((s: String, c: Char) =>
        c match {
            case 'u' => s.toUpperCase
            case 'r' => s.reverse
            case _   => s
        }
    )
}
0
Andrey Tyukin On

You forgot a yield after the for ( ... ), therefore the last for simply runs, throws away everything inside the body, and finally returns a unit ().

If you want to return something, you should use something like that:

def stringPipeline(string1: String) = (string2: String) => {
  for (c <- string1) yield {
    c match {
      case 'u' => "Upper Case"
      case 'r' => "reverse"
      case _ => "do something"
    }
  }
}

This will give you some sort of "list of action descriptions" for each string2.

If you really want to print something immediately, you can do this:

def stringPipeline(string1: String) = (string2: String) => {
  for (c <- string1) {
    println(c match {
      case 'u' => "Upper Case"
      case 'r' => "reverse"
      case _ => "do something"
    })
  }
}

but the way it is now, it neither returns anything meaningful, nor does it have any side effects.

EDIT:

Update: if you want to treat string1 as a sequence of operations and string2 as the "material" to which you want to apply those operations, you can do something like this:

def stringPipeline(string1: String) = (string2: String) => {
  string1.foldLeft(string2) { 
    case (s, 'u') => s.toUpperCase
    case (s, 'r') => s.reverse
    case (_, x) => 
      throw new Error("undefined string manipulation: " + x)
  }
}

The above does the following: it starts with the string2, and then applies each operation from string1 to the result of all the transformations accumulated so far.

0
Thomas Böhm On

If your goal is to receive a function, that performs the pattern on a string, you can do something like that:

def stringPipeline(pattern : String) = {
  def handleString(s : String, restPattern : List[Char]) : String = {
     restPattern match{
      case hd :: tl => {
        hd match{
          case 'u' => handleString(s.toUpperCase(), tl)
          case 'r' => handleString(s.reverse, tl)
          case _ => handleString(s, tl)
        }
      }
      case Nil => s    
    }
  }    
  s : String => handleString(s, pattern.toList)
}

This function returns a recursive function, that performs one letter of the pattern after each other, and finally returns the result String.

You can then do something like that:

val s = "urx"
val fun = stringPipeline(s)
println(fun("hello"))

which returns OLLEH

However, it is not an iterative, but a recursive approach (which fits better for a functional programming language like Scala).

0
sarveshseri On

You need to implement your pipeline such that it folds over the characters of ops string with each character being a stage of transformation in pipeline.

You start with the input string for the first fold and then each subsequent fold of ops string will transform the output of last fold.

Now, you just need to add different transform operations when you encounter different characters while folding the ops string.

def stringPipeline(ops: String) = (input: String) => {
  ops.toLowerCase.foldLeft(input)((str: String, op: Char) => op match {
    case 'u' =>
      println("Operator - 'u' :: Transforming - Upper Case")
      str.toUpperCase
    case 'r' =>
      println("Operator - 'r' :: Transforming - Reverse")
      str.reverse
    case _ =>
      println("Operator - 'unknown' :: Doing Nothing")
      str
  })
}

val pipeline1 = stringPipeline("ur")

val s1 = "abcde"

val s1p1 = pipeline1(s1)

// Operator - 'u' :: Transforming - Upper Case
// Operator - 'r' :: Transforming - Reverse
// s1p1: String = EDCBA