25) June 1 - Cost Models ================================================================================ 1) Let's remember how was the implementation of append in Prolog? @([], L, L). @([H|T], L, [H|Tt]) :- @(T, L, Tt). 1.1) Which one is faster? $> swipl ?- length(L, 5000000), time(append([a], L, LL)). ?- length(L, 5000000), time(append(L, [a], LL)). 2) How do you think lists are implemented in SML or Prolog? 3) Represent it graphically: ?- D = [2, 3], E = [1|D], E = [F|G], D = [2, 3]. 4) How do we know that SML and Prolog share list structures? 4.1) Python has an append operator '+': >>> l = [1,2,3] >>> a = l + [4] >>> l [1, 2, 3] >>> a [1, 2, 3, 4] How do we know if '+' reuses the elements of both or some of the lists, or copies them again? 5) How was the implementation of length in SML? fun length nil = 0 length (head::tail) = 1 + length tail; 5.1) What's its complexity? 6) How was the implementation of append in SML? fun app nil L = L | app (h::t) L = h :: app t L 6.1) What's its complexity? 6.2) Represent it graphically: val h = [1,2]; val i = [3,4]; val j = app h i; 7) What is the complexity of unifying lists? ?- K = [1,2], | M = K, | M = [1,2]. 7.1) Represent this program graphically. 7.2) Implement the predicate xequal(X, Y), that is true when X and Y represent the same list: &=([], []). &=([H|Tl], [H|Tr]) :- &=(Tl, Tr). 7.3) Which one is faster: to unify two lists, or to compute the length of two lists? 7.4) Any optimization? * Cons-Cell Sumarry: - Consing takes constant time. - Extracting head or tail takes constant time. - Computing the length of a list takes time proportional to the length. - Computing the result of appending two lists takes time proportional to the length of the first list. - Comparing two lists, in the worst case, takes time proportional to their size. 8) What's the complexity of reverse? rev0([],[]). rev0([Head|Tail],Rev) :- rev0(Tail,TailRev), append(TailRev,[Head],Rev). 8.1) How to do it better? rev1(X,Y) :- rev_aux(X,[],Y). rev_aux([],Sofar,Sofar). rev_aux([Head|Tail],Sofar,Rev) :- rev_aux(Tail,[Head|Sofar],Rev). 8.2) How to create a list of 10,000 elements in Prolog? - Try it: length(X, 10000), rev0(X, Y). - Try it: length(X, 10000), rev1(X, Y). 8.3) What about in SML? - fun rev [] acc = acc = | rev (h::t) acc = rev t (h::acc); - fun reverse l = rev l []; 8.4) Draw the activation records of reverse [1,2] rev -> head: 1 tail: [2] sofar: nil ret addr P.A.R. Res: ? 8.5) Which optimizations are possible in this sequence of calls? 9) Which implementation is more efficient: - fun sum [] = 0 = | sum (a::l) = sum l + a; - or - - fun sum [] acc = acc = | sum (a::l) acc = sum l (a + acc); * When a function makes a tail call, it no longer needs its activation record 10) Is it tail call? fun sum [] = 0 | sum (h::t) = h + sum t 10.1) How to convert to a tail call function? 11) How to convert length below to tail call? fun length nil = 0 | length (head::tail) = 1 + length tail; fun length thelist = let fun len (nil,sofar) = sofar | len (head::tail,sofar) = len (tail,sofar+1); in len (thelist,0) end; 12) Is p a tail call in the predicate below? p(X) :- q(X), Z is X - 1, p(Z). - Not necessarily, due to backtracking. 13) Why is the predicate below inefficient? grandfather(X,Y) :- parent(X,Z), parent(Z,Y), male(X). e.g: parent(d, a). parent(d,a) -> parent(a,b) -> male(d) -> V parent(f, e). parent(f,e) -> parent(e,b) -> male(f) -> X parent(a, b). parent(f,e) -> parent(e,b) -> male(f) -> X parent(e, b). parent(a,b) -> X parent(e, c). parent(e,b) -> X male(d). parent(e,c) -> X male(a). 13.1) How to improve it? grandfather(X,Y) :- male(X), parent(X,Z), parent(Z,Y). e.g: parent(d, a). male(d) -> parent(d,a) -> parent(a, b) -> V parent(f, e). male(a) -> parent(a,b) -> X parent(a, b). parent(e, b). parent(e, c). male(d). 13.2) What is a general guideline to code efficient Prolog searches? 14) How are arrays stored in memory? 15) What is the result of the matrix.c program? * There are two main allocation schemes for arrays: - row-major order: C, C++. - column-major order: Fortran 16) Does a C program still work if the allocation scheme changes? 17) How to index data in a m*n bi-dimensional array allocated in row major order? A[i, j] = base + (i * n * size) + (j * size) 17.1) What if the m*n array is column major? A[i, j] = base + (i * size) + (j * m * size) 17.2) Can you find the bug in the program below? #include #include void swap(int x[3][2], int i0, int j0, int i1, int j1) { printf("x[%d][%d] = %d\n", i0, j0, x[i0][j0]); printf("x[%d][%d] = %d\n", i1, j1, x[i1][j1]); int tmp = x[i0][j0]; x[i0][j0] = x[i1][j1]; x[i1][j1] = tmp; } int main() { int a0[3][2] = {{0, 1}, {2, 3}, {4, 5}}; int a1[4][2] = {{0, 1}, {2, 3}, {4, 5}, {6, 7}}; int** a2 = (int**)malloc(3 * sizeof(int*)); for (int i = 0; i < 2; ++i) { a2[i] = (int*)malloc(2 * sizeof(int)); for (int j = 0; j < 2; ++j) { a2[i][j] = 2*i + j; } } swap(a0, 0, 0, 1, 1); swap(a1, 0, 0, 1, 1); swap(a2, 0, 0, 1, 1); } 18) Which way to sum up the elements in the matrix is faster? #include #include int main(int argc, char** argv) { const int M = 4000; const int N = 2000; char m[M][N]; int i, j, k; int sum = 0; clock_t start, end; double time; // Initializes the array: for (i = 0; i < M; i++) { for (j = 0; j < N; j++) { m[i][j] = (i + j) & 7; } } start = clock(); printf("argc = %d\n", argc); if (argc % 2) { printf("argc is even: summing row major:\n"); // Sums up the matrix elements, row major: for (i = 0; i < M; i++) { for (j = 0; j < N; j++) { sum += m[i][j]; } } } else { printf("argc is odd: summing column major:\n"); // Sums up the matrix of elements, column major: for (j = 0; j < N; j++) { for (i = 0; i < M; i++) { sum += m[i][j]; } } } end = clock(); time = ((double) (end - start)) / CLOCKS_PER_SEC; printf("Sum: %d, Time: %lf\n", sum, time); } 19) How are arrays organized in Java? 20) How to speed up the program below? // user 0m0.853s #include "stdio.h" int max(int i, int j) { return i > j ? i : j; } int main() { int i, j; double sum = 0.0; for (i = 0; i < 10000; i++) { for (j = 0; j < 10000; j++) { sum += max(i, j); } } printf("%d\n", sum); } 21) Is inlining a case for macros over functions, after all? 22) How to code efficient programs, after all?