Recursion Flashcards

1
Q

Do you need to declare the type of a variable in a functional programming language like Scala?

A

No as Scala has type inference, so its rarely necessary to declare the type of a variable.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

What is a pure function?

A

Pure functions are functions that have no side effects. This can have some good benefits for execution purposes:

  • Any unused expressions can be removed
  • If the arguments have no side effects, the function will return the same result as the previous time (referential integrity)
  • If there’s no dependency between two pure function calls, they can be done in either order or in parallel concurrently
  • The compiler can then reorder the program execution to optimize efficiency

Not all functions can be pure such as I/O functions

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Lazy Evaluation - Why do it?

A

Lazy evaluation means that the expressions are only evaluated when they are required. The expression Func(34), 34 would only be passed to the function and evaluated when it is used.

It can improve efficiency and provide a useful way of implementing non-terminating sequences

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Recursion & Tail-Recursion

A

In Scala, if you are using recursion you must specify the return type.

Recursion is not without its problems. Every new iteration is essentially a new function call, a new stack frame gets pushed for each step. This can make the stack grow unnecessarily large and make some calculations fail for a lack of resources. (Causing stack overflow)

Tail recursion prevents stack overflow by having the last instruction in a function be the recursive call. This means that you can re-use the same stack frame and ultimately prevents stack overflow from happening.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Is it possible to re-write non-tail recursive algorithms?

A

Yes, you can do that to make it tail recursive. Ideally, we don’t want to have to change the function definition but instead introduce a nested function within that function as the tail recursive method

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

What is the difference between pass by value and pass by name in Scala?

A

Pass by value parameters are calculated on the call of function whereas pass by name parameters are calculated on each use of the parameters.

Pass by value parameters are really a copy of the value passed to the function whereas pass by name may never be calculated if the parameter is never used.

Passing by name uses the => symbol (Known as the Fat arrow or the Rocket) and pass by value doesn’t have it.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

What is currying?

A

Currying in essence is where a function that takes multiple arguments can be re-written as a composite of functions that take single arguments. We can’t call the curried function with the arguments presented as a list and instead what is happening the call of the function with the first argument, then composing the result of that with the result of calling the function again on the second one.

Example:

def mult(a: Int, b: Int) = a * b
mult: (a: Int, b: Int) Int
mult(3, 4)
res30: Int = 12
def curryMult(a: Int)(b: Int) = a * b
curryMult: (a: Int)(b: Int) Int //Note that the a and b are wrapped as their own individual single parameter functions as curried functions can’t take multiple parameters when specified like ()()

Curried methods can be used to define new control structures i.e. resource management - a try with resources, a synchronised block or working with a database connection.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

What is meant by a partial function in Scala?

A

Partially defined functions are functions that are not defined over an entire domain. This is not the same as partially applied functions. PartialFunction is a subtype of Function1 but has two additional methods

  1. def isDefinedAt(a: A) Boolean
  2. orElse(that: PartialFunction[A,B]): PartialFunction[A,B]

To define a partial function, we can use cases or we can provide implementations for the methods for isDefineAt, orElse and importantly the apply method which gives the appearance of a function call on an object so we can use that to do the work for us

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Can you Map as Partial Functions?

A

Yes, we can view some collections as functions like in sequence maps a key onto some other value. A map does the same thing but doesn’t restrict the domain to a specific type like Int. However, the mapping doesn’t need to be defined for all values in the type of the key, so we can treat a map or a Seq as a partial function.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

What do exceptions look like in Scala?

A

Since Scala runs on the JVM, it can take advantage of Java’s existing exception syntax with a functional twist. Scala unlike Java does NOT support checked exceptions and as such all exceptions are runtime exceptions. Exception handling is achieved through a variation of pattern matching.

try {
val i: Int = “123”.toInt
} catch {
case e: NumberFormatException => println (“oops”)
}

Whilst this is fine it is a bit clumsy and can create lots of try’s and catches, as well as making it difficult to multi-thread code as the try and catch code is by definition executed on the same thread. So it can be difficult to deal with a situation where a call is dispatched on a separate thread, often transparently to the caller.

The try catch approach doesn’t fit well with the functional approach that is encouraged by Scala in that exceptions are essentially a mutable state that makes it hard to reason about the code.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Try Catch As An Expression

A

The try and catch block returns a value in Scala that can be assigned and used further in the program. If we had tried to force things to be a specific type like int, then the code wouldn’t always compile as the value returned from the failure path may not always be int.

Retaining type information, the failure path needs to be able to return a value of the same type as the success path. This could be 0 for success and 1 for failure just as an example. However, now the caller needs to figure out if the result value is a value returned due to an exception or a value returned in an expression that worked inside the try block.

To fix this, we can use the Option[T] type in scala which is used to represent success or failure. Some(t: T) means success and None means failure. These are very much like Optionals in Java or Nullables in C#

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

The either type

A

Either[A,B] is a union type that contains the instance of A or the instance of B. This can also be used in exception handling. There are two subtypes, Left and Right. If the value is of type A then the Either will contain Left otherwise will be Right.

The compiler ensures this rule is followed. Values of Either type can be examined using pattern matching. In use for exceptions, Left should be for the exception and the right should be for the success value. This allows you to access the Exception object.

Either for exceptions doesn’t support map or flatmap directly but it can work if you project to left or right before hand. Either[A, B] is an unbiased type where the use of Left is to store the exceptions details by convention.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Try[T]

A

In Scala 2.10, the Union Type Try[T] was added and it has two subclasses: Success[T] and Failure[T] where Success[T] contains the value and Failure[T] contains the throwable. Try[T] is a monad type and does support maps and flatmaps as well as any other higher order functions.

It can offer implementations of the combinator functions map, foreach, flatMap, filter etc. that don’t form part of Either[A,B]. This makes it easier to build more sophisticated compositions of functions based on this type

The flatMap makes it possible to do Try[T] in for comprehensions as the behaviour is to pass on whatever the result was seen in the previous stage of the pipeline processing it further if it was a Success or simply passing it on without processing if it was a Failure

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Try[T] Recovering from exceptions

A

Try[T] supports the recover method which returns ‘this’ if it was a Success and apply function on ‘this’ if it was a Failure. By executing the function, it allows it to recover from an exception

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

What are implicit definitions?

A

Scala enables us to have definitions inserted by the compiler when necessary including methods, method parameters and more recently classes. This is useful for converting one type to another, flexible default values for parameters and adding further mechanisms for bounding type parameters.

Something that is implicit can be defined as such within objects, classes, package objects etc. but can’t be defined at the top level in the code.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Type Conversion Using Implicit

A

implicit def strToInt(s: String) = s.toInt
math.min(“2”, 3)
res2: Int = 2 where math.min returns the lowest value of the parameters passed in.

With implicit the conversion happens automatically, you don’t need to specify that you want to convert that parameter as it just goes looking for the implicit scope for the function with the correct function signature to match what it wants. The implicit scope includes the normal scope plus companion objects of all types that may be relevant to the function

17
Q

View Bounds

A

Implicit type conversions lead to a further mechanism for qualifying type parameters. If you have

class MyContainer[A <% Int] then what that means is that Parameter A must be viewable (be able to convert to) an Int.

18
Q

What’s left to cover

A

Implicit classes, parameters, type classes, and context bounds

19
Q

Implicit Classes

A

Allow us to have extension methods and encapsulates wrapper class and implicit conversion definitions - only one class parameter is allowed and implicit conversion from the class parameter type is provided.

20
Q

Implicit parameters

A

Is an alternative way of specifying default parameters by defining the parameter as implicit. A method defined using the curried notation can have its last argument defined as implicit. If the method is called with all arguments supplied then nothing changes. However, if the last argument is omitted then the compiler searches the implicit scope for a value of the correct type and uses that. Whilst thats similar to the idea of a default arg value, the advantage is that the default can be changed without changing the function definition itself. If an implicit parameter is given a default value, it can still be used with no other default in the implicit scope

21
Q

Implicits and Type classes

A

Is a way of encapsulating required behaviour as a type i.e. generation of XML for a value.

22
Q

Context Bounds for Types

A

Improvement on View Bounds and requires the type to implement a type class. ‘has’ functionality rather than ‘can be viewed as’