initial
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.pdf
|
||||
*.ozf
|
||||
scala_project_2025/src/.scala-build/
|
||||
scala_project_2025/src/.bsp/
|
||||
151
001 - Introduction to Oz/main.oz
Normal file
151
001 - Introduction to Oz/main.oz
Normal file
@@ -0,0 +1,151 @@
|
||||
functor
|
||||
import
|
||||
System
|
||||
Application
|
||||
|
||||
define
|
||||
% Task 1
|
||||
{System.showInfo 'Hello World'}
|
||||
|
||||
% Task 2
|
||||
% Emacs specific
|
||||
|
||||
% Task 3 a
|
||||
local X Y=300 Z=30 in
|
||||
X = Y * Z
|
||||
{System.showInfo X}
|
||||
end
|
||||
% 3 b
|
||||
local X Y in
|
||||
X = "This is a string"
|
||||
thread {System.showInfo Y} end
|
||||
Y = X
|
||||
end
|
||||
% removing the thread keyword results in not printing anything, and the printing occurs after all the other sequential code, and by that time the value has been assigned. The keyword itself implies it is run in another thread, but the behaviour seems to be more akin to a defer, but not running at the end of a scope, but the program. It might be useful if one wants do something specifically when the program is done. The Y = X assigns the value of X to Y, while code using Y is above it is deferred until after Y has been assigned.
|
||||
% the keyword alone would suggest that it is just run in a separate thread and that the only reason the value of Y can be assigned after being used is because it takes time to start the thread.
|
||||
|
||||
% Task 4 a
|
||||
fun {Max X Y}
|
||||
if X > Y then
|
||||
X
|
||||
else
|
||||
Y
|
||||
end
|
||||
end
|
||||
% Task 4 b
|
||||
proc {PrintGreater Number1 Number2}
|
||||
{System.showInfo {Max Number1 Number2}}
|
||||
end
|
||||
{System.showInfo "The greater number in the set {1, 2} is: "}
|
||||
{PrintGreater 1 2}
|
||||
|
||||
% Task 5
|
||||
proc {Circle R} A D C PI = 355.0/113.0 in
|
||||
A = PI * R*R
|
||||
D = 2.0 * R
|
||||
C = D * PI
|
||||
{System.showInfo "Area: "#A}
|
||||
{System.showInfo "Diameter: "#D}
|
||||
{System.showInfo "Circumference: "#C}
|
||||
end
|
||||
{Circle 2.0}
|
||||
|
||||
% Task 6 a
|
||||
fun {Factorial N}
|
||||
if N == 0 then 1 else N * {Factorial N - 1} end
|
||||
end
|
||||
{System.showInfo "The Factorial of 7 is: "#{Factorial 7}}
|
||||
|
||||
% Task 7 a
|
||||
fun {Length List}
|
||||
case List of Head|Tail then
|
||||
1 + {Length Tail}
|
||||
else 0 end
|
||||
end
|
||||
{System.showInfo "The length of the list [1 2 3 4 5] is: "#{Length [1 2 3 4 5]}}
|
||||
|
||||
% 7 b
|
||||
fun {Take Head|Tail Count}
|
||||
if Count > 0 then
|
||||
Head|{Take Tail Count - 1}
|
||||
else nil end
|
||||
end
|
||||
local R = {Take [1 2 3 4 5 6] 4} in
|
||||
{System.showInfo "The taken list: "}
|
||||
{System.show R}
|
||||
|
||||
{System.showInfo "The length of the taken list: "#{Length R}}
|
||||
end
|
||||
|
||||
% 7 c
|
||||
fun {Drop Head|Tail Count}
|
||||
if Count > 1 then
|
||||
{Drop Tail Count - 1}
|
||||
else Tail end
|
||||
end
|
||||
local R = {Drop [1 2 3 4 5 6] 4} in
|
||||
{System.showInfo "The dropped list: "}
|
||||
{System.show R}
|
||||
{System.showInfo "The length of the dropped list: "#{Length R}}
|
||||
end
|
||||
|
||||
% 7 d
|
||||
fun {Reverser List List2}
|
||||
if {Length List} > 0 then
|
||||
{Reverser List.2 List.1|List2}
|
||||
else List2 end
|
||||
end
|
||||
fun {Reverse List}
|
||||
{Reverser List nil}
|
||||
end
|
||||
fun {Appender List1 List2}
|
||||
if {Length List1} > 0 then
|
||||
{Appender List1.2 List1.1|List2}
|
||||
else
|
||||
List2
|
||||
end
|
||||
end
|
||||
fun {Append List1 List2}
|
||||
local List = {Reverse List1} in
|
||||
{Appender List List2}
|
||||
end
|
||||
end
|
||||
{System.show {Append [1 2 3 4 5] [6 7 8 9]}}
|
||||
% 7 e
|
||||
fun {Member List Element}
|
||||
if List == nil then
|
||||
false
|
||||
else if List.1 == Element then
|
||||
true
|
||||
else
|
||||
{Member List.2 Element}
|
||||
end end
|
||||
end
|
||||
{System.show {Member [1 2 3] 3}}
|
||||
|
||||
% 7 f
|
||||
fun {Position List Element} I in
|
||||
if List.1 == Element then
|
||||
1
|
||||
else
|
||||
1 + {Position List.2 Element}
|
||||
end
|
||||
end
|
||||
{System.show {Position [1 2 3] 3}}
|
||||
|
||||
% Task 8 a
|
||||
fun {Push List Element}
|
||||
Element|List
|
||||
end
|
||||
{System.show {Push [1 2 3] 0}}
|
||||
% 8 b
|
||||
fun {Peek List}
|
||||
List.1
|
||||
end
|
||||
{System.show {Peek [1 2 3]}}
|
||||
% 8 c
|
||||
fun {Pop List}
|
||||
List.2
|
||||
end
|
||||
{System.show {Pop [1 2 3]}}
|
||||
end
|
||||
5
001 - Introduction to Oz/run.sh
Executable file
5
001 - Introduction to Oz/run.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
if ozc -c main.oz 2> /dev/null; then
|
||||
ozengine main.ozf && return 0
|
||||
fi
|
||||
ozc -c main.oz
|
||||
62
002 - Introduction to Language Theory/list.oz
Normal file
62
002 - Introduction to Language Theory/list.oz
Normal file
@@ -0,0 +1,62 @@
|
||||
%declasre Length Take Drop Append Member Position in
|
||||
fun {Length List}
|
||||
case List of Head|Tail then
|
||||
1 + {Length Tail}
|
||||
else 0 end
|
||||
end
|
||||
|
||||
fun {Take Head|Tail Count}
|
||||
if Count > 0 then
|
||||
Head|{Take Tail Count - 1}
|
||||
else nil end
|
||||
end
|
||||
fun {Drop Head|Tail Count}
|
||||
if Count > 1 then
|
||||
{Drop Tail Count - 1}
|
||||
else Tail end
|
||||
end
|
||||
fun {Reverser List List2}
|
||||
if {Length List} > 0 then
|
||||
{Reverser List.2 List.1|List2}
|
||||
else List2 end
|
||||
end
|
||||
fun {Reverse List}
|
||||
{Reverser List nil}
|
||||
end
|
||||
fun {Appender List1 List2}
|
||||
if {Length List1} > 0 then
|
||||
{Appender List1.2 List1.1|List2}
|
||||
else
|
||||
List2
|
||||
end
|
||||
end
|
||||
fun {Append List1 List2}
|
||||
local List = {Reverse List1} in
|
||||
{Appender List List2}
|
||||
end
|
||||
end
|
||||
fun {Member List Element}
|
||||
if List == nil then
|
||||
false
|
||||
else if List.1 == Element then
|
||||
true
|
||||
else
|
||||
{Member List.2 Element}
|
||||
end end
|
||||
end
|
||||
fun {Position List Element} I in
|
||||
if List.1 == Element then
|
||||
1
|
||||
else
|
||||
1 + {Position List.2 Element}
|
||||
end
|
||||
end
|
||||
fun {Push List Element}
|
||||
Element|List
|
||||
end
|
||||
fun {Peek List}
|
||||
List.1
|
||||
end
|
||||
fun {Pop List}
|
||||
List.2
|
||||
end
|
||||
90
002 - Introduction to Language Theory/main.oz
Normal file
90
002 - Introduction to Language Theory/main.oz
Normal file
@@ -0,0 +1,90 @@
|
||||
functor
|
||||
import
|
||||
System
|
||||
Application
|
||||
define
|
||||
\insert 'list.oz'
|
||||
% Task 1 a
|
||||
fun {Lex S}
|
||||
{String.tokens S 32}
|
||||
end
|
||||
{System.show {Lex "1 2 + 3 *"}}
|
||||
|
||||
% 1 b
|
||||
fun {Tokenize Head|Tail}
|
||||
if Tail == nil then
|
||||
case Head of nil then nil
|
||||
[] "+" then operator(type:plus)|nil
|
||||
[] "-" then operator(type:minus)|nil
|
||||
[] "*" then operator(type:multiply)|nil
|
||||
[] "/" then operator(type:divide)|nil
|
||||
[] "p" then function(type:print)|nil
|
||||
[] "d" then function(type:duplicate)|nil
|
||||
[] "i" then function(type:negate)|nil
|
||||
[] "c" then function(type:clear)|nil
|
||||
else number({String.toInt Head})|nil
|
||||
end
|
||||
else
|
||||
case Head of nil then nil
|
||||
[] "+" then operator(type:plus)|{Tokenize Tail}
|
||||
[] "-" then operator(type:minus)|{Tokenize Tail}
|
||||
[] "*" then operator(type:multiply)|{Tokenize Tail}
|
||||
[] "/" then operator(type:divide)|{Tokenize Tail}
|
||||
[] "p" then function(type:print)|{Tokenize Tail}
|
||||
[] "d" then function(type:duplicate)|{Tokenize Tail}
|
||||
[] "i" then function(type:negate)|{Tokenize Tail}
|
||||
[] "c" then function(type:clear)|{Tokenize Tail}
|
||||
else number({String.toInt Head})|{Tokenize Tail}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
{System.show {Tokenize {Lex "1 2 + 3 *"}}}
|
||||
|
||||
% 1 c
|
||||
fun {Interpreter Tokens S}
|
||||
if Tokens == nil then
|
||||
S
|
||||
else
|
||||
case Tokens.1 of nil then nil
|
||||
[] number(N) then {Interpreter Tokens.2 N|S}
|
||||
[] operator(type:plus) then {Interpreter Tokens.2 S.1 + S.2.1|S.2.2}
|
||||
[] operator(type:minus) then {Interpreter Tokens.2 S.1 - S.2.1|S.2.2}
|
||||
[] operator(type:multiply) then {Interpreter Tokens.2 S.1 * S.2.1|S.2.2}
|
||||
[] operator(type:divide) then {Interpreter Tokens.2 S.1 / S.2.1|S.2.2}
|
||||
[] function(type:print) then {System.show S} {Interpreter Tokens.2 S}
|
||||
[] function(type:duplicate) then {Interpreter Tokens.2 S.1|S}
|
||||
[] function(type:negate) then {Interpreter Tokens.2 0 - S.1|S.2}
|
||||
[] function(type:clear) then {Interpreter Tokens.2 nil}
|
||||
else nil
|
||||
end
|
||||
end
|
||||
end
|
||||
fun {Interpret Tokens}
|
||||
{Interpreter Tokens nil}
|
||||
end
|
||||
local X = [0 - 13 11] in
|
||||
{System.show X}
|
||||
end
|
||||
local X = {Interpret {Tokenize {Lex "1 2 3 + p"}}} in {System.show X} end
|
||||
local X = {Interpret {Tokenize {Lex "1 2 3 + d"}}} in {System.show X} end
|
||||
local X = {Interpret {Tokenize {Lex "1 2 3 + i"}}} in {System.show X} end
|
||||
local X = {Interpret {Tokenize {Lex "1 2 3 + c"}}} in {System.show X} end
|
||||
|
||||
fun {ExpressionTreeInternal Tokens ExpressionStack}
|
||||
if Tokens == nil then ExpressionStack else
|
||||
case Tokens.1 of nil then ExpressionStack
|
||||
[] operator(type:plus) then {ExpressionTreeInternal Tokens.2 plus(ExpressionStack.1 ExpressionStack.2.1)|ExpressionStack.2.2}
|
||||
[] operator(type:minus) then {ExpressionTreeInternal Tokens.2 minus(ExpressionStack.1 ExpressionStack.2.1)|ExpressionStack.2.2}
|
||||
[] operator(type:multiply) then {ExpressionTreeInternal Tokens.2 multiply(ExpressionStack.1 ExpressionStack.2.1)|ExpressionStack.2.2}
|
||||
[] operator(type:divide) then {ExpressionTreeInternal Tokens.2 divide(ExpressionStack.1 ExpressionStack.2.1)|ExpressionStack.2.2}
|
||||
else
|
||||
{ExpressionTreeInternal Tokens.2 Tokens.1.1|ExpressionStack}
|
||||
end
|
||||
end
|
||||
end
|
||||
fun {ExpressionTree Tokens}
|
||||
{ExpressionTreeInternal Tokens nil}
|
||||
end
|
||||
{System.show {ExpressionTree {Tokenize {Lex "3 10 9 * - 7 +"}}}}
|
||||
end
|
||||
65
002 - Introduction to Language Theory/main.typ
Normal file
65
002 - Introduction to Language Theory/main.typ
Normal file
@@ -0,0 +1,65 @@
|
||||
#import "@preview/simplebnf:0.1.1": *
|
||||
|
||||
= Task 1
|
||||
First i split on whitespace, producing lexemes, then I converte these lexemes into tokens, represented as records. Then I evaluate the expression by adding numbers to a stack until get an operator, which is when i pop 2 tokens from the stack, and use those as arguments, and the result is pushed to the stack again. This continues until the list of tokens is empty, and the stack should contain the result.
|
||||
|
||||
= Task 2
|
||||
|
||||
You start with an empty expression stack and a list of tokens to be converted into a expression stack. The tokens are popped one at a time until the list of tokens is empty. When a token is popped and is a number, it is added to the expression stack. If the popped token is a operator, two tokens are popped from the expression stack and put as the arguments for the function based on the operator. Any expression may be a number or a operator function with two expressions as arguments.
|
||||
|
||||
= Task 3 Theory
|
||||
== a)
|
||||
$ V = {c, o, n, d} $
|
||||
|
||||
$ S = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +, -, \/, \*} $
|
||||
|
||||
$ R = {(c, o), (c, n), (n, d n), (o, +), (o, -), (o, \/), (o, \*), (d, 0), (d, 1), (d, 2), (d, 3), (d, 4), (d, 5), (d, 6), (d, 7), (d, 8), (d, 9)} $
|
||||
|
||||
== b)
|
||||
|
||||
#bnf(
|
||||
Prod(
|
||||
$e$,
|
||||
annot: $sans("")$,
|
||||
{
|
||||
Or[$angle.l o angle.r(angle.l e angle.r, angle.l e angle.r)$][]
|
||||
Or[$angle.l n angle.r$][]
|
||||
},
|
||||
),
|
||||
Prod(
|
||||
$o$,
|
||||
annot: $sans("")$,
|
||||
{
|
||||
Or[plus][]
|
||||
Or[minus][]
|
||||
Or[multiply][]
|
||||
Or[divide][]
|
||||
},
|
||||
),
|
||||
Prod(
|
||||
$n$,
|
||||
annot: $sans("")$,
|
||||
{
|
||||
Or[$angle.l d angle.r angle.l n angle.r$][]
|
||||
},
|
||||
),
|
||||
Prod(
|
||||
$d$,
|
||||
annot: $sans("")$,
|
||||
{
|
||||
Or[$0$][]
|
||||
Or[$1$][]
|
||||
Or[$2$][]
|
||||
Or[$3$][]
|
||||
Or[$4$][]
|
||||
Or[$5$][]
|
||||
Or[$6$][]
|
||||
Or[$7$][]
|
||||
Or[$8$][]
|
||||
Or[$9$][]
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
== c)
|
||||
Both of the grammars are context-free, but only the first one is regular.
|
||||
5
002 - Introduction to Language Theory/run.sh
Executable file
5
002 - Introduction to Language Theory/run.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
if ozc -c main.oz 2> /dev/null; then
|
||||
ozengine main.ozf && return 0
|
||||
fi
|
||||
ozc -c main.oz
|
||||
104
003 - Higher-Order Programming/main.oz
Normal file
104
003 - Higher-Order Programming/main.oz
Normal file
@@ -0,0 +1,104 @@
|
||||
functor
|
||||
import
|
||||
System
|
||||
Application
|
||||
define
|
||||
% Task 1
|
||||
proc {QuadraticEquation A B C ?RealSol ?X1 ?X2}
|
||||
local S = B*B + ~4.0*A*C in
|
||||
if S >= 0.0 then
|
||||
RealSol = true
|
||||
X1 = (~B + {Float.sqrt S})/(2.0 * A)
|
||||
X2 = (B + {Float.sqrt S})/(2.0 * A)
|
||||
else
|
||||
RealSol = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local RealSol X1 X2 in
|
||||
{QuadraticEquation 2.0 1.0 ~1.0 RealSol X1 X2}
|
||||
if RealSol then
|
||||
{System.showInfo "Got real solution"}
|
||||
{System.showInfo "X1: "#X1}
|
||||
{System.showInfo "X2: "#X2}
|
||||
else
|
||||
{System.showInfo "No real solution"}
|
||||
end
|
||||
end
|
||||
local RealSol X1 X2 in
|
||||
{QuadraticEquation 2.0 1.0 2.0 RealSol X1 X2}
|
||||
if RealSol then
|
||||
{System.showInfo "Got real solution"}
|
||||
{System.showInfo "X1: "#X1}
|
||||
{System.showInfo "X2: "#X2}
|
||||
else
|
||||
{System.showInfo "No real solution"}
|
||||
end
|
||||
end
|
||||
|
||||
% Task 2
|
||||
fun {Sum List}
|
||||
if List == nil then
|
||||
0
|
||||
else
|
||||
List.1 + {Sum List.2}
|
||||
end
|
||||
end
|
||||
|
||||
{System.showInfo "Sum of list: "# {Sum [2 3 5 7]}}
|
||||
|
||||
% Task 3
|
||||
fun {RightFold List Op U}
|
||||
if List == nil then
|
||||
U
|
||||
else
|
||||
{Op List.1 {RightFold List.2 Op U}}
|
||||
end
|
||||
end
|
||||
|
||||
{System.showInfo "Mul of list: "# {RightFold [2 3 5] fun {$ X Y} X * Y end 1}}
|
||||
|
||||
fun {NewSum List}
|
||||
{RightFold List fun {$ X Y} X + Y end 0}
|
||||
end
|
||||
fun {NewLength List}
|
||||
{RightFold List fun {$ X Y} 1 + Y end 0}
|
||||
end
|
||||
{System.showInfo "New Sum of list: "# {NewSum [2 3 5 7 11]}}
|
||||
{System.showInfo "New Length of list: "# {NewLength [2 3 5 7 11]}}
|
||||
|
||||
% Task 4
|
||||
fun {Quadratic A B C}
|
||||
fun {$ X} A*X*X + B*X + C end
|
||||
end
|
||||
{System.showInfo {{Quadratic 3 2 1} 2}}
|
||||
|
||||
% Task 5
|
||||
fun {LazyNumberGenerator X}
|
||||
X|fun {$} {LazyNumberGenerator X + 1} end
|
||||
end
|
||||
{System.showInfo {LazyNumberGenerator 0}.1}
|
||||
{System.showInfo {{LazyNumberGenerator 0}.2}.1}
|
||||
{System.showInfo {{{{{{LazyNumberGenerator 0}.2}.2}.2}.2}.2}.1}
|
||||
|
||||
fun {TailRecursiveSumInternal List X}
|
||||
if List == nil then X else
|
||||
{TailRecursiveSumInternal List.2 X + List.1}
|
||||
end
|
||||
end
|
||||
fun {TailRecursiveSum List}
|
||||
{TailRecursiveSumInternal List 0}
|
||||
end
|
||||
|
||||
fun {TailRecursiveLengthInternal List X}
|
||||
if List == nil then X else
|
||||
{TailRecursiveLengthInternal List.2 X + 1}
|
||||
end
|
||||
end
|
||||
fun {TailRecursiveLength List}
|
||||
{TailRecursiveLengthInternal List 0}
|
||||
end
|
||||
{System.showInfo "Tail Recursive Sum of list: "# {TailRecursiveSum [2 3 5 7 11]}}
|
||||
{System.showInfo "Tail Recursive Length of list: "# {TailRecursiveLength [2 3 5 7 11]}}
|
||||
end
|
||||
23
003 - Higher-Order Programming/main.typ
Normal file
23
003 - Higher-Order Programming/main.typ
Normal file
@@ -0,0 +1,23 @@
|
||||
= Task 1
|
||||
== c)
|
||||
Procedural abstractions are useful because useful stuff usually involves I/O, which is a side effect, and thus impure. Procedures allow for side effects and often buils upon pure functions to allow structuring and reuse in programming.
|
||||
== d)
|
||||
A procedure in oz has side effects, and functions do not. This means that if you have the same input to a function in oz, it will always return the same output. This is however not guaranteed for procedures.
|
||||
|
||||
= Task 3
|
||||
== b)
|
||||
If the List is empty it returns the neutral value for the operator, which in the case of plus is 1 and multiplication is 1. Since $a + 0 = a$ and $a times 1 = a$. This also allows the function to return without further recursion and complete execution. If the list is non-empty, it takes the runs the anonymous function Op on the values of the head of the list and the result of the fold function on the list without the first element. If this list now ends up empty it would return the neutral value and the result computed.
|
||||
== e)
|
||||
It would be 1 since $a times 1 = a$
|
||||
|
||||
= Task 4
|
||||
== b)
|
||||
It starts at a certain number, and then it produces a list with the head set to the value and tail to an anonymous function which takes the previous value and adds one to it. This means that by calling the value of the tail n times and getting the value of the head at that point you get the result of starting value plus n
|
||||
|
||||
= Task 6
|
||||
= a)
|
||||
I simply need to use an accumulator and a wrapper function that sets the accumulator to 0.
|
||||
= b)
|
||||
The benefit is that the code can be optimized for a constant stack size for the duration of the function. If it is not tail call and optimized for that in the compiler, many recursive calls will lead to a rapidly growing stack which can lead to stack overflow since the stack size is usually pretty limited. The compiler essentially has to recognize when it does not need to make a new stack frame.
|
||||
= c)
|
||||
It is usually dependent on the compiler and not the language itself, but one example is Java which does not have tail call optimization. This does not mean that it is not possible in Java, but that you are expected to just use loops and an imperative style of programming to avoid the growing stack from recursion. It is usually a matter of programming paradigms, where the functional or declarative programming languages often depend on tail call optimization since the language is designed around functions and recursion, and procedural or imperative programming languages expect you to solve things with procedures and loops instead which does not lead to excessive nested function calling.
|
||||
5
003 - Higher-Order Programming/run.sh
Executable file
5
003 - Higher-Order Programming/run.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
if ozc -c main.oz 2> /dev/null; then
|
||||
ozengine main.ozf && return 0
|
||||
fi
|
||||
ozc -c main.oz
|
||||
46
scala_project_2025/ConcurrencyTroubles.scala
Normal file
46
scala_project_2025/ConcurrencyTroubles.scala
Normal file
@@ -0,0 +1,46 @@
|
||||
package example
|
||||
|
||||
object ConcurrencyTroubles {
|
||||
private var value1: Int = 1000
|
||||
private var value2: Int = 0
|
||||
private var sum: Int = 0
|
||||
|
||||
def moveOneUnit(): Unit = {
|
||||
value1 -= 1
|
||||
value2 += 1
|
||||
if(value1 == 0) {
|
||||
value1 = 1000
|
||||
value2 = 0
|
||||
}
|
||||
}
|
||||
|
||||
def updateSum(): Unit = {
|
||||
sum = value1 + value2
|
||||
}
|
||||
|
||||
def execute(): Unit = {
|
||||
while(true) {
|
||||
moveOneUnit()
|
||||
updateSum()
|
||||
Thread.sleep(50)
|
||||
}
|
||||
}
|
||||
|
||||
// This is the "main" method, the entry point of execution.
|
||||
// It could have been placed in a different file.
|
||||
def main(args: Array[String]): Unit = {
|
||||
for (i <- 1 to 2) {
|
||||
val thread = new Thread {
|
||||
override def run = execute()
|
||||
}
|
||||
thread.start()
|
||||
}
|
||||
|
||||
while(true) {
|
||||
updateSum()
|
||||
println(sum + " [" + value1 + " " + value2 + "]")
|
||||
Thread.sleep(100)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
6
scala_project_2025/bank_system/build.sbt
Normal file
6
scala_project_2025/bank_system/build.sbt
Normal file
@@ -0,0 +1,6 @@
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.0" % "test"
|
||||
|
||||
resolvers += "Central" at "https://central.maven.org/maven2/"
|
||||
|
||||
scalacOptions := Seq("-unchecked", "-deprecation", "-feature", "-language:postfixOps")
|
||||
|
||||
11
scala_project_2025/bank_system/src/main/scala/Account.scala
Normal file
11
scala_project_2025/bank_system/src/main/scala/Account.scala
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
class Account(val code : String, val balance: Double) {
|
||||
|
||||
// TODO
|
||||
// Implement functions. Account should be immutable.
|
||||
// Change return type to the appropriate one
|
||||
def withdraw(amount: Double) : Unit = ???
|
||||
|
||||
def deposit (amount: Double) : Unit = ???
|
||||
|
||||
}
|
||||
65
scala_project_2025/bank_system/src/main/scala/Bank.scala
Normal file
65
scala_project_2025/bank_system/src/main/scala/Bank.scala
Normal file
@@ -0,0 +1,65 @@
|
||||
import collection.mutable.Map
|
||||
|
||||
class Bank(val allowedAttempts: Integer = 3) {
|
||||
|
||||
private val accountsRegistry : Map[String,Account] = Map()
|
||||
|
||||
val transactionsPool: TransactionPool = new TransactionPool()
|
||||
val completedTransactions: TransactionPool = new TransactionPool()
|
||||
|
||||
|
||||
def processing : Boolean = !transactionsPool.isEmpty
|
||||
|
||||
// TODO
|
||||
// Adds a new transaction for the transfer to the transaction pool
|
||||
def transfer(from: String, to: String, amount: Double): Unit = ???
|
||||
|
||||
// TODO
|
||||
// Process the transactions in the transaction pool
|
||||
// The implementation needs to be completed and possibly fixed
|
||||
def processTransactions: Unit = {
|
||||
|
||||
// val workers : List[Thread] = transactionsPool.iterator.toList
|
||||
// .filter(/* select only pending transactions */)
|
||||
// .map(processSingleTransaction)
|
||||
|
||||
// workers.map( element => element.start() )
|
||||
// workers.map( element => element.join() )
|
||||
|
||||
/* TODO: change to select only transactions that succeeded */
|
||||
// val succeded : List[Transaction] = transactionsPool
|
||||
|
||||
/* TODO: change to select only transactions that failed */
|
||||
// val failed : List[Transaction] = transactionsPool
|
||||
|
||||
// succeded.map(/* remove transactions from the transaction pool */)
|
||||
// succeded.map(/* add transactions to the completed transactions queue */)
|
||||
|
||||
//failed.map(t => {
|
||||
/* transactions that failed need to be set as pending again;
|
||||
if the number of retry has exceeded they also need to be removed from
|
||||
the transaction pool and to be added to the queue of completed transactions */
|
||||
//})
|
||||
|
||||
if(!transactionsPool.isEmpty) {
|
||||
processTransactions
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// The function creates a new thread ready to process
|
||||
// the transaction, and returns it as a return value
|
||||
private def processSingleTransaction(t : Transaction) : Thread = ???
|
||||
|
||||
|
||||
// TODO
|
||||
// Creates a new account and returns its code to the user.
|
||||
// The account is stored in the local registry of bank accounts.
|
||||
def createAccount(initialBalance: Double) : String = ???
|
||||
|
||||
|
||||
// TODO
|
||||
// Return information about a certain account based on its code.
|
||||
// Remember to handle the case in which the account does not exist
|
||||
def getAccount(code : String) : Option[Account] = ???
|
||||
}
|
||||
13
scala_project_2025/bank_system/src/main/scala/Main.scala
Normal file
13
scala_project_2025/bank_system/src/main/scala/Main.scala
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
object Main extends App {
|
||||
|
||||
def thread(body: => Unit): Thread = {
|
||||
val t = new Thread {
|
||||
override def run() = body
|
||||
}
|
||||
|
||||
t.start
|
||||
t
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
object TransactionStatus extends Enumeration {
|
||||
val SUCCESS, PENDING, FAILED = Value
|
||||
}
|
||||
|
||||
class TransactionPool {
|
||||
|
||||
// Remove and the transaction from the pool
|
||||
def remove(t: Transaction): Boolean = ???
|
||||
|
||||
// Return whether the queue is empty
|
||||
def isEmpty: Boolean = ???
|
||||
|
||||
// Return the size of the pool
|
||||
def size: Integer = ???
|
||||
|
||||
// Add new element to the back of the queue
|
||||
def add(t: Transaction): Boolean = ???
|
||||
|
||||
// Return an iterator to allow you to iterate over the queue
|
||||
def iterator : Iterator[Transaction] = ???
|
||||
|
||||
}
|
||||
|
||||
class Transaction(val from: String,
|
||||
val to: String,
|
||||
val amount: Double,
|
||||
val retries: Int = 3) {
|
||||
|
||||
private var status: TransactionStatus.Value = TransactionStatus.PENDING
|
||||
private var attempts = 0
|
||||
|
||||
def getStatus() = status
|
||||
|
||||
// TODO: Implement methods that change the status of the transaction
|
||||
|
||||
}
|
||||
206
scala_project_2025/bank_system/src/test/scala/AccountTests.scala
Normal file
206
scala_project_2025/bank_system/src/test/scala/AccountTests.scala
Normal file
@@ -0,0 +1,206 @@
|
||||
import org.scalatest.FunSuite
|
||||
|
||||
class AccountTests extends FunSuite {
|
||||
|
||||
test("Test 01: Account should be immutable") {
|
||||
val account = new Account("1234", 500)
|
||||
val result = account.withdraw(200)
|
||||
assert(account.balance == 500)
|
||||
assert(account.code == "1234")
|
||||
}
|
||||
|
||||
test("Test 02: Valid account withdrawal") {
|
||||
val account = new Account("1234", 500)
|
||||
val result = account.withdraw(200)
|
||||
result match {
|
||||
case Right(x) => assert(x.balance == 300)
|
||||
case Left(x) => assert(false)
|
||||
}
|
||||
}
|
||||
|
||||
test("Test 03: Invalid account withdrawal should return error message") {
|
||||
val account = new Account("1234",500)
|
||||
val result = account.withdraw(1000)
|
||||
assert(result.isLeft)
|
||||
}
|
||||
|
||||
test("Test 04: Withdrawal of negative amount should return error message") {
|
||||
val account = new Account("1234",500)
|
||||
val result = account.withdraw(-100)
|
||||
assert(result.isLeft)
|
||||
}
|
||||
|
||||
test("Test 05: Valid account deposit") {
|
||||
val account = new Account("1234",500)
|
||||
val result = account.deposit(250)
|
||||
assert(result.isRight)
|
||||
assert(result.toOption.get.balance == 750)
|
||||
}
|
||||
|
||||
test("Test 06: Deposit of negative amount should return an error") {
|
||||
val account = new Account("1234",500)
|
||||
val result = account.deposit(-50)
|
||||
assert(result.isLeft)
|
||||
}
|
||||
|
||||
test("Test 07: Correct balance amount after several withdrawals and deposits") {
|
||||
var account = new Account("1234",50000)
|
||||
val lock: Object = new Object
|
||||
|
||||
val first = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
lock.synchronized {
|
||||
account = account.withdraw(10).toOption.get
|
||||
}
|
||||
Thread.sleep(10)
|
||||
}
|
||||
}
|
||||
val second = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
lock.synchronized {
|
||||
account = account.deposit(5).toOption.get
|
||||
}
|
||||
Thread.sleep(20)
|
||||
}
|
||||
}
|
||||
val third = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
lock.synchronized {
|
||||
account = account.withdraw(50).toOption.get
|
||||
}
|
||||
Thread.sleep(10)
|
||||
}
|
||||
}
|
||||
val fourth = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
lock.synchronized {
|
||||
account = account.deposit(100).toOption.get
|
||||
}
|
||||
Thread.sleep(10)
|
||||
}
|
||||
}
|
||||
first.join()
|
||||
second.join()
|
||||
third.join()
|
||||
fourth.join()
|
||||
assert(account.balance == 54500)
|
||||
}
|
||||
}
|
||||
|
||||
class AccountTransferTests extends FunSuite {
|
||||
|
||||
test("Test 08: Valid transfer between accounts") {
|
||||
val bank = new Bank()
|
||||
|
||||
val code1 = bank.createAccount(100)
|
||||
val code2 = bank.createAccount(200)
|
||||
|
||||
bank transfer(code1, code2, 50)
|
||||
bank processTransactions
|
||||
|
||||
while (bank.processing) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
assert(bank.completedTransactions.iterator.toList.last.getStatus == TransactionStatus.SUCCESS)
|
||||
assert(bank.getAccount(code1).get.balance == 50)
|
||||
assert(bank.getAccount(code2).get.balance == 250)
|
||||
}
|
||||
|
||||
test("Test 09: Transfer of negative amount between accounts should fail") {
|
||||
val bank = new Bank()
|
||||
|
||||
val code1 = bank.createAccount(500)
|
||||
val code2 = bank.createAccount(1000)
|
||||
|
||||
bank transfer(code1, code2, -100)
|
||||
bank processTransactions
|
||||
|
||||
while (bank.processing) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
assert(bank.completedTransactions.iterator.toList.last.getStatus == TransactionStatus.FAILED)
|
||||
assert(bank.getAccount(code1).get.balance == 500)
|
||||
assert(bank.getAccount(code2).get.balance == 1000)
|
||||
}
|
||||
|
||||
|
||||
test("Test 10: Invalid transfer between accounts due to insufficient funds should lead to transaction status FAILED and no money should be transferred between accounts") {
|
||||
val bank = new Bank()
|
||||
val code1 = bank.createAccount(100)
|
||||
val code2 = bank.createAccount(1000)
|
||||
|
||||
bank transfer(code1, code2, 150)
|
||||
bank processTransactions
|
||||
|
||||
while (bank.processing) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
assert(bank.completedTransactions.iterator.toList.last.getStatus == TransactionStatus.FAILED)
|
||||
assert(bank.getAccount(code1).get.balance == 100)
|
||||
assert(bank.getAccount(code2).get.balance == 1000)
|
||||
}
|
||||
|
||||
|
||||
test("Test 11: Correct balance amounts after several transfers") {
|
||||
val bank = new Bank()
|
||||
val code1 = bank.createAccount(3000)
|
||||
val code2 = bank.createAccount(5000)
|
||||
|
||||
val first = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
bank transfer(code1, code2, 30)
|
||||
}
|
||||
}
|
||||
val second = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
bank transfer(code2, code1, 23)
|
||||
}
|
||||
}
|
||||
first.join()
|
||||
second.join()
|
||||
|
||||
bank processTransactions
|
||||
|
||||
while (bank.processing) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
assert(bank.getAccount(code1).get.balance == 2300)
|
||||
assert(bank.getAccount(code2).get.balance == 5700)
|
||||
}
|
||||
|
||||
test("Test 12: All the submitted transactions are processed") {
|
||||
val bank = new Bank()
|
||||
val code1 = bank.createAccount(3000)
|
||||
val code2 = bank.createAccount(5000)
|
||||
|
||||
val first = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
bank transfer(code1, code2, 30)
|
||||
}
|
||||
}
|
||||
val second = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
bank transfer(code2, code1, 23)
|
||||
}
|
||||
}
|
||||
first.join()
|
||||
second.join()
|
||||
|
||||
val submitted = bank.transactionsPool.size
|
||||
|
||||
bank processTransactions
|
||||
|
||||
while (bank.processing) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
assert(bank.completedTransactions.size == submitted)
|
||||
assert(bank.transactionsPool.isEmpty)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
74
scala_project_2025/src/Main.scala
Normal file
74
scala_project_2025/src/Main.scala
Normal file
@@ -0,0 +1,74 @@
|
||||
object Hello extends App {
|
||||
// Task 1 a
|
||||
var array: Array[Int] = new Array(50)
|
||||
for i <- 0 to 49 do array(i) = i + 1
|
||||
for e <- array do print(s"$e, ")
|
||||
// Task 1 b
|
||||
def for_sum(x: Array[Int]): Int = {
|
||||
var s = 0
|
||||
for i <- x do s = s + i
|
||||
s
|
||||
}
|
||||
println(for_sum(array))
|
||||
// Task 1 c
|
||||
def inner (x: Array[Int], s: Int, i: Int): Int = {
|
||||
if (i < x.length) {
|
||||
val a: Int = inner(x, s + x(i), i + 1)
|
||||
a
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
def rec_sum (x: Array[Int]): Int = {
|
||||
inner(x, 0, 0)
|
||||
}
|
||||
println(rec_sum(array))
|
||||
// Task 1 d
|
||||
def inner_fib (x: BigInt, p: BigInt, c: BigInt): BigInt = {
|
||||
if (x < 1) {
|
||||
p
|
||||
} else {
|
||||
inner_fib(x - 1, c, c + p)
|
||||
}
|
||||
}
|
||||
def fib (x: BigInt): BigInt = {
|
||||
inner_fib(x - 1, 0, 1)
|
||||
}
|
||||
println(fib(2))
|
||||
// BigInt allows for storing arbitrary sized integers.
|
||||
// This means that it is variably sized memory-wise, and increases in size on-demand.
|
||||
// Int has a constant size and has a fixed range of integers that can be representetd. Since it is a 32 bit signed integer it ranges from -2^31 to 2^31 - 1
|
||||
|
||||
// Task 2 a
|
||||
// Sub Task 1 a
|
||||
def quadratic_equation (a: Int, b: Int, c: Int) : (Boolean, Int, Int) = {
|
||||
val r = b*b - 4*a*c
|
||||
if (r >= 0) {
|
||||
(true, (-b + r)/2*a, (-b + r)/2*a)
|
||||
} else {
|
||||
(false, 0, 0)
|
||||
}
|
||||
}
|
||||
// Sub Task 1 b
|
||||
println(quadratic_equation(2, 1, -1))
|
||||
println(quadratic_equation(2, 1, 2))
|
||||
// Sub Task 1 c
|
||||
// Readability and code reuse.
|
||||
// Sub Task 1 d
|
||||
// Pureness
|
||||
//
|
||||
// Sub Task 4
|
||||
def quadratic (a: Int, b: Int, c: Int) : (Int => Int) = {
|
||||
(x: Int) => a*x*x + b*x + c
|
||||
}
|
||||
println(quadratic(3, 2, 1)(2))
|
||||
// Task 2 b
|
||||
// There is no significant difference
|
||||
|
||||
// Task 3
|
||||
}
|
||||
object ThreadsMain extends App {
|
||||
val t: Thread = Thread.currentThread
|
||||
val name = t.getName
|
||||
println(s"I am the thread $name")
|
||||
}
|
||||
Reference in New Issue
Block a user