1. Εισαγωγή στο TDD
Test Driven
Development
Δομημένος Προγραμματισμός
Τσαγκατάκης Ιωάννης
2. 2
Πως λύνουμε ένα πρόβλημα ;
Σχεδιασμός
Αλγορίθμου
Κώδικας
(υλοποίηση)
Profit !
3. 3
Άσκηση 4
Γράψτε ένα πρόγραμμα το οποίο θα βρίσκει και θα εμφανίζει στη
οθόνη όλους τους τετραψήφιους αριθμούς που είναι ίσοι με το
άθροισμα των ψηφίων τους στη τέταρτη δύναμη.
Α Β C D
0...9
1...9
Ένα κόμμα κάνει την διαφορά
#include <math.h>
int a, b, c, d, x;
int sum1 = pow(a, 4) + pow(b, 4) +
pow(c, 4) + pow(d, 4);
int sum2 = pow(a + b + c + d, 4);
4. 4
undefined reference to `pow'
●
Χρειάζεται την βιβλιοθήκη libm
gcc -Wall -Wconversion -o ex4 -lm ex4.c
●
cmake
cmake_minimum_required(VERSION 3.15 )
project(Ergasia1 C)
add_compile_options(-Wall -Wextra -Wconversion -Wpedantic)
add_executable(ex1 ex1.c)
add_executable(ex4 ex4.c)
target_link_libraries(ex4 m)
5. 5
undefined reference to `pow'
●
Περιφραστικά
●
Συνάρτηση
int sum3= a*a*a*a + b*b*b*b + c*c*c*c + d*d*d*d;
int pow4(int num) {
int result = num * num * num * num;
return result;
}
6. 6
Μια απλή λύση
int main() {
for (int a = 1; a <= 9; a++) {
for (int b = 0; b <= 9; b++) {
for (int c = 0; c <= 9; c++) {
for (int d = 0; d <= 9; d++) {
// Horner method
int x = (((a * 10) + b) * 10 + c) * 10 + d;
int sum = pow(a, 4) + pow(b, 4) + pow(c, 4) + pow(d, 4);
if (sum == x) {
printf("Solution found %dn", x);
}
}
}
}
}
}
Solution found 1634
Solution found 8208
Solution found 9474
Είναι σωστά;
7. 7
Μια (αυστηρή) κριτική της λύσης
●
Δουλεύει μόνο για 4ψήφιους αριθμούς
– Αν αύριο μας ζητήσουν για 5-ψήφιους αριθμούς;
●
Κάνει πολλά πράγματα μπλεγμένα μεταξύ τους
– Παραγωγή 4-ψήφιων αριθμών
– Έλεγχος της συνθήκης φιλτραρίσματος. Αν αύριο αλλάξει;
●
Πάντα αναζητούμε ποιο γενικές λύσεις
●
Αυτά δεν είναι προβλήματα στο επίπεδο που είμαστε,
αλλά καλό να το έχουμε υπόψιν μας
8. 8
Μια συνήθης λύση
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
int main() {
bool isFound = false;
int a, b, c, d;
for (int x = 1000; x <= 9999; x++) {
a = 0;
b = 0;
c = 0;
d = 0;
int sum = pow(a, 4) + pow(b, 4) + pow(c, 4) + pow(d, 4);
if (sum == x) {
printf("Solution found %dn", x);
isFound = true;
}
}
if (isFound == false) {
puts("No solutions found");
}
}
9. 9
Ο λογικός τύπος bool
// Old C
typedef int bool;
#define true 1
#define false 0
// C99
#include <stdbool.h>
10. 10
Μια συνήθης λύση
(μικρά προβλήματα)
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
int main() {
bool isFound = false;
int a, b, c, d;
for (int x = 1000; x <= 9999; x++) {
a = 0;
b = 0;
c = 0;
d = 0;
double sum = pow(a, 4) + pow(b, 4) + pow(c, 4) + pow(d, 4);
if (sum == x) {
printf("Solution found %dn", x);
isFound = true;
}
}
if (isFound == false) {
puts("No solutions found");
}
}
warning: conversion to ‘int’ from ‘double’ may
alter its value [-Wfloat-conversion]
Αυτά θέλουν σκέψη,
ας το καθυστερήσουμε λιγάκι
Έτσι θα ξέρω και πως αυτό θα δουλέψει !
11. 11
Test Driven Development
●
Σπάω το πρόβλημα
σε μικρότερα και
απλούστερα
●
Βεβαιώνομε πως
κάθε τι είναι σωστό
●
Ο αλγόριθμος θα
προκύψει
12. 12
Τα assertions
●
Το TDD του φτωχού
●
Μια συνθήκη που αν δεν είναι αληθής
– Σε Release mode δεν υπάρχουν
●
δεν θα κάνουν τίποτα, ούτε θα παραχθεί κώδικας
– Σε Debug mode
●
Θα σταματήσουν βίαια την εκτέλεση του προγράμματος
●
Με ένα Debugger θα δούμε την λάθος τιμή.
13. 13
Εισαγωγή στα assertions
#include <assert.h>
int main() {
int x = 4712;
int a, b, c, d;
a = 0;
b = 0;
c = 0;
d = 0;
assert(a == 4);
assert(b == 7);
assert(c == 1);
assert(d == 2);
puts("All assertions passed !");
}
main: Assertion `a == 4' failed.
Ας το φτιάξουμε
14. 14
Εισαγωγή στα assertions
#include <assert.h>
int main() {
int x = 4712;
int a, b, c, d;
a = 4;
b = 7;
c = 1;
d = 2;
assert(a == 4);
assert(b == 7);
assert(c == 1);
assert(d == 2);
puts("All assertions passed !");
}
Ας το φτιάξουμε
στα αλήθεια
15. 15
Βήμα 2: Assertion pass
●
Ο κώδικας δουλεύει
●
Υπάρχει κάποια κρυμμένη δομή;
●
Μπορεί να γίνει ποιο γενικός;
int x = 4712;
int d = x % 10;
int a = x / 1000;
int b = (x / 100) % 10;
int c = (x / 10) % 10;
assert(a == 4);
assert(b == 7);
assert(c == 1);
assert(d == 2); int a = x / 1000; // 3
int b = (x / 100) % 10; // 2
int c = (x / 10) % 10; // 1
int d = x % 10; // 0
16. 16
Αναζητώντας ένα γενικό τύπο
●
Ο κώδικας δουλεύει
●
Υπάρχει κάποια κρυμμένη δομή;
●
Μπορεί να γίνει ποιο γενικός;
int a = x / 1000; // 3
int b = (x / 100) % 10; // 2
int c = (x / 10) % 10; // 1
int d = x % 10; // 0
// 1st pass
int a = (x / 1000) % 10; // 3
int b = (x / 100) % 10; // 2
int c = (x / 10) % 10; // 1
int d = (x / 1) % 10; // 0
// 2nd pass
int a = (x / 10^3) % 10; // 3
int b = (x / 10^2) % 10; // 2
int c = (x / 10^1) % 10; // 1
int d = (x / 10^0) % 10; // 0
17. 17
Βήμα 3 : Refactoring
int ipow(int num, int to) {
double result = pow(num, to);
return (int)result;
}
int main() {
int x = 4712;
int a = (x / ipow(10, 3)) % 10; // 3
int b = (x / ipow(10, 2)) % 10; // 2
int c = (x / ipow(10, 1)) % 10; // 1
int d = (x / ipow(10, 0)) % 10; // 0
assert(a == 4); assert(b == 7);
assert(c == 1); assert(d == 2);
int sum = ipow(a, 4) + ipow(b, 4) + ipow(c, 4) + ipow(d, 4);
assert(sum == pow(4, 4) + pow(7, 4) + pow(1, 4) + pow(2, 4)); // 2674
puts("All assertions passed !");
}
18. 18
Βήμα 3.1 : Refactoring with for
int ipow(int num, int to) {
double result = pow(num, to);
return (int)result;
}
int main() {
int x = 4712;
int digits = 4;
int sum = 0;
for(int i=0; i< digits; i++) {
int digit = (x / ipow(10, i)) % 10;
sum = sum + ipow(digit,4);
}
assert(sum == pow(4, 4) + pow(7, 4) + pow(1, 4) + pow(2, 4)); // 2674
puts("All assertions passed !");
}
19. 19
Βήμα 3.2 : Refactoring no doubles
int ipow(int num, int to) {
int result = 1;
for (int i = 0; i < to; i++)
result *= num;
return result;
}
int main() {
int x = 4712;
int digits = 4;
int sum = 0;
for(int i=0; i< digits; i++) {
int digit = (x / ipow(10, i)) % 10;
sum += ipow(digit,4);
}
assert(sum == pow(4, 4) + pow(7, 4) + pow(1, 4) + pow(2, 4)); // 2674
puts("All assertions passed !");
}
20. 20
Βήμα 3.3
int ipow(int num, int to) {
int result = 1;
for (int i = 0; i < to; i++)
result *= num;
return result;
}
int main() {
int x = 4712;
int digits = 4;
int sum = 0;
for(int i=0; i< digits; i++) {
int digit = (x / ipow(10, i)) % 10;
sum += ipow(digit,4);
}
assert(sum == pow(4, 4) + pow(7, 4) + pow(1, 4) + pow(2, 4)); // 2674
puts("All assertions passed !");
}
Χωρίς
αυτό;
21. 21
Βήμα 3.3 : Refactoring do..while
int main() {
int x = 4712;
...
int sum2 = 0;
int num = x;
do {
int digit = num % 10;
sum2 = sum2 + ipow(digit, 4);
num = num / 10;
} while ( num != 0 );
assert(sum2==sum);
...
}
ΟΚ
22. 22
Βήμα 3.4
int main() {
int x = 4712;
...
int sum2 = 0;
int num = x;
do {
int digit = num % 10;
sum2 = sum2 + ipow(digit, 4);
num = num / 10;
} while ( num != 0 );
assert(sum2==sum);
...
}
Συνάρτηση;
23. 23
Βήμα 3.4 : Refactoring to functions
int ipow(int num, int to);
int calculateSquare4(int num);
int calculateSquare4(int num) {
int sum = 0;
do {
int digit = num % 10;
sum += ipow(digit, 4);
num = num / 10;
} while ( num != 0 );
return sum;
}
int main() {
int x = 4712;
int sum = calculateSquare4(x);
assert(sum == pow(4, 4) + pow(7, 4) + pow(1, 4) + pow(2, 4)); // 2674
puts("All assertions passed !");
}
24. 24
Βήμα 3.5 : Βελτιστοποιώντας την ipow
●
Εφόσον τα πάντα είναι στην θέση τους μπορώ
να κάνω με ασφάλεια μικρές πινελιές απόδοσης
int ipow4(int num);
int ipow4(int num) {
int temp = num * num;
return temp *temp;
}
25. 25
Το τελικό πρόγραμμα
int main() {
bool isFound = false;
for (int x = 1000; x <= 9999; x++) {
int sum = calculateSquare4(x);
if (sum == x) {
printf("A solution found %dn", x);
isFound = true;
}
}
if (isFound == false) {
puts("No solutions found");
}
}
26. 26
Συμπεράσματα
●
Καταλήξαμε (σχεδόν αυτόματα!) σε ένα κομψό αλγόριθμο
●
Εύκολα κατανοητός και με ευκολία αλλαγής
– Τα assertions μας προστάτευαν από τα λάθη μας
– Πόσο εύκολα αλλάζει αν κάναμε λάθος στο “,” της εκφώνησης;
●
Ποιο γενικός κώδικας
– Δουλεύει με αριθμούς οποιουδήποτε μεγέθους.
– Δεν χρησιμοποιεί αριθμούς κινητής υποδιαστολής
27. 27
Μικροβελτιστοποιήσεις
●
Μια μοντέρνα CPU είναι πολύπλοκη.
●
Δεν είναι όλες οι πράξεις ίδιες.
●
Δεν είναι όλες οι προσβάσεις στην μνήμη ίδιες
●
Η κλίμακα είναι λογαριθμική
●
Η πρόσβαση στην μνήμη συχνά ποιο σημαντική
από τον αλγόριθμο
●
Μια απλή αλλαγή μπορεί να αλλάξει την ταχύτητα
Χ100 φορές.
●
Μόνο με μετρήσεις μπορούμε να ξτο
τεκμηριώσουμε,
●
Δεν θα ασχοληθούμε με αυτό περαιτέρω