├── README.md ├── fd.pl ├── fd_parser.pl ├── fdc.pl ├── functional.pl ├── index.html ├── plunit_install.txt ├── sets.pl ├── start.bat ├── start.sh ├── test.pl ├── test.sh ├── timeout.pl └── ws.pl /README.md: -------------------------------------------------------------------------------- 1 | Functional Dependency Calculator 2 | ================================ 3 | Gabor Szarnyas, Adam Lippai, 2012--2021. 4 | 5 | Little Prolog tool for performing simple algorithms on functional dependency sets. 6 | 7 | Given a relational scheme and a set of functional dependencies the program can 8 | - determine the highest normal form of a relational schema, 9 | - enumerate minimal covers of the FD set, 10 | - enumerate the keys of the relational schema, 11 | - determine the primary and secondary attributes of the relational schema, 12 | - enumerate lossless and dependency preserving 3NF or lossless BCNF decompositions of the schema. 13 | 14 | Compatible and tested with [SWI-Prolog](https://www.swi-prolog.org/). This Prolog implementation was chosen because of its module concept, its ability to run a HTTP server, exchange data in AJAX format and its unit testing framework. 15 | 16 | Architecture 17 | ------------ 18 | ``` 19 | ┌───────────────────────┐ ┌────────────────────────┐ 20 | │ web frontend │ │ Prolog console │ 21 | │ [e.g. HTML+AJAX page] │ │ [e.g. SWI─Prolog] │ 22 | └───────────┬───────────┘ └────────────┬───────────┘ 23 | │ │ 24 | ┌───────────┴───────────┐ │ 25 | │ web service (ws.pl) │ │ 26 | │ [SWI-Prolog] │ │ 27 | └───────────┬───────────┘ │ 28 | │ │ 29 | ┌───────────┴────────────────────────────┴───────────┐ 30 | │ Functional Dependency Calculator frontend (fd.pl) │ 31 | └─────────────────────────┬──────────────────────────┘ 32 | │ 33 | ┌─────────────────────────┴──────────────────────────┐ 34 | │ Functional Dependency Calculator core (fdc.pl) │ 35 | └────────────────────────────────────────────────────┘ 36 | ``` 37 | 38 | Each layer uses only lower layers so the web service, the frontend and the core layer may run without the higher ones. 39 | 40 | Usage 41 | ----- 42 | In the SWI-Prolog console compile `fd.pl` (type `[fd].`). 43 | 44 | Functional dependencies are formatted as `ab->cd`. Prolog atoms must begin with a small letter so you should use small letters for each attribute. 45 | 46 | Enumerate minimal covers of a FD set (note that the relational schema is irrelevant in this case). 47 | ``` 48 | ?- fmin([cd->e, ab->cd, d->a, a->b, b->ac], FMin). 49 | FMin = [ (a->b), (b->c), (b->d), (d->a), (d->e)] ; 50 | FMin = [ (a->b), (a->d), (b->a), (b->c), (d->a), (d->e)] ; 51 | FMin = [ (a->b), (a->c), (b->d), (d->a), (d->e)] ; 52 | FMin = [ (a->b), (a->c), (a->d), (b->a), (d->a), (d->e)] ; 53 | false. 54 | ``` 55 | 56 | Determine the highest normal form of a relational schema: 57 | ``` 58 | ?- nf(abcdef, [a->b, b->c, c->a, d->e, e->f, f->d], NF). 59 | NF = nf3NF. 60 | ``` 61 | Enumerate the keys of a relational schema: 62 | ``` 63 | ?- keys(abcdef, [a->b, b->c, c->a, d->e, e->f, f->d], Keys). 64 | Keys = [af, ae, ad, bf, be, bd, cf, ce, cd]. 65 | ``` 66 | 67 | Determine the primary and secondary attributes of a relational schema: 68 | ``` 69 | ?- primaryattributes(abcd, [a->b, bc->ad], Primary). 70 | Primary = [a, b, c]. 71 | 72 | ?- secondaryattributes(abcd, [a->b, bc->ad], Secondary). 73 | Secondary = [d]. 74 | ``` 75 | 76 | Enumerate lossless and dependency preserving 3NF... 77 | ``` 78 | ?- d3nf(abcde, [ab->b, b->c, c->bd], Rho). 79 | Rho = [ace, bc, cd] ; 80 | Rho = [abe, bc, cd] ; 81 | false. 82 | ``` 83 | 84 | ...or lossless BCNF decompositions of a schema: 85 | ``` 86 | ?- bcnf(abcde, [ab->b, b->c, c->bd], Rho). 87 | Rho = [ab, abe, bc, bd] ; 88 | Rho = [abe, bc, bd] ; 89 | Rho = [abe, bc, bd, be] ; 90 | Rho = [ac, ace, bc, cd] ; 91 | Rho = [ace, bc, cd] ; 92 | Rho = [ace, bc, cd, ce] ; 93 | Rho = [ab, ace, bc, cd] ; 94 | Rho = [ab, abe, bc, cd] ; 95 | Rho = [abe, ac, bc, cd] ; 96 | Rho = [abe, bc, cd] ; 97 | Rho = [abe, bc, be, cd] ; 98 | Rho = [abe, bc, cd, ce] ; 99 | Rho = [ace, bc, be, cd] ; 100 | false. 101 | ``` 102 | The BCNF decomposition algoritm may produce a _lot_ of possible decompositions even for schemas with only a few attributes. 103 | 104 | Starting the web service 105 | ------------------------ 106 | - From SWI-Prolog: compile ```ws.pl``` then run ```start.```. 107 | - From command line: start the web service with the ```start.sh``` or ```start.bat``` script. Visit http://localhost:5000/ and try the examples. You may change the port by editing the ```port``` clause in ```ws.pl```. 108 | 109 | Running the unit tests 110 | ---------------------- 111 | To run the unit tests, run ```test.sh```. Note that SWI-Prolog does not include ```plunit``` by default, see ```plunit_install.txt``` for details. 112 | -------------------------------------------------------------------------------- /fd.pl: -------------------------------------------------------------------------------- 1 | :- module(fd, [nf/3, fmin/2, fmins_all/2, fequiv/2, keys/3, primaryattributes/3, secondaryattributes/3, fclose/3, bcnf/3, bcnfs_all/3, d3nf/3, d3nfs_all/3]). 2 | :- use_module(functional). 3 | :- use_module(fdc). 4 | :- use_module(library(lists)). 5 | :- use_module(library(time)). 6 | :- use_module(fd_parser). 7 | 8 | % convert atom to list of one-character atoms and backwards 9 | % myatom <---> [m, y, a, t, o, m] 10 | % [a] ----> add 11 | % a <---- a 12 | atom_to_list(A, L) :- 13 | ( atom(L) -> A = L 14 | ; atom_chars(A, L) 15 | ). 16 | 17 | list_of_atom_to_list([], []) :- !. 18 | list_of_atom_to_list([A|As], [L|Ls]) :- 19 | atom_to_list(A, L), 20 | list_of_atom_to_list(As, Ls). 21 | 22 | list_sort([], []). 23 | list_sort([H|T], [HS|TS]) :- 24 | sort(H, HS), 25 | list_sort(T, TS). 26 | 27 | % from pretty to canonical 28 | prettyFDs(FC, FP) :- 29 | map(FC, fd:prettyFD, FP). 30 | 31 | prettyFD(XL->YL, XA->YA) :- 32 | atom_to_list(XA, XL), 33 | atom_to_list(YA, YL). 34 | 35 | % from canonical to pretty 36 | canonicalFDs(FC, FP) :- 37 | map(FC, fd:canonicalFD, FP). 38 | 39 | canonicalFD(C, P) :- 40 | prettyFD(P, C). 41 | 42 | nf(R, F, N) :- 43 | atom_to_list(R, R0), 44 | canonicalFDs(F, F0), 45 | cSingleRightSide(F0, F1), 46 | cNF(R0, F1, N). 47 | 48 | % ========== ========== 49 | 50 | fequiv(F, G) :- 51 | canonicalFDs(F, F0), 52 | canonicalFDs(G, G0), 53 | cSingleRightSide(F0, F1), 54 | cSingleRightSide(G0, G1), 55 | cFequiv(F1, G1). 56 | 57 | keys(R, F, Keys) :- 58 | atom_to_list(R, R0), 59 | canonicalFDs(F, F0), 60 | cSingleRightSide(F0, F1), 61 | cKeys(R0, F1, Keys0), 62 | list_of_atom_to_list(Keys, Keys0). 63 | 64 | primaryattributes(R, F, PrimaryAttributes) :- 65 | atom_to_list(R, R0), 66 | canonicalFDs(F, F0), 67 | cSingleRightSide(F0, F1), 68 | cPrimaryAttributes(R0, F1, PrimaryAttributes0), 69 | sort(PrimaryAttributes0, PrimaryAttributes). 70 | 71 | secondaryattributes(R, F, SecondaryAttributes) :- 72 | atom_to_list(R, R0), 73 | canonicalFDs(F, F0), 74 | cSingleRightSide(F0, F1), 75 | cSecondaryAttributes(R0, F1, SecondaryAttributes0), 76 | sort(SecondaryAttributes0, SecondaryAttributes). 77 | 78 | % convert a schema decomposition (set of atoms) to readable text 79 | decomposition_to_text(L, L0) :- 80 | atomic_list_concat(L, ', ', L0). 81 | 82 | % calculate the closure of a set 83 | fclose(R, F, FClosed) :- 84 | atom_to_list(R, R0), 85 | canonicalFDs(F, F0), 86 | cSingleRightSide(F0, F1), 87 | cFclose(R0, F1, FClosed0), 88 | prettyFDs(FClosed0, FClosed). 89 | 90 | % ==================== prodecures that return multiple answers ==================== 91 | % naming convention: 92 | % - proc: produces one answer, can backtrack (e.g. by hitting the ; key on the console) 93 | % - procs_all: produces all answers in a list 94 | % - procs: produces all answers in multiline text format 95 | 96 | % ========= BCNF ========== 97 | % decomposition to BCNF schemas 98 | bcnf(R, F, Rho) :- 99 | atom_to_list(R, R0), 100 | canonicalFDs(F, F0), 101 | cSingleRightSide(F0, F1), 102 | cBCNF(R0, F1, Rho0), 103 | list_of_atom_to_list(Rho, Rho0). 104 | 105 | % list all BCNF decompositions 106 | bcnfs_all(R, F, Rhos) :- 107 | findall(Rho, bcnf(R, F, Rho), Rhos). 108 | 109 | % ========== 3NF ========== 110 | % decomposition to 3NF schemas 111 | d3nf(R, F, Rho) :- 112 | atom_to_list(R, R0), 113 | canonicalFDs(F, F0), 114 | c3NF(R0, F0, Rho0), 115 | list_of_atom_to_list(Rho, Rho0). 116 | 117 | % aggregate all 3NF decompositons 118 | d3nfs_all(R, F, Rhos) :- 119 | findall(Rho, d3nf(R, F, Rho), Rhos). 120 | 121 | % ========= FMin ========== 122 | fmin(F, Fmin) :- 123 | canonicalFDs(F, F0), 124 | cFmin(F0, F1), 125 | prettyFDs(F1, Fmin). 126 | 127 | fmins_all(F, FMins) :- 128 | findall(FMin, fmin(F, FMin), FMins). 129 | 130 | 131 | -------------------------------------------------------------------------------- /fd_parser.pl: -------------------------------------------------------------------------------- 1 | :- module(fd_parser, [parse_fds/2, fds_to_string/2]). 2 | :- use_module(functional). 3 | 4 | % operator for readable FDs 5 | :- op(800, xfx, ->). 6 | 7 | strip_spaces(S, NoSpaces) :- 8 | atomic_list_concat(L, ' ', S), 9 | atomic_list_concat(L, NoSpaces). 10 | 11 | fd_string_to_list(S, L) :- 12 | strip_spaces(S, NoSpaces), 13 | atomic_list_concat(L, ',', NoSpaces). 14 | 15 | fds_to_string(L, S) :- 16 | map(L, fd_parser:fd_to_string, S0), 17 | atomic_list_concat(S0, ', ', S). 18 | 19 | fd_to_string(X->Y, S) :- 20 | atomic_list_concat([X,Y], '->', S). 21 | 22 | fdsplitter(S, FD) :- 23 | atomic_list_concat(FD0, '->', S), 24 | FD0 = [X,Y], 25 | FD = (X->Y). 26 | 27 | parse_fds(S, FD) :- 28 | fd_string_to_list(S, L), map(L, fd_parser:fdsplitter, FD). 29 | 30 | -------------------------------------------------------------------------------- /fdc.pl: -------------------------------------------------------------------------------- 1 | :- module(fdc, [cSingleRightSide/2, cNF/3, cFmin/2, cFequiv/2, cKeys/3, cPrimaryAttributes/3, cSecondaryAttributes/3, cFclose/3, cBCNF/3, c3NF/3, cDecomposeTo3NF/3]). 2 | :- use_module(functional). 3 | :- use_module(sets). 4 | :- dynamic(leftred/1). 5 | :- dynamic(minimal/1). 6 | :- dynamic(bcnfdecomposition/1). 7 | :- dynamic(d3nfdecomposition/1). 8 | 9 | % operator for readable FDs 10 | :- op(800, xfx, ->). 11 | 12 | % the code relies heavily on backtracking, so it's worth noting 13 | % that the -> operator destroys choice-points created inside the clause. 14 | % so instead of 15 | % ( Condition -> Then 16 | % ; Else 17 | % ) 18 | % we have to use 19 | % ( Condition, "Then"... 20 | % ; \+ Condition, "Else"... 21 | % ) 22 | % to let backtrack work. 23 | % see also: http://www.swi-prolog.org/pldoc/man?predicate=send_arrow%2f2 24 | 25 | % XClosed = X+(F) 26 | cClose(X, F, XClosed) :- 27 | ( X = [] -> XClosed = [] 28 | ; foldr(F, fdc:cExpand, X, X0), 29 | ( X = X0 -> XClosed = X0 30 | ; cClose(X0, F, XClosed) 31 | ) 32 | ). 33 | 34 | % FD, set of attributes, expanded set of attributes 35 | cExpand(Y->B, X, XExpanded) :- 36 | ( subset(Y, X) -> union(X, [B], XExpanded) % Y is a subset of X 37 | ; XExpanded = X % cannot expand 38 | ). 39 | 40 | % X is a superkey if X+(F) = R 41 | % two sets are equivalent if both are a subset of the other. 42 | % the X+(F) is always a subset of R, so we need to check if R is a subset of X+(F) 43 | cSuperkey(R, F, X) :- 44 | cClose(X, F, XClosed), subset(R, XClosed). 45 | 46 | % X->A is trivial if A is an element of X 47 | cTrivial(X->A) :- 48 | memberchk(A, X). 49 | 50 | % S is the superkeys of schema R with FDs F 51 | cSuperKeys(R, F, S) :- 52 | powerSet(R, Hatv), 53 | findall(X, (member(X, Hatv), cSuperkey(R, F, X)), S). 54 | 55 | % a key K is minimal if no real subset of K is a superkey 56 | cMinimal(X, SuperKeys) :- 57 | \+ bagof(K, (member(K, SuperKeys), K \= X, subset(K, X)), _). 58 | % ^ looking for real subsets of X in the set of SuperKeys 59 | % if we found one, the superkey is not minimal -> the negation causes the clause to fail 60 | 61 | % key: 1) superkey 2) minimal 62 | cKeys(R, F, Keys) :- 63 | cSuperKeys(R, F, SuperKeys), 64 | findall(X, (member(X, SuperKeys), cMinimal(X, SuperKeys)), Keys). 65 | 66 | % set of primary attributes 67 | cPrimaryAttributes(R, F, PrimaryAttributes) :- 68 | cKeys(R, F, Keys), 69 | union(Keys, PrimaryAttributes). 70 | 71 | % set of secondary attributes 72 | cSecondaryAttributes(R, F, SecondaryAttributes) :- 73 | cPrimaryAttributes(R, F, PrimaryAttributes), 74 | subtract(R, PrimaryAttributes, SecondaryAttributes). 75 | 76 | % is primary attribute 77 | cPrimary(R, F, A) :- 78 | cPrimaryAttributes(R, F, PrimaryAttributes), 79 | memberchk(A, PrimaryAttributes). 80 | 81 | % ==================== BCNF =================== 82 | % for all X->A in we check if it satisfies BCNF 83 | cTestBCNF(R, F) :- 84 | findall(XA, (member(XA, F), cSatisfiesBCNF(R, F, XA)), L), 85 | subset(F, L). 86 | 87 | % X->A non-trivial FD satisfies BCNF if X is a superkey 88 | cSatisfiesBCNF(R, F, X->A) :- 89 | ( cTrivial(X->A) 90 | ; cSuperkey(R, F, X) 91 | ). 92 | 93 | % ==================== 3NF ==================== 94 | cTest3NF(R, F) :- 95 | findall(XA, (member(XA, F), cSatisfies3NF(R, F, XA)), L), 96 | subset(F, L). 97 | 98 | % X->A non-trivial FD satisfies 3NF if X is a superkey or A is a primary attribute 99 | cSatisfies3NF(R, F, X->A) :- 100 | ( cTrivial(X->A) 101 | ; cSuperkey(R, F, X) 102 | ; cPrimary(R, F, A) 103 | ). 104 | 105 | % ==================== 2NF ==================== 106 | cTest2NF(R, F) :- 107 | cKeys(R, F, Keys), 108 | cSecondaryAttributes(R, F, SecondaryAttributes), 109 | % collect the solutions of key->secondary attribute FDs 110 | \+ bagof(K->A, (member(K, Keys), member(A, SecondaryAttributes), \+ cSatisfies2NF(F, K->A)), _). 111 | 112 | % K->A (where K is a key, A is a secondary attribute) satisfies 2NF if no real subset X of K exist such that X->A 113 | cSatisfies2NF(F, K->A) :- 114 | powerSet(K, KSubsets), 115 | subtract(KSubsets, [K], KRealSubsets), 116 | \+ bagof(X, (member(X, KRealSubsets), cClose(X, F, XClosed), memberchk(A, XClosed)), _). 117 | 118 | % ============ highest normal form ============= 119 | cNF(R, F, NF) :- 120 | ( cTestBCNF(R, F) -> NF = nfBCNF 121 | ; cTest3NF(R, F) -> NF = nf3NF 122 | ; cTest2NF(R, F) -> NF = nf2NF 123 | ; NF = nf1NF 124 | ). 125 | 126 | % 1st step of minimalizing 127 | % all FDs may have a single attribute on their right side 128 | cSingleRightSide(F, FFormatted) :- 129 | foldl(F, fdc:cDecompose, [], F0), 130 | lists:reverse(F0, FFormatted). 131 | 132 | % decomposing right side of a FD (consequence of Armstrong's axioms) 133 | cDecompose(X->Y, F, F1) :- 134 | ( Y = [A|Yt] -> cDecompose(X->Yt, [X->A|F], F1) 135 | ; F1 = F 136 | ). 137 | 138 | % 2nd step of minimalizing 139 | % omitting superfluous attributes from the left side of FDs 140 | cMinimalizeLeftSide(F, FLeftRed) :- 141 | retractall(leftred(_)), 142 | cMinimalizeLeftSide(F, F, FLeftRed, false). 143 | 144 | cMinimalizeLeftSide([], FLeftRed, FLeftRed, false) :- 145 | \+ leftred(FReduced), assert(leftred(FReduced)). 146 | cMinimalizeLeftSide([X->A|T], F, FLeftRed, SkippedFlag) :- 147 | ( cReducible(X->A, F, Y), 148 | subtract(F, [X->A], F0), 149 | union(F0, [Y->A], F1), 150 | ( cMinimalizeLeftSide(F1, FLeftRed) 151 | ; cMinimalizeLeftSide(T, F, FLeftRed, true) 152 | ) 153 | ; \+ cReducible(X->A, F, Y), cMinimalizeLeftSide(T, F, FLeftRed, SkippedFlag) 154 | ). 155 | 156 | % 3rd step of minimalizing 157 | % skipping deducible FDs 158 | cSkipFDs(F, FMin) :- 159 | cSkipFDs(F, F, FMin, false). 160 | 161 | % we may skip X->A if A is in (X)+(G), where G is F \ {X->A} 162 | % cSkipFDs(tail of FDs, all FDs, minimalised FDs, reduced bit) 163 | cSkipFDs([], FReduced, FReduced, false). 164 | cSkipFDs([X->A|T], F, FReduced, SkippedFlag) :- 165 | subtract(F, [X->A], G), % G = F \ {X->A} 166 | cClose(X, G, XClosed), % calculating X+(G) 167 | ( memberchk(A, XClosed) -> % A is in X+(G) 168 | ( cSkipFDs(G, FReduced) 169 | ; cSkipFDs(T, F, FReduced, true) 170 | ) 171 | ; cSkipFDs(T, F, FReduced, SkippedFlag) 172 | ). 173 | 174 | cReducible(X->A, F, Y) :- 175 | cLeftRed(X, X->A, F, Y). 176 | 177 | cLeftRed([H|T], X->A, F, Y) :- 178 | subtract(X, [H], X0), 179 | cClose(X0, F, X0C), 180 | ( memberchk(A, X0C), Y = X0 181 | ; cLeftRed(T, X->A, F, Y) 182 | ). 183 | 184 | cFmin(F, FMin) :- 185 | retractall(minimal(_)), 186 | cSingleRightSide(F, F1), 187 | cMinimalizeLeftSide(F1, F2), 188 | cSkipFDs(F2, F3), 189 | sort(F3, FMin), 190 | \+ minimal(FMin), assert(minimal(FMin)). 191 | 192 | cFsubset([], _G). 193 | cFsubset([X->A|T], G) :- 194 | cClose(X, G, XClosed), 195 | memberchk(A, XClosed), 196 | cFsubset(T, G). 197 | 198 | cFequiv(F, G) :- 199 | cFsubset(F, G), 200 | cFsubset(G, F). 201 | 202 | cNonTrivialClosures([], _, []). 203 | cNonTrivialClosures([H|T], F, FClosed) :- 204 | cNonTrivialClosures(T, F, TClosed), 205 | cClose(H, F, HClosed), 206 | subtract(HClosed, H, HClosedNonTrivial), 207 | ( HClosedNonTrivial = [] -> FClosed = TClosed 208 | ; FClosed = [H->HClosedNonTrivial|TClosed] 209 | ). 210 | 211 | cFclose(R, F, FClosed) :- 212 | powerSet(R, RP), 213 | cNonTrivialClosures(RP, F, FClosed0), 214 | cSingleRightSide(FClosed0, FClosed). 215 | 216 | cFilterProjected([], _, []). 217 | cFilterProjected([X->A|T], S, FF) :- 218 | cFilterProjected(T, S, TFF), 219 | ( memberchk(A, S) -> FF = [X->A|TFF] 220 | ; FF = TFF 221 | ). 222 | 223 | % FP is the set of projected FDs on F 224 | cProjectFDs(F, S, FP) :- 225 | cFclose(S, F, FP0), 226 | cFilterProjected(FP0, S, FP). 227 | 228 | % enumerate the next BCNF decomposition 229 | cBCNF(S, G, Rho) :- 230 | retractall(bcnfdecomposition(_)), 231 | cDecomposeToBCNF(S, G, Rho0), 232 | sort(Rho0, Rho), 233 | \+ bcnfdecomposition(Rho), 234 | assert(bcnfdecomposition(Rho)). 235 | 236 | % decompose to BCNF 237 | cDecomposeToBCNF(S, G, Rho) :- 238 | findall(XA, (member(XA, G), cSatisfiesBCNF(S, G, XA)), SatisfyingBCNF), 239 | subtract(G, SatisfyingBCNF, ViolatingBCNF), % find the FDs that violate the BCNF property 240 | ( member(X->A, ViolatingBCNF), 241 | ( 242 | union(X, [A], S1), 243 | subtract(S, [A], S2), 244 | cProjectFDs(G, S1, G1), 245 | cProjectFDs(G, S2, G2), 246 | sort(S1, S10), 247 | sort(S2, S20), 248 | cDecomposeToBCNF(S10, G1, Rho1), 249 | cDecomposeToBCNF(S20, G2, Rho2), 250 | append(Rho1, Rho2, Rho) 251 | ) 252 | ; ViolatingBCNF = [], Rho = [S] 253 | ). 254 | 255 | % enumerate the next BCNF decomposition 256 | c3NF(S, G, Rho) :- 257 | retractall(d3nfdecomposition(_)), 258 | cDecomposeTo3NF(S, G, Rho0), 259 | sort(Rho0, Rho), 260 | \+ d3nfdecomposition(Rho), 261 | assert(d3nfdecomposition(Rho)). 262 | 263 | % decompose to 3NF 264 | cDecomposeTo3NF(S, G, Rho) :- 265 | cSingleRightSide(G, G0), 266 | cKeys(S, G0, Keys), % determine the keys 267 | member(Key, Keys), % pick a key 268 | cFmin(G, GMin), % determine a possible minimal FD set 269 | map(GMin, fdc:cDependencyToSchema, Rho0), % convert the minimal FD set to relational schemas 270 | ( hasSuperSet(Rho0, Key) -> Rho = Rho0 % it contains the key, there is no need to add 271 | ; union(Rho0, [Key], Rho1), sort(Rho1, Rho) 272 | ). 273 | 274 | % converts a (canonical) minimal FD to relational schema, 275 | % e.g. [a, b]->e becomes [a, b, e] 276 | cDependencyToSchema(X->Y, R) :- 277 | union(X, [Y], R0), 278 | sort(R0, R). 279 | 280 | % returns if there is a set in a list of sets L which is a superset of S 281 | hasSuperSet(L, S) :- 282 | member(E, L), !, 283 | ( call(subset, S, E), ! 284 | ; L = [_|T], hasSuperSet(T, S) 285 | ). 286 | 287 | -------------------------------------------------------------------------------- /functional.pl: -------------------------------------------------------------------------------- 1 | :- module(functional, [foldl/4, foldr/4, map/3]). 2 | % fold functions 3 | foldl([X|Xs], Pred, Y0, Y) :- 4 | call(Pred, X, Y0, Y1), foldl(Xs, Pred, Y1, Y). 5 | foldl([], _, Y, Y). 6 | 7 | foldr([X|Xs], Pred, Y0, Y) :- 8 | foldr(Xs, Pred, Y0, Y1), call(Pred, X, Y1, Y). 9 | foldr([], _, Y, Y). 10 | 11 | % mapping function 12 | map([X|Xs], Pred, [Y|Ys]) :- 13 | call(Pred, X, Y), map(Xs, Pred, Ys). 14 | map([], _, []). -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |