This commit is contained in:
2025-10-16 16:03:00 +02:00
commit 33179bdf58
18 changed files with 971 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.pdf
*.ozf
scala_project_2025/src/.scala-build/
scala_project_2025/src/.bsp/

View 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

View 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

View 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

View 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

View 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.

View 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

View 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

View 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.

View 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

View 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)
}
}
}

View 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")

View 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 = ???
}

View 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] = ???
}

View 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
}
}

View File

@@ -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
}

View 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)
}
}

View 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")
}