Question: Background A string is well formed with respect to parentheses if the parentheses are balanced. An example well formed string is (a+b*(d+(e))-f) A string that

Background

A string is well formed with respect to parentheses if the parentheses are balanced. An example well formed string is

(a+b*(d+(e))-f)

A string that is not well formed is

(a+b*(d+(e-g))

One way to see that it is not well formed is to count up left parentheses (there are three of them) versus right parentheses (there are two of them). Since these counts are different, the input is not well formed.

Unfortunately, that rule is not enough: another example of a not well-formed string is

)b(

Here the counts are the same, but the string is not well formed. In class it will be shown how, with the use of a stack, it is easy to check if a string is well formed.

For checking whether or not a string is well formed, we can just ignore everything but parentheses. The previous input examples become, when we ignore everything except parentheses,

( ( ( ) ) )

( ( ( ) )

) (

(Removing everything except parentheses is a kind of "filter" step.)

Algorithm

Checking the well-formed property turns out to be simple with this algorithm. Write down 0 on a line below the input parentheses (we'll put in some spaces to make things easier to read). Then, going from left to right, increase the previous number below a left parenthesis and decrease the number below a right parenthesis. The values are a kind of "nesting depth" of the input symbols. Here is an example

( ( ( ) ) )

0 1 2 3 2 1 0

The decision that this is well-formed is that the final value on the right is 0, and nowhere was there a negative value. Here's another example

( ) ( ) ( ( ) )

0 1 0 1 0 1 2 1 0

What about an input that is not well formed?

) (

0 -1 0

Because there was a negative value in the numbers, this input fails the well-formed test.

( ( ( ) ( ) )

0 1 2 3 2 3 2 1

That is another way that input is not well formed: the final number was 1, not 0.

More Complicated

Two useful contributions of the previous homework are (1) a Stack class, and (2) code that splits up input on the basis of whitespace and symbols. For this homework, the symbols will be different kinds of parentheses.

Although it is simple to deal with plain parentheses, by counting left to right, the problem becomes harder when we allow multiple kinds of parentheses: ( ) [ ] { } -- these are just different kinds of parentheses. The question is how to generalize the simple solution for one kind to having multiple kinds. This will also be discussed in class. An interesting puzzle to think about is how to recognize that

( [ ) ]

is not a well-formed input. The algorithm here is to use a stack, going left to right through the input, either pushing an input symbol on the stack, or popping a symbol off the stack in each step. If the stack is empty at the end of the input, then the input is well formed.

All types of left parenthesis, (, [, and {, are pushed onto the stack when they are encountered, going left to right. All types of right parenthesis, when they are encountered, should attempt to pop the stack (if the stack is empty, then the input is not well formed). The kind of left parenthesis returned by pop() should be exactly the same kind of right parenthesis which triggered the pop() -- if this is not true, then the input is not well formed.

More Kinds of Parenthesis

This topic is discussed in this page (Links to an external site.)Links to an external site., and will be part of unit tests.

What to Download

We have to wait until we know all students have finished submitting work for the previous homework before the IntelliJ project can be revealed. The file oct18.zip has solutions to the 11 October homework, so that everyone has a working Stack class and a Tokenizer.

In the project, you will find two source kotlin files and one testing file. The two source files are oct11.kt and oct18.kt. Don't change oct11.kt (unless you need to add println statements for debugging, that's fine) - the oct11.kt file has a working Stack and Tokenizer.

The file oct18.kt has some things already done. There is an interface WFF and a class EasyWFF which implements WFF. Part of the code is already done for you, you only need to work on the recognizer method. You can see in the existing code how it creates S, the Stack, and T, the Tokenizer. Then it calls T.addSymbol to add the parentheses, so that the input can be split up in a convenient way. If you don't understand what this code does, feel free to add a main() method, print some things, and get some confidence first.

There are many tests cases to satisfy, starting with easy ones.

Please complete the following code in Kotlin, Only Recognize class need to be completed everything else is completed, All teste cases should pass.

class StackError(): Error() // subtype of Error, just for Stack // Stack Interface interface Stack { val debug: String // for debugging, print entire stack  val size: Int // the stack's current number of elements  val isEmpty: Boolean // true if the stack is empty  fun clear() // empty the stack entirely  // both peek() and pop() throw StackError when stack is empty  fun peek(): T // return value on top of Stack  fun pop(): T // return element on top, plus remove that element  fun push(item:T) // push new item on top of stack  fun hasElem(value:T): Boolean // return true if stack contains value  } interface Tokenizer { fun addSymbol(s:String) // add symbol pair to Parser  fun tokenize(input:String): List // break apart  // input into list of Strings separated by  // (1) whitespace, and (2) symbols added previously } /**** Stack Implementation ****/ class EasyStack(): Stack { val store = mutableListOf() // implements the stack, initially empty  override val debug: String get() = "$store"  override val size: Int get() = store.size  override val isEmpty: Boolean get() = store.isEmpty() override fun clear() = store.clear() override fun peek(): T { if (isEmpty) throw StackError() else return store.last() } override fun pop(): T { if (isEmpty) throw StackError() val r = store.last() store.removeAt(store.size-1) return r } override fun push(item:T) { store.add(item) } override fun hasElem(value:T) = store.contains(value) } /**** Implementation of Tokenizer ****/ class EasyTokenizer(): Tokenizer { val symbols = mutableListOf() override fun addSymbol(s:String) { if (s in symbols) return  // add the symbol to the current list  symbols.add(s) // then sort symbols with longest strings first  symbols.sortBy { it.length } // first shortest first,  symbols.reverse() // then flip backwards  } private fun subtokenize(input: String): List { // input needs to be a string with no whitespace  val R = mutableListOf() // used to store output  var cursor = 0 // start at leftmost character of input  var currentToken = "" // we use currentToken in the loop  while (cursor < input.length) { // check if string at cursor matches one of the symbols  var symbolfound = false  for (sym in symbols) { if (sym.length > input.substring(cursor).length) continue  if (sym != input.substring(cursor, cursor + sym.length)) continue  // sym found at start of current cursor position  if (currentToken.length > 0) { // pause to add current token  R.add(currentToken) currentToken = ""  } R.add(sym) // then add matching symbol  symbolfound = true  cursor += sym.length  } if (!symbolfound) { currentToken = currentToken + input[cursor] cursor += 1 } } if (currentToken.length > 0) R.add(currentToken) return R } override fun tokenize(input: String): List { val R = mutableListOf() // first break apart input with any whitespace  val nonwhites = input.trim().split(Regex("\\s+")) // next, for each thing in nonwhites, tokenize it by symbols  for (word in nonwhites) { R.addAll(subtokenize(word)) } return R } } 
 
 
 
// define methods that recognizer will have interface WFF { fun addParens(left:String, right:String) // called to add parentheses fun recognize(input:String): Boolean // true if input is Well Formed } // this class implements the recognizer class EasyWFF(): WFF { val parenTable = mutableListOf>() val S = EasyStack() as Stack val T = EasyTokenizer() as Tokenizer // the addParens method is called to add a pair (left,right) to table override fun addParens(left: String, right: String) { parenTable.add(left to right) T.addSymbol(left) T.addSymbol(right) } override fun recognize(input: String): Boolean { val splitup = T.tokenize(input) // write some code here that uses stack S and the list in splitup return false } }
 
Following Unit Tests Must Pass.
class Oct18Test { @Test fun test1() { // try cases with one kind of parenthesis, ( and )  val P: WFF = EasyWFF() P.addParens("(", ")") assertTrue(P.recognize("(a + b)")) } @Test fun test2() { val P: WFF = EasyWFF() P.addParens("(", ")") assertFalse(P.recognize("(")) } @Test fun test3() { val P: WFF = EasyWFF() P.addParens("(", ")") assertFalse(P.recognize(")")) } @Test fun test4() { val P: WFF = EasyWFF() P.addParens("(", ")") assertFalse(P.recognize(")(")) } @Test fun test5() { val P: WFF = EasyWFF() P.addParens("(", ")") assertFalse(P.recognize("(a * b))")) } @Test fun test6() { val P: WFF = EasyWFF() P.addParens("(", ")") assertFalse(P.recognize("(x+y")) } @Test fun test7() { val P: WFF = EasyWFF() P.addParens("(", ")") assertTrue(P.recognize("(a*(2+b)/c)")) } @Test fun test8() { val P: WFF = EasyWFF() // finally we test with two kinds of parenthesis  P.addParens("(", ")") P.addParens("[","]") assertTrue(P.recognize("2+(E[1]*E[2])")) } @Test fun test9() { val P: WFF = EasyWFF() // finally we test with two kinds of parenthesis  P.addParens("(", ")") P.addParens("[","]") assertFalse(P.recognize("([)]")) } @Test fun test10() { val P: WFF = EasyWFF() // finally we test with two kinds of parenthesis  P.addParens("(", ")") P.addParens("[", "]") P.addParens("{", "}") P.addParens("(.", ".)") assertFalse(P.recognize("t > ( j = 5 .)")) } @Test fun test11() { val P: WFF = EasyWFF() // finally we test with two kinds of parenthesis  P.addParens("(", ")") P.addParens("[", "]") P.addParens("{", "}") P.addParens("/*", "*/") val expr = "f = {j:B[9], r:(e/3)} /* f is a set */"  assertTrue(P.recognize(expr)) } } 
 

Step by Step Solution

There are 3 Steps involved in it

1 Expert Approved Answer
Step: 1 Unlock blur-text-image
Question Has Been Solved by an Expert!

Get step-by-step solutions from verified subject matter experts

Step: 2 Unlock
Step: 3 Unlock

Students Have Also Explored These Related Databases Questions!