Projekt 1

Opis projektu

Zadanie

Zadanie polega na napisaniu funkcji getFlow(), której zadaniem jest analiza podanej funkcji pod kątem wykonywanych obliczeń (computation flow). Jako arguemnt funkcjia getFlow() powinna przyjmować funkcję (obiekt z typem bazowym closure). Jako wynik powinna zwracać obiekt zawierający wszystkich przypisania razem z warunkami ich wykonania. Poniżej jest opis przykładowej implementacji takiej funkcji.

Zaczniemy od prostych przykładów. Popatrzmy na zachowanie się funkcji getFlow() na bardzo prostej funkcji wykonującej tylko jedno przypisanie.

### Definicja funkcji
f <- function(a) {
    x <- a + rnorm(1)
    x
}

fFlow <- getFlow(f)
fFlow

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

Funkcja zwróciła obiekt zawierając listę przypisań. W przykładowej implementacji lista przypisań to obiekt klasy assignmentList, który jest listą obiektów klasy assignment. Dla obu klasy dopisane są metody print a dla klasy assignmentList dodatkowo metoda c. W przykładowej implementacji do stosowania metod (dispatching) został wykorzystany model obiektowy S3. W uzyskanej liście mamy jedno przypisanie, które jest wykonywane bezwarunkowo.

Zobaczmy teraz nico bardziej skomplikowny przykład.

### Definicja funkcji
f <- function(a, b) {
    b <- abs(b)
    x <- a ^ b
    x
}

fFlow <- getFlow(f)
fFlow

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

Dla powyższej funkcji flow obliczeń jest również bardzo prostych, składa się jedynie z dwóch przypisań, gdzie każde z przypisań jest dokonywane bezwarunkowo.

Popatrzmy następnie na zachowanie się funkcji w przypadku występowania wykonań warunkowych.

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

fFlow <- getFlow(f)
fFlow

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

Zachowanie funkcji jest tutaj bardziej skomplikowane. Mamy co prawda tylko jedno przypisanie ale jest to przypisanie warunkowe, jeżeli nie zachodzi warunek z <= 0 (zapisywane jako not z <= 0) to wtedy wykonywane jest przypisanie.

Zobaczmy jeszcze bardzoej skomplikowany flow obliczeń.

### Definicja funkcji
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
    }
    y
}

fFlow <- getFlow(f)
fFlow

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

W powyższym przykładzie flow obliczeń jest zdecydowanie bardziej skomplikowny. Pierwsze przypisanie jest bezwarunkowe ale wszystkie kolejne przypisania są wykonywane warunkowo. Przypisania te znajdują się w różnych gałęziach kodu i wykonywane co jest widoczne w warunkach wykonania przypisania. Drugie przypisanie jest wykonywane w pierwszej gałęzi kodu (pierwszy poziom wykonania warunkowego). Kolejne dwa mają bardziej złożone warunki bo znajdują się na drugim poziomie wykonania warunkowe. Jest to odzwierciedlone w warunku złożonym z dwóch elementów powiązanych spójnikiem and. Ostatnie dwa przypisania są wykonywane w gałęziach kodu umieszczonych w bloku else co jest wskazane w warunkach przez poprzedzenie warunku przez not.

Tak napisaną funkcję można oczywiście zastosować do dowolnych funkcji, tak jak poniżej.

### Proste zastosowanie
getFlow(setOldClass)

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

Jak widać jest tutaj znacznie więcej przypisań i są one również znacznie bardziej skomplikowane. Takie przedstawienie kodu daje relatywnie dobry pogląd na flow obliczeń w ramach danej funkcji.

Przedstawiona przykładowa implementacja ma kilka ograniczeń. Jedno z tych ograniczeń to brak analizy warunku pod kątem występowania tam przypisania. Widać to w powyższych wynikach, gdzie jeden z warunków zawiera przypisanie. W rozwiązaniu zadania również można pominąć ten przypadek.

Techniczne warunki zaliczenia

Zadanie powinno być rozwiązane w pojedynczym skrypcie w pliku R. Rozwiązanie powinno zawierać odpowiednie definicje oraz przykładowe wykorzystanie (podobne do tych pokazanych poniżej, mogą być identyczne) zaproponowanych rozwiązań. Rozwiązania bez przykładów nie będą zaliczane. W rozwiązaniu nie wolno wykorzystywać dodatkowych pakietów poza pakietem stringr jeżeli będzie taka konieczność. Proszę nie używać polskich znaków.

Data: 2021-05-01 Sat 00:00

Autor: Michał Ramsza

Created: 2021-05-02 Sun 18:41

Validate