Project 1

Project description


The main task of the project is to write a function getFlow(). The purpose of this function is to analyze a body of a function given as an argument. The analysis is concerned with assignments done within the body of an argument function. Technically, the getFlow() function takes a function as an argument (object with a base type closure) and returns an object containing a list of all assignments together with conditions (resulting from if / else phrases). Below are usage examples of my simple implementation of this idea.

I start with a simple example. Let's see how the getFlow() deals with a function containing a single unconditional assignment.

### Function definition
f <- function(a) {
    x <- a + rnorm(1)

fFlow <- getFlow(f)

x  <-  a + rnorm(1)  ||| TRUE

The getFlow() returns an object containing a list of assignments with conditions attached. In my implementation, the list of assignments is an instance of a new class assignmentList, and each element of this list is an instance of another new class assignment. For both classes, I implemented print methods and c methods for convenience. In the above example, the list contains a single element: an assignment with a condition TRUE indicating that this is an unconditional assignment.

Let's see another slightly more complicated example.

### Function definition
f <- function(a, b) {
    b <- abs(b)
    x <- a ^ b

fFlow <- getFlow(f)

b  <-  abs(b)  ||| TRUE
x  <-  a^b     ||| TRUE

The computation flow here is also very simple for the above function as it comprises only two unconditional assignments.

Next, let's see how the getFlow() works for functions containing conditional statements.

### Function definition
f <- function(z, b) {
    if( z <= 0) {
        stop("Only positive values of z are allowed.", call. = FALSE)
    } else {
        x <- z ^ b

fFlow <- getFlow(f)

x  <-  z^b  ||| not z <= 0  and TRUE

The behavior of the functions is a little bit more complicated. There is still only one assignment. It is, however, a more complicated one as this time it contains a condition resulting from the else clause. Such conditions are marked with a not keyword.

Let's see an even more complicated example with multiple conditional statements.

### Function definition
f <- function(a, b) {
    x <- rnorm(1)
    if(a < 0)
        a <- abs(a)
        if(b == 2L)
            y <- a ^ 2
            y <- y + x
    } else
        y <- a ^ b
        y <- y + x

fFlow <- getFlow(f)

x  <-  rnorm(1)  ||| TRUE
a  <-  abs(a)    ||| a < 0  and TRUE
y  <-  a^2       ||| a < 0  and b == 2L  and TRUE
y  <-  y + x     ||| a < 0  and b == 2L  and TRUE
y  <-  a^b       ||| not a < 0  and TRUE
y  <-  y + x     ||| not a < 0  and TRUE

As expected, the flow of computations is a notch more complicated here. The first assignment is unconditional. The second one contains a simple condition resulting from the first level of the if clause. The two following assignments have compound conditions resulting from two if clauses. The last two assignments have negated conditions of the first if clause resulting from the else statement.

I can apply the getFlow() function to any function. An example is below.

### Simple application

simpleCase  <-  is.null(prototype)                            ||| TRUE
mainClass  <-  Classes[[1L]]                                  ||| TRUE
prevDef  <-  getClassDef(mainClass, where, inherits = FALSE)  ||| TRUE
clName  <-  S4Class                                           ||| !missing(S4Class)  and !is(S4Class, "classRepresentation")  and is.character(S4Class)  and TRUE
S4Class  <-  getClass(S4Class)                                ||| !missing(S4Class)  and !is(S4Class, "classRepresentation")  and is.character(S4Class)  and TRUE
S4prototype  <-  S4Class@prototype                            ||| !missing(S4Class)  and !is.null(prototype)  and TRUE
S4Class@prototype  <-  .mergeAttrs(prototype, S4prototype)    ||| !missing(S4Class)  and !is.null(prototype)  and TRUE
prevClass  <-  "oldClass"                                     ||| TRUE
S3Class  <-  character()                                      ||| TRUE
S3table  <-  new.env()                                        ||| is.null(S3table <- where$.S3MethodsClasses)  and TRUE
dataPartClass  <-  NULL                                       ||| TRUE
S3Class  <-  c(cl, S3Class)                                   ||| TRUE
def  <-  getClass(cl, where)                                  ||| isClass(cl, where)  and TRUE
cl1  <-  .validDataPartClass(cl, where, dataPartClass)        ||| isClass(cl, where)  and !extends(def, prevClass)  and TRUE
prevP  <-  def@prototype                                      ||| isClass(cl, where)  and not !extends(def, prevClass)  and TRUE
prevS3Class  <-  attr(prevP, ".S3Class")                      ||| isClass(cl, where)  and not !extends(def, prevClass)  and TRUE
useP  <-  TRUE                                                ||| not isClass(cl, where)  and TRUE
useP  <-  FALSE                                               ||| TRUE
def  <-  getClassDef(cl, where)                               ||| not isClass(cl, where)  and TRUE
attr(clp, ".S3Class")  <-  S3Class                            ||| not isClass(cl, where)  and TRUE
def@prototype  <-  .notS4(clp)                                ||| not isClass(cl, where)  and TRUE
prevClass  <-  cl                                             ||| TRUE

There are many more assignments here, and the conditions are also more complicated, resulting from many nested if / else statements. However, this way of looking at the code tells more about the computation flow and inherently about the code's complexity.

My implementation of the getFlow() function has several limitations. One of those limitations is the lack of analysis of the condition statement in the if clause. You can observe this in the above example, where one of the conditions contains an assignment. While doing the project, you can also ignore this case.

Technical conditions

The project should be solved in a single R file. The file should contain the solution (implementation) and examples of use (you can use the above examples). Solutions without examples will not be excepted. You are not allowed to use any additional packages except for stringr, if necessary. Please, do not use polish diacritics.

Date: 2021-05-01 Sat 00:00

Author: Michał Ramsza

Created: 2021-05-03 Mon 18:22