CS 331 Spring 2025 > Exam Review Problems, Set C
CS 331 Spring 2025
Exam Review Problems, Set C
This is the third set of exam review problems.
Problems
Review problems are given below. Answers are in the Answers section of this document. Do not turn these in.
Answers
- Egbert is wrong,
because, even if a grammar is not LL(k),
we can often convert it to a grammar that is LL(k)
that generates the same language
and specifies much the same structure.
In that case, we can convert the grammar to a usable one
and then write our parser based on that grammar instead.
(This is exactly what we did in class with the expression grammar
that ended up giving us
rdparser3.lua
.) -
- In a Predictive Recursive-Descent parser, if we call a parsing function, and it indicates a syntax error, then we must immediately return, flagging a syntax error.
- The above is true because to do otherwise would require backtracking, and a Predictive Recursive-Descent parser does not backtrack.
-
- An abstract syntax tree (AST) is a way of representing the structure of input. In an AST, each internal node represents an operation (defined broadly). The node’s subtrees are the ASTs for the entities the operation is applied to.
- ASTs are commonly used as the output of programming-language parsers—in particular, the parsers that form the first step in the process of compiling a program.
- The 4 actions that a shift-reduce automaton
can perform at each step in its execution are as follows.
- SHIFT. Push the current input symbol onto the stack, along with a specified state, and advance the input to the next symbol.
- REDUCE. Run a production backward, popping its right-hand side from the stack, and pushing its left-hand side, along with a state obtained from a goto table lookup.
- ACCEPT. Terminate, indicating a successful parse (input is syntactically correct).
- ERROR. Terminate, indicating an unsuccessful parse (input contains a syntax error).
-
- Practical lexers and parsers run in linear time.
- False. Known practical parsing methods cannot handle all CFLs.
- The worst-case runtime for a typical parsing method that can handle all CFLs is cubic time [O(n3)].
- A shotgun parser is a parser that emits code directly, rather than producing an AST and allowing another code module to produce code.
-
- Manifest typing is when the type of an entity is explicitly stated in source code.
- An extensible type system is one in which programs can define new types.
- A sound type system is one that does static typing and guarantees that operations that are incorrect for a type will not be performed.
- The problem with the term strong typing is that it has no standard definition; it is used by different people in different ways (and sometimes by the same person in different ways).
-
- Haskell does static typing.
- Haskell’s typing is mostly implicit, but type annotations are allowed.
- Haskell’s type system is extensible; programs may defined new types.
- Variables do have types in Haskell.
-
- False. Haskell is oriented primarily toward declarative programming.
- Haskell functions are not introduced by a keyword.
- True. Haskell has first-class functions.
- True. Haskell allows for lambda functions.
- True. By default, Haskell does lazy evaluation.
- We can do evaluation that is not lazy by using Haskell’s
seq
primitive.
-
- True. Ordinary Haskell variables declared inside a function are always local variables.
- In Haskell, we import a module using the
import
keyword.
-
- False. Haskell has no loops at all.
- Haskell requires its implementations to do tail-call optimization (TCO).
- Lazy evaluation means that an expression is only evaluated when its value is needed.
-
- Cons is an operation that constructs a list from its first item and a list of all its other items.
- We write cons in Haskell as a colon (
:
).
-
- A predicate is a function that returns a Boolean value.
- The filter operation takes a predicate and a list. It returns a list of all items in the original list for which the predicate returns true.
- True. Every map or filter operation can be replaced by a list comprehension.
- The zip operation takes two lists. It returns a single list of pairs, each pair containing one item from each of the original lists.
-
- A Haskell typeclass is a collection of types that all implement some particular interface.
- The Haskell function
show
(lower-cases
) converts a value to a string. This function is overloaded using typeclassShow
(upper-caseS
). - The Haskell function
read
(lower-caser
) converts a string to some type of value. This function is overloaded using typeclassRead
(upper-caseR
).
-
- A Haskell I/O action is a representation of some I/O to perform. It holds a description of a sequence of zero or more side effects, along with a wrapped value.
- When we combine multiple Haskell I/O actions into a single I/O action, the resulting I/O action contains descriptions of all the side effects from the original I/O actions, along with the value wrapped by the last one.
- We use the
>>
and>>=
operators to combine I/O actions into a single I/O action. - Haskell’s do-expression construct is more convenient than using these operators.
-
- Haskells
return
inside an I/O do-expression creates an I/O action describing no side effects. - False. Execution of a Haskell
return
does not cause an immediate exit of the function it is in.
- Haskells
- Haskell has two constructions that allow for binding an identifier to a value inside a do-expression.
<-
Binds an identifier to the value wrapped by an I/O action. For example, “line <- getLine
” binds the nameline
to the string wrapped by the I/O action returned bygetLine
.let
...=
Binds an identifier to a non-wrapped value. For example, “let a = b+c
” binds the namea
to the result of addingb
andc
.
-
- The primary Haskell keyword used to create a new type is
data
. - The Haskell keyword
instance
is used to declare that a type lies in some particular typeclass.
- The primary Haskell keyword used to create a new type is
-
- Haskell’s
Maybe
creates a new type that is much the same as an existing type, but contains one new value. Values in the existing type are marked withJust
. The new value isNothing
. - Haskell’s
Either
creates a new type that can take on values from either of two existing types. Values in the first of the two existing types are marked withLeft
. Values in the second are marked withRight
.
- Haskell’s