InterviewPitch
Prolog interview questions

Prolog Interview Questions with Answers

Most Asked Prolog Interview Questions for Software Engineer Roles

100+ QuestionsDetailed AnswersCode ExamplesUpdated for 2026

Introduction

This page provides a complete collection of Prolog Interview Questions and Answers designed for software developers, AI engineers, logic programming professionals, and candidates preparing for programming language interviews. Prolog is a declarative programming language based on logic programming principles. It is widely used in artificial intelligence, expert systems, natural language processing, knowledge representation, and automated reasoning applications. This interview guide covers beginner, intermediate, and advanced Prolog concepts including facts, rules, queries, predicates, unification, backtracking, recursion, lists, logical programming, knowledge bases, and real-world AI programming scenarios.

Why Prolog?

  • Declarative paradigm – focuses on what to solve rather than how to solve it
  • Logical reasoning – built-in backtracking and unification for complex problem-solving
  • Used in AI research – expert systems, NLP, theorem proving, and knowledge graphs
  • Pattern matching – powerful unification mechanism for symbolic computation
  • Supports recursion and list processing – efficient for AI and logic applications
  • Academic and research strength – widely used in universities and computational linguistics
  • Unique problem-solving approach – invaluable for certain domains like scheduling and planning

Most Asked Prolog Interview Questions

Beginner
1. What is Prolog?

Prolog (Programming in Logic) is a logic programming language associated with artificial intelligence and computational linguistics.

  • Declarative: Express logic without specifying control flow
  • Pattern matching: Unification and backtracking
  • Facts and rules: Knowledge base representation
  • Recursion: Primary iteration mechanism
  • Backtracking: Automatic search for solutions
prolog
% Hello World in Prolog
:- write('Hello, World!'), nl.
Beginner
2. How to declare variables in Prolog?

Variables in Prolog start with uppercase letters or underscores. They are used for unification and pattern matching.

  • Variables: Start with uppercase (X, Variable)
  • Anonymous variable: _
  • Constants: atom, number
  • Facts: parent(john, mary).
  • Rules: grandparent(X, Z) :- parent(X, Y), parent(Y, Z).
prolog
% Variables in Prolog
% Variables start with uppercase letter or underscore
X = 10,          % Integer
Y = 3.14,        % Float
Name = 'Prolog', % String (atoms)
is_active = true,% Atom

write(X), nl,
write(Y), nl,
write(Name), nl,
write(is_active), nl.
Beginner
3. What are the data types in Prolog?

Prolog has several basic data types including atoms, numbers, lists, and compound terms.

  • Atom: hello, 'Hello World'
  • Number: 42, 3.14
  • Variable: X, _
  • List: [1, 2, 3]
  • Compound term: person(name('Alice'), age(25))
prolog
% Data Types in Prolog
% Integer types
a = 10.          % Integer

% Floating point
d = 3.14.        % Float

% Atom (string)
f = 'Hello Prolog'.

% Boolean (atoms)
g = true.
h = false.

% List
k = [1, 2, 3, 4, 5].

% Tuple (compound term)
person(name('Alice'), age(25), city('NYC')).

% Facts and rules for types
integer(10).
float(3.14).
atom('Hello').
list([1,2,3]).

% Type checking
number(10).
atom('hello').
compound(person(name, age)).
Beginner
4. How to define predicates in Prolog?

Predicates in Prolog are defined using facts and rules. Facts are unconditional truths, while rules define relationships.

  • Facts: parent(john, mary).
  • Rules: grandparent(X, Z) :- parent(X, Y), parent(Y, Z).
  • Recursive predicates: ancestor(X, Z) :- parent(X, Z).
  • Arithmetic: add(X, Y, Z) :- Z is X + Y.
  • Multiple clauses: factorial(0, 1). factorial(N, F) :- ...
prolog
% Functions (Predicates) in Prolog
% Fact
add(0, X, X).

% Recursive predicate
add(s(X), Y, s(Z)) :- add(X, Y, Z).

% Multiple clauses
factorial(0, 1).
factorial(N, F) :- 
    N > 0,
    N1 is N - 1,
    factorial(N1, F1),
    F is N * F1.

% Predicate with multiple arguments
fibonacci(0, 0).
fibonacci(1, 1).
fibonacci(N, F) :- 
    N > 1,
    N1 is N - 1,
    N2 is N - 2,
    fibonacci(N1, F1),
    fibonacci(N2, F2),
    F is F1 + F2.

% Anonymous predicate using lambda (SWI-Prolog)
% square = (X^2).

% Using maplist for mapping
double(X, Y) :- Y is X * 2.

% Usage
% add(s(s(0)), s(0), Z).
% factorial(5, F).
% fibonacci(8, F).
Beginner
5. What are lists in Prolog?

Lists in Prolog are ordered collections of elements. They are built using cons (|) and can be recursively processed.

  • Creation: [1, 2, 3, 4, 5]
  • Head and Tail: [H|T]
  • Common predicates: append, member, length
  • List manipulation: findall, maplist
  • Pattern matching: my_list([H|T]) :- ...
prolog
% Lists in Prolog
% Creating a list
arr = [1, 2, 3, 4, 5].

% Head and tail
head_tail([H|T], H, T).

% Map - transform each element
doubled([], []).
doubled([H|T], [H2|T2]) :- 
    H2 is H * 2,
    doubled(T, T2).

% Filter - select elements
evens([], []).
evens([H|T], [H|T2]) :- 
    H mod 2 =:= 0,
    evens(T, T2).
evens([H|T], T2) :- 
    H mod 2 =:= 1,
    evens(T, T2).

% Reduce - aggregate
sum_list([], 0).
sum_list([H|T], Sum) :- 
    sum_list(T, Sum1),
    Sum is H + Sum1.

% List comprehension (using findall)
squares(N, Squares) :- 
    findall(X2, (between(1, N, X), X2 is X * X), Squares).

% Push and pop
push(List, Element, [Element|List]).
pop([H|T], H, T).

% List operations
append([], L, L).
append([H|T], L, [H|R]) :- append(T, L, R).

% Usage
% doubled([1,2,3,4,5], D).
% evens([1,2,3,4,5,6], E).
% sum_list([1,2,3,4,5], S).
Beginner
6. What are association lists in Prolog?

Association lists are lists of key-value pairs used as simple dictionaries.

  • Creation: [name-alice, age-25, city-nyc]
  • Access: member(name-Value, List)
  • Add: append(List, [key-value], NewList)
  • Check: memberchk(key-_, List)
  • SWI-Prolog library: library(assoc)
prolog
% Association Lists (Key-Value) in Prolog
% Creating association list
person = [name-Alice, age-25, city-NYC].

% Access values using member
get_value(Key, List, Value) :- member(Key-Value, List).

% Add/update values
add_pair(Key, Value, List, [Key-Value|List]).

% Check if key exists
has_key(Key, List) :- member(Key-_, List).

% Iterate over association list
print_pairs([]).
print_pairs([Key-Value|T]) :- 
    write(Key), write(': '), write(Value), nl,
    print_pairs(T).

% Using SWI-Prolog library(assoc)
% :- use_module(library(assoc)).
% empty_assoc(Assoc).
% put_assoc(name, Assoc, 'Alice', NewAssoc).
% get_assoc(name, Assoc, Value).

% Dict (SWI-Prolog)
% Person = _{name:'Alice', age:25, city:'NYC'}.
% Name = Person.name.
Beginner
7. What are compound terms in Prolog?

Compound terms are data structures that combine multiple values, similar to tuples or records.

  • Creation: person(name('Alice'), age(25))
  • Access: Using pattern matching
  • Records: :- use_module(library(record)).
  • Arity: functor(Term, Name, Arity)
  • Decomposition: Term =.. [Functor|Args]
prolog
% Tuples (Compound Terms) in Prolog
% Creating tuple
t = (1, 'hello', 3.14, true).

% Access elements using pattern matching
first((X, _, _, _), X).
second((_, X, _, _), X).

% Named tuple (using compound term)
person(name('Alice'), age(25), city('NYC')).

% Pattern matching on tuples
describe_tuple((0, 0), 'Origin').
describe_tuple((_, 0), 'On x-axis').
describe_tuple((0, _), 'On y-axis').
describe_tuple((_, _), 'Somewhere else').

% Function returning multiple values
divide(A, B, Quotient, Remainder) :- 
    Quotient is A // B,
    Remainder is A mod B.

% Tuple concatenation
concat_tuples((A1, A2, A3), (B1, B2, B3), (A1, A2, A3, B1, B2, B3)).

% Usage
% divide(10, 3, Q, R).
Beginner
8. What are control flow mechanisms in Prolog?

Prolog uses recursion and backtracking for control flow, with predicates for conditional logic.

  • If-else: Condition -> Then ; Else
  • Pattern matching: Multiple predicate clauses
  • Recursion: Primary loop mechanism
  • Cut: ! to prune backtracking
  • Fail: fail to force backtracking
prolog
% Control Flow in Prolog
% If-else using cut and fail
age(25).
age_category(Age, Category) :-
    Age < 18 -> Category = 'Minor';
    Age < 65 -> Category = 'Adult';
    Category = 'Senior'.

% Pattern matching
describe_number(0, 'Zero').
describe_number(1, 'One').
describe_number(2, 'Two').
describe_number(_, 'Other').

% For loop (using between)
print_numbers(N) :- 
    between(1, N, I),
    write(I), nl,
    fail.
print_numbers(_).

% While loop (recursive)
print_upto(0) :- !.
print_upto(N) :- 
    write(N), nl,
    N1 is N - 1,
    print_upto(N1).

% Recursion (preferred in Prolog)
print_list([]).
print_list([H|T]) :- 
    write(H), nl,
    print_list(T).

% Using forall
print_all(List) :- 
    forall(member(X, List), (write(X), nl)).

% Conditional using predicates
is_positive(X) :- X > 0.
is_negative(X) :- X < 0.
is_zero(0).
Beginner
9. How to generate lists in Prolog?

Prolog provides predicates like findall, bagof, and setof for list generation.

  • findall: findall(X, Goal, List)
  • bagof: bagof(X, Goal, List)
  • setof: setof(X, Goal, List)
  • between: between(1, 10, X)
  • maplist: maplist(Goal, List1, List2)
prolog
% List Generation in Prolog
% Using findall for list comprehension
squares(N, Squares) :- 
    findall(X2, (between(1, N, X), X2 is X * X), Squares).

% Filter with findall
evens(N, Evens) :- 
    findall(X, (between(1, N, X), X mod 2 =:= 0), Evens).

% Nested loops
pairs(N, Pairs) :- 
    findall((I,J), (between(1, N, I), between(1, N, J)), Pairs).

% Dict comprehension using findall
square_dict(N, Dict) :- 
    findall(K-V, (between(1, N, K), V is K * K), Dict).

% Generator (using between)
generate_squares(N, Squares) :- 
    findall(X, (between(1, N, I), X is I * I), Squares).

% Conditional list
results(N, Results) :- 
    findall(R, (between(1, N, X), 
                (X mod 2 =:= 0 -> R = even ; R = odd)), Results).

% Usage
% squares(10, S).
% evens(20, E).
% pairs(3, P).
Beginner
10. How to work with atoms in Prolog?

Atoms are the string-like data type in Prolog, used for symbolic representation.

  • Creation: hello, 'Hello World'
  • Concatenation: atom_concat(A, B, C)
  • Functions: atom_length, atom_upper, atom_lower
  • Substring: sub_atom
  • Compare: atom_compare
prolog
% Strings (Atoms) in Prolog
% Atom creation
str1 = 'Hello'.
str2 = 'World'.

% String concatenation (using atom_concat)
atom_concat(str1, str2, Greeting).

% String interpolation (using format)
name = 'Prolog',
version = 7.4,
format('Welcome to ~w version ~w', [name, version]).

% String functions
text = 'Hello, World!',
atom_length(text, Len),
atom_upper(text, Upper),
atom_lower(text, Lower).

% Substring (using sub_atom)
sub_atom(text, 0, 5, _, Sub).

% Split and join (using split_string and atomics_to_string)
split_string('Hello World Prolog', ' ', '', Words),
atomics_to_string(Words, '-', Joined).

% String comparison
'hello' == 'hello'.
'hello' @< 'world'.

% String formatting
format('Value: ~2f', [3.14159]).

% Using SWI-Prolog string type
string_string("Hello", Str).
string_length("Hello", Len).
Beginner
11. What are modules in Prolog?

Modules in Prolog organize code and provide encapsulation for predicates.

  • Definition: :- module(module_name, [exported/arity]).
  • Use: :- use_module(module_name).
  • Consult: :- consult('file.pl').
  • Exports: export/1
  • Meta-predicates: module_transparent/1
prolog
% Modules in Prolog
% Defining a module (save as mymath.pl)
% :- module(mymath, [add/3, subtract/3, pi/1]).
% 
% pi(3.14159).
% add(A, B, C) :- C is A + B.
% subtract(A, B, C) :- C is A - B.
% multiply(A, B, C) :- C is A * B.  % Not exported

% Using a module
% :- use_module(mymath).

% Loading a file
% :- consult('mymath.pl').

% Module with exports
:- module(my_math, [add/3, subtract/3]).

add(A, B, C) :- C is A + B.
subtract(A, B, C) :- C is A - B.

% Using SWI-Prolog libraries
:- use_module(library(lists)).
:- use_module(library(assoc)).
:- use_module(library(apply)).

% Usage
% add(5, 3, Result).
Beginner
12. What are types in Prolog?

Prolog has a dynamic type system with predicates for type checking and type definitions.

  • Built-in types: atom, number, compound
  • Type checking: atom(X), integer(X), float(X)
  • Record types: :- use_module(library(record)).
  • Variant types: Using compound terms
  • Type definitions: type(animal).
prolog
% Types in Prolog
% Basic type definitions
% Type declaration using predicates
int_list([]).
int_list([H|T]) :- integer(H), int_list(T).

% Compound types
:- use_module(library(record)).

% Record definition
:- record person(
    name:atom,
    age:integer,
    city:atom
).

% Creating record
person(name(Alice), age(25), city(NYC)).

% Variant types (using predicates)
color(red).
color(green).
color(blue).
color(rgb(R, G, B)) :- 
    integer(R), integer(G), integer(B), 
    R >= 0, R =< 255,
    G >= 0, G =< 255,
    B >= 0, B =< 255.

% Type with parameters (polymorphic)
% Using compound terms
option(none).
option(some(X)).

% Recursive types
tree(empty).
tree(node(Value, Left, Right)) :-
    tree(Left), tree(Right).

% Type checking
is_integer(X) :- integer(X).
is_atom(X) :- atom(X).
is_compound(X) :- compound(X).

% Usage
% person(name(Alice), age(25), city(NYC)).
% color(rgb(255, 0, 0)).
Intermediate
13. What is pattern matching in Prolog?

Pattern matching is a core feature of Prolog that deconstructs data structures through unification.

  • Basic: match(0) :- ...
  • Lists: sum([], 0). sum([H|T], S) :- ...
  • Compound terms: person(name(Name), age(Age))
  • Guards: Using -> or multiple clauses
  • Nested: Deep pattern matching
prolog
% Pattern Matching in Prolog
% Basic pattern matching
is_zero(0).

% Pattern matching on lists
sum_list([], 0).
sum_list([H|T], Sum) :- 
    sum_list(T, Sum1),
    Sum is H + Sum1.

% Pattern matching with guards
classify_number(N, 'Zero') :- N =:= 0.
classify_number(N, 'Positive') :- N > 0.
classify_number(N, 'Negative') :- N < 0.

% Pattern matching on compound terms
add_tuple((X, Y), Sum) :- Sum is X + Y.

% Pattern matching on records
person(name(Name), age(Age), city(City)).

% Nested pattern matching
sum_tree(empty, 0).
sum_tree(node(Value, Left, Right), Sum) :-
    sum_tree(Left, Sum1),
    sum_tree(Right, Sum2),
    Sum is Value + Sum1 + Sum2.

% Pattern matching with OR (using multiple clauses)
is_zero_or_one(0).
is_zero_or_one(1).

% Fibonacci with multiple patterns
fib(0, 0).
fib(1, 1).
fib(N, F) :- 
    N > 1,
    N1 is N - 1,
    N2 is N - 2,
    fib(N1, F1),
    fib(N2, F2),
    F is F1 + F2.

% Usage
% sum_list([1,2,3,4,5], S).
Intermediate
14. How to handle exceptions in Prolog?

Prolog provides throw and catch for exception handling.

  • Throw: throw(exception(Message))
  • Catch: catch(Goal, Exception, Handler)
  • Custom exceptions: my_exception(Reason)
  • Error handling: error(Error, Info)
  • Finally: Use nested catch
prolog
% Exception Handling in Prolog
% Exception definition (using throw/catch)
divide(A, B, Result) :- 
    B =:= 0 -> 
        throw(division_by_zero(A, B));
    Result is A / B.

% Try-catch block
safe_divide(A, B, Result) :- 
    catch(divide(A, B, Result), 
          division_by_zero(_, _), 
          (write('Cannot divide by zero'), nl, fail)).

% Multiple exception handlers
process_file(File, Content) :- 
    catch(open(File, read, Stream),
          error(existence_error(file, _), _),
          (write('File not found'), nl, fail)),
    catch(read_string(Stream, _, Content),
          End_of_file,
          (write('Empty file'), nl, fail)).

% Using finally
with_file(File, Goal) :- 
    open(File, read, Stream),
    catch((call(Goal, Stream), close(Stream)),
          Error,
          (close(Stream), throw(Error))).

% Custom exceptions
error_invalid_input(Message).

validate_input(Input) :- 
    Input =:= 0 -> 
        throw(error_invalid_input('Input cannot be zero'));
    true.

% Usage
% safe_divide(10, 0, Result).
Intermediate
15. How to work with files in Prolog?

Prolog provides file I/O through open, read, and write predicates.

  • Read: open(File, read, Stream)
  • Write: open(File, write, Stream)
  • Append: open(File, append, Stream)
  • CSV: csv_read_file, csv_write_file
  • File info: exists_file
prolog
% File I/O in Prolog
% Reading files
read_file(File, Content) :- 
    open(File, read, Stream),
    read_string(Stream, _, Content),
    close(Stream).

% Reading line by line
read_lines(File, Lines) :- 
    open(File, read, Stream),
    read_lines(Stream, Lines),
    close(Stream).

read_lines(Stream, []) :- at_end_of_stream(Stream), !.
read_lines(Stream, [Line|Lines]) :- 
    read_line_to_string(Stream, Line),
    read_lines(Stream, Lines).

% Writing files
write_file(File, Content) :- 
    open(File, write, Stream),
    write(Stream, Content),
    close(Stream).

% Appending to files
append_file(File, Content) :- 
    open(File, append, Stream),
    write(Stream, Content),
    close(Stream).

% Reading CSV
read_csv(File, Rows) :- 
    open(File, read, Stream),
    read_csv_rows(Stream, Rows),
    close(Stream).

read_csv_rows(Stream, []) :- at_end_of_stream(Stream), !.
read_csv_rows(Stream, [Row|Rows]) :- 
    read_line_to_string(Stream, Line),
    split_string(Line, ',', '', Row),
    read_csv_rows(Stream, Rows).

% Writing CSV
write_csv(File, Rows) :- 
    open(File, write, Stream),
    forall(member(Row, Rows), 
           (atomic_list_concat(Row, ',', Line),
            write(Stream, Line), nl(Stream))),
    close(Stream).

% File existence
file_exists(File) :- exists_file(File).
Intermediate
16. How to use packages in Prolog?

Prolog packages are managed through the SWI-Prolog pack system or by loading modules.

  • Install: ?- pack_install('package_name').
  • Use: :- use_module(library(package_name)).
  • Libraries: lists, assoc, apply
  • HTTP: http_client, http_json
  • Load file: :- consult('file.pl').
prolog
% Packages in Prolog
% Using SWI-Prolog packages
% Install packages:
% ?- pack_install('library_name').

% Using library
:- use_module(library(lists)).
:- use_module(library(assoc)).
:- use_module(library(apply)).
:- use_module(library(clpfd)).

% Using JSON
:- use_module(library(http/json)).
% json_read, json_write

% Using HTTP
:- use_module(library(http/http_client)).
% http_get, http_post

% Using CSV
:- use_module(library(csv)).
% csv_read_file, csv_write_file

% Using YAML
:- use_module(library(yaml)).
% yaml_read, yaml_write

% Using ODBC
:- use_module(library(odbc)).
% odbc_connect, odbc_query

% Using Redis
:- use_module(library(redis)).
% redis_connect, redis_get

% Loading a file
:- consult('myfile.pl').

% Usage
% member(X, [1,2,3]).
% maplist(double, [1,2,3], Doubled).
Intermediate
17. How to create plots in Prolog?

Prolog can create plots using the built-in plot library or by generating data for external tools.

  • Plot library: library(plot)
  • Gnuplot: library(gnuplot)
  • Graphviz: library(graphviz)
  • Data export: Generate data files
  • Web-based: Generate HTML
prolog
% Plotting in Prolog
% Using SWI-Prolog's plotting
:- use_module(library(plot)).

% Simple plot
plot_square :-
    X = [1,2,3,4,5,6,7,8,9,10],
    Y = [1,4,9,16,25,36,49,64,81,100],
    plot(X, Y).

% Multiple series
plot_multi :-
    X = [1,2,3,4,5],
    Y1 = [1,4,9,16,25],
    Y2 = [3,5,7,9,11],
    plot(X, [Y1, Y2]).

% Scatter plot
scatter_plot :-
    X = [1,2,3,4,5,6,7,8,9,10],
    Y = [1,4,9,16,25,36,49,64,81,100],
    plot(X, Y, [type(scatter)]).

% Histogram (using SWI-Prolog's histogram)
histogram_plot(Data) :-
    histogram(Data, [binwidth(1)]).

% 3D plot (using gnuplot)
:- use_module(library(gnuplot)).

plot_3d :-
    X = [1,2,3,4,5],
    Y = [1,2,3,4,5],
    Z = [[1,4,9,16,25],
         [4,9,16,25,36],
         [9,16,25,36,49]],
    plot(X, Y, Z).

% Using Graphviz for graph plotting
:- use_module(library(graphviz)).
% graphviz_dot, graphviz_render

% Usage
% plot_square.
Intermediate
18. What are data structures in Prolog?

Prolog provides various data structures through lists, compound terms, and built-in libraries.

  • Stack: Using lists
  • Queue: Using two lists
  • Map: Association lists
  • Set: List with unique elements
  • Tree: Compound terms
prolog
% Data Structures in Prolog
% Lists as data structures

% Stack (using list)
push(Element, Stack, [Element|Stack]).
pop([H|T], H, T).
peek([H|_], H).
is_empty([]).

% Queue (using two lists)
enqueue(Element, Queue, NewQueue) :- 
    append(Queue, [Element], NewQueue).
dequeue([H|T], H, T).

% Map using association list
get_value(Key, [(Key, Value)|_], Value).
get_value(Key, [(K,_)|T], Value) :- 
    Key = K,
    get_value(Key, T, Value).
set_value(Key, Value, List, [(Key, Value)|List]).

% Set using list with unique elements
add_to_set(Element, [], [Element]).
add_to_set(Element, [Element|T], [Element|T]).
add_to_set(Element, [H|T], [H|Result]) :- 
    Element = H,
    add_to_set(Element, T, Result).

% Binary tree
tree_empty(empty).
tree_node(Value, Left, Right).

% Graph using adjacency list
graph_adj(1, [2,3]).
graph_adj(2, [1,4]).
graph_adj(3, [1,4]).
graph_adj(4, [2,3]).

% Usage
% push(1, [], S).
% pop(S, H, T).
Intermediate
19. How to do statistics in Prolog?

Prolog provides statistical functions through arithmetic operations and list processing.

  • Mean: sum + length
  • Median: Sort and find middle
  • Standard deviation: Custom calculation
  • Correlation: Manual calculation
  • Quantiles: Custom implementation
prolog
% Statistics in Prolog
% Basic statistics
mean(Data, Mean) :- 
    sum_list(Data, Sum),
    length(Data, N),
    Mean is Sum / N.

median(Data, Median) :- 
    sort(Data, Sorted),
    length(Sorted, N),
    (N mod 2 =:= 1 ->
        N1 is N // 2,
        nth0(N1, Sorted, Median);
        N1 is N // 2 - 1,
        N2 is N // 2,
        nth0(N1, Sorted, V1),
        nth0(N2, Sorted, V2),
        Median is (V1 + V2) / 2).

variance(Data, Variance) :- 
    mean(Data, Mean),
    sum_of_squares(Data, Mean, SS),
    length(Data, N),
    Variance is SS / N.

sum_of_squares([], _, 0).
sum_of_squares([H|T], Mean, SS) :- 
    sum_of_squares(T, Mean, SS1),
    SS is SS1 + (H - Mean) * (H - Mean).

std_dev(Data, StdDev) :- 
    variance(Data, Var),
    StdDev is sqrt(Var).

correlation(X, Y, Corr) :- 
    length(X, N),
    mean(X, MeanX),
    mean(Y, MeanY),
    covariance(X, Y, MeanX, MeanY, Cov),
    std_dev(X, StdX),
    std_dev(Y, StdY),
    Corr is Cov / (StdX * StdY).

covariance([], [], _, _, 0).
covariance([XH|XT], [YH|YT], MeanX, MeanY, Cov) :- 
    covariance(XT, YT, MeanX, MeanY, Cov1),
    Cov is Cov1 + (XH - MeanX) * (YH - MeanY).

quantile(Data, Q, Value) :- 
    sort(Data, Sorted),
    length(Sorted, N),
    Pos is (N - 1) * Q,
    integer(Pos) ->
        nth0(Pos, Sorted, Value);
        Base is floor(Pos),
        Frac is Pos - Base,
        nth0(Base, Sorted, V1),
        nth0(Base + 1, Sorted, V2),
        Value is V1 + Frac * (V2 - V1).

% Usage
% mean([1,2,3,4,5,6,7,8,9,10], M).
Intermediate
20. How to do linear algebra in Prolog?

Prolog provides linear algebra operations through custom implementations using lists.

  • Matrix multiplication: mat_mul
  • Transpose: transpose
  • Determinant: det
  • Norm: Frobenius norm
  • Identity: identity
prolog
% Linear Algebra in Prolog
% Matrix operations
% Matrix multiplication
mat_mul(A, B, Result) :- 
    transpose(B, BT),
    maplist(row_mul(BT), A, Result).

row_mul(BT, Row, RowResult) :- 
    maplist(dot_product(Row), BT, RowResult).

dot_product(Row, Col, Dot) :- 
    sum_product(Row, Col, 0, Dot).

sum_product([], [], Acc, Acc).
sum_product([H1|T1], [H2|T2], Acc, Dot) :- 
    Acc1 is Acc + H1 * H2,
    sum_product(T1, T2, Acc1, Dot).

% Transpose
transpose([], []).
transpose([[]|_], []).
transpose(Matrix, [Col|Cols]) :- 
    get_column(Matrix, Col, Rest),
    transpose(Rest, Cols).

get_column([], [], []).
get_column([[H|T]|Rows], [H|Col], [T|Rest]) :- 
    get_column(Rows, Col, Rest).

% Determinant
det([[A]], A).
det([[A,B],[C,D]], Det) :- 
    Det is A * D - B * C.
det(Matrix, Det) :- 
    length(Matrix, N),
    N > 2,
    det_helper(Matrix, 1, 0, Det).

det_helper([], _, Acc, Acc).
det_helper([Row|Rows], Sign, Acc, Det) :- 
    det_remove(Matrix, Row, SubMatrix),
    det(SubMatrix, SubDet),
    Acc1 is Acc + Sign * Row * SubDet,
    det_helper(Rows, -Sign, Acc1, Det).

% Identity matrix
identity(N, Matrix) :- 
    length(Matrix, N),
    maplist(identity_row(N), Matrix).

identity_row(N, Row) :- 
    length(Row, N),
    add_identity(Row, 0).

add_identity([], _).
add_identity([H|T], I) :- 
    (H is 1, I = 0 -> true ; H is 0),
    I1 is I + 1,
    add_identity(T, I1).

% Matrix norm (Frobenius)
norm(Matrix, Norm) :- 
    flatten(Matrix, Flat),
    sum_squares(Flat, Sum),
    Norm is sqrt(Sum).

sum_squares([], 0).
sum_squares([H|T], Sum) :- 
    sum_squares(T, Sum1),
    Sum is Sum1 + H * H.

% Usage
% mat_mul([[1,2],[3,4]], [[5,6],[7,8]], R).
Intermediate
21. How to work with dates in Prolog?

Prolog provides date handling through the date library and arithmetic operations.

  • Current: get_time/1
  • Create: date/3
  • Arithmetic: date_add, date_add/3
  • Difference: date_diff
  • Formatting: format_time
prolog
% Dates and Time in Prolog
% Using SWI-Prolog date/time
:- use_module(library(date)).

% Current date and time
current_date(Year, Month, Day) :-
    get_time(Now),
    stamp_date_time(Now, DateTime, local),
    date_time_value(year, DateTime, Year),
    date_time_value(month, DateTime, Month),
    date_time_value(day, DateTime, Day).

% Date creation
create_date(Year, Month, Day, Date) :-
    date(Year, Month, Day, Date).

% Date arithmetic
add_days(Date, Days, NewDate) :-
    date_add(Date, days(Days), NewDate).
add_months(Date, Months, NewDate) :-
    date_add(Date, months(Months), NewDate).

% Date difference
date_diff(Date1, Date2, Days) :-
    date_diff(Date1, Date2, Days).

% Formatting dates
format_date(Date, Format, String) :-
    format_time(String, Format, Date).

% Date functions
date_year(Date, Year) :-
    date_time_value(year, Date, Year).
date_month(Date, Month) :-
    date_time_value(month, Date, Month).
date_day(Date, Day) :-
    date_time_value(day, Date, Day).

% Date range
date_range(Start, End, Dates) :-
    findall(Date, (between_dates(Start, End, Date)), Dates).

between_dates(Start, End, Date) :-
    Start @=< End,
    (Start = End -> Date = Start; 
     date_add(Start, days(1), Next),
     between_dates(Next, End, Date)).

% Timezone handling
current_timezone(Timezone) :-
    get_time(Now),
    stamp_date_time(Now, DateTime, local),
    date_time_value(timezone, DateTime, Timezone).

% Usage
% current_date(Y, M, D).
Intermediate
22. How to use regular expressions in Prolog?

Prolog provides regular expression support through the regex library.

  • Create: regex_compile
  • Match: regex_match
  • Capture groups: regex_match(RE, String, Groups)
  • Replace: regex_replace
  • Split: regex_split
prolog
% Regular Expressions in Prolog
% Using SWI-Prolog regex library
:- use_module(library(regex)).

% Create regex
regex_compile('hello', RE).

% Match
regex_match(RE, 'hello world').

% Find all
regex_matches(RE, 'hello world hello again', Matches).

% Regex with capture groups
regex_compile('(\\d{4})-(\\d{2})-(\\d{2})', DateRE),
regex_match(DateRE, '2024-01-01', Matches).

% Replace with regex
regex_replace('d+', 'NUM', 'Hello 123 World', Replaced).

% Case insensitive
regex_compile('hello', RE, [icase]).
regex_match(RE, 'HELLO world').

% Split with regex
regex_split('[\\s,]+', 'Hello World Prolog', Parts).

% Using string regex functions
string_match('hello', 'hello world').
string_replace('hello world', 'hello', 'hi', Replaced).

% Regex predicates
regex_sub('\\d{3}-\\d{4}', '123-4567').
regex_contains('\\d+', 'abc123def').

% Usage
% regex_match(re('hello'), 'hello world').
Advanced
23. How to do parallel computing in Prolog?

Prolog supports parallel computing through threads, concurrent libraries, and parallel predicates.

  • Threads: thread_create, thread_join
  • Concurrent library: library(concurrent)
  • Parallel map: parallel_maplist
  • Fork/Join: Custom implementation
  • Parallel fold: parallel_fold
prolog
% Parallel Computing in Prolog
% Using SWI-Prolog's concurrent predicates
:- use_module(library(concurrent)).

% Creating threads
thread_create(Task, Thread) :-
    thread_create(Task, Thread).

% Parallel map (using threads)
parallel_map(_, [], []).
parallel_map(F, [H|T], [R|RT]) :-
    thread_create(call(F, H, R), _),
    parallel_map(F, T, RT).

% Using bagof with threads
parallel_findall(Template, Goal, List) :-
    findall(Template, (thread_create(call(Goal), _), true), List).

% Using fork/join
fork_join(Tasks, Results) :-
    maplist(thread_create, Tasks, Threads),
    maplist(thread_join, Threads, Results).

% Parallel fold
parallel_fold(_, [], Acc, Acc).
parallel_fold(F, [H|T], Acc, Result) :-
    thread_create(call(F, H, Acc, Partial), _),
    parallel_fold(F, T, Acc, Result),
    F(Partial, Acc, NewAcc).

% Using SWI-Prolog's concurrent lists
:- use_module(library(concurrent/lists)).

% parallel_maplist/3
parallel_maplist(Goal, List1, List2).

% Parallel reduction
parallel_reduce(Goal, List, Result) :-
    concurrent_mapreduce:mapreduce(Goal, List, Result).

% Usage
% parallel_map(double, [1,2,3,4,5], R).
Advanced
24. What is metaprogramming in Prolog?

Prolog supports metaprogramming through term manipulation, dynamic predicates, and reflection.

  • Term construction: =..
  • Dynamic predicates: assertz, retract
  • call/1: call(Goal)
  • clause/2: Inspect predicates
  • findall/3: findall(X, Goal, List)
prolog
% Metaprogramming in Prolog
% Using call for dynamic predicate calls
call_predicate(Goal) :- call(Goal).

% Using =.. for constructing terms
construct(Goal, Args, Term) :- 
    Term =.. [Goal|Args].

% Dynamic predicate creation
create_predicate(Name, Arity, Body) :-
    functor(Head, Name, Arity),
    assertz((Head :- Body)).

% Using clause/2 for inspecting predicates
list_clauses(Head) :-
    clause(Head, Body).

% Using term_variables/2
get_variables(Term, Vars) :-
    term_variables(Term, Vars).

% Using copy_term/2
copy_term(Term, Copy) :-
    copy_term(Term, Copy).

% Using bagof/3 for collection
collect_all(Goal, List) :-
    bagof(X, Goal, List).

% Using findall/3 for collection
find_all(Goal, List) :-
    findall(X, Goal, List).

% Using forall/2
forall_condition(Condition, Action) :-
    forall(Condition, Action).

% Using var/1 and nonvar/1
is_variable(X) :- var(X).
is_nonvar(X) :- nonvar(X).

% Using ground/1
is_ground(Term) :- ground(Term).

% Usage
% call_predicate(write('Hello')).
% construct(my_predicate, [a,b], Term).
Advanced
25. How to interface with C in Prolog?

Prolog provides foreign function interface for calling C functions and creating extensions.

  • Foreign predicates: foreign/3
  • Load library: load_foreign_library
  • C headers: SWI-Prolog.h
  • PL_foreign: Foreign function API
  • Memory management: PL_get_*, PL_unify_*
prolog
% Interoperability with C in Prolog
% Using foreign function interface (SWI-Prolog)
% Loading C library
:- use_foreign_library(foreign(libm)).

% Defining foreign predicates
foreign(sin, [float], float).
foreign(cos, [float], float).
foreign(sqrt, [float], float).

% Using C functions
sin_value(X, Result) :- sin(X, Result).
cos_value(X, Result) :- cos(X, Result).
sqrt_value(X, Result) :- sqrt(X, Result).

% Foreign with multiple arguments
foreign(pow, [float, float], float).

% Using C strings
foreign(strlen, [string], integer).

% Calling C from Prolog (using PL_foreign)
% In C:
% #include <SWI-Prolog.h>
% foreign_t pl_add(term_t a, term_t b, term_t c) {
%     int A, B, C;
%     if (!PL_get_integer(a, &A)) return FALSE;
%     if (!PL_get_integer(b, &B)) return FALSE;
%     C = A + B;
%     return PL_unify_integer(c, C);
% }

% Loading foreign object
:- load_foreign_library('libmylib.so').

% Using Windows DLL
:- load_foreign_library('mylib.dll').

% Foreign predicate registration
foreign(add, [integer, integer], integer).

% Usage
% sin_value(0.5, Result).
% sqrt_value(9, Result).
Advanced
26. How to optimize performance in Prolog?

Prolog performance can be optimized through tail recursion, cuts, memoization, and indexing.

  • Tail recursion: sum_tail/3
  • Difference lists: append_diff
  • Cuts: !
  • Indexing: :- dynamic predicate/arity
  • Memoization: :- table predicate/arity
prolog
% Performance Optimization in Prolog
% Performance tips

% 1. Use tail recursion
sum_tail([], Acc, Acc).
sum_tail([H|T], Acc, Result) :- 
    Acc1 is Acc + H,
    sum_tail(T, Acc1, Result).

% 2. Use difference lists
append_diff(X-Y, Y-Z, X-Z).
concat_diff(L1, L2, Result) :- 
    append_diff(L1, L2, Result).

% 3. Use cuts to avoid backtracking
factorial(0, 1) :- !.
factorial(N, F) :- 
    N > 0,
    N1 is N - 1,
    factorial(N1, F1),
    F is N * F1.

% 4. Use indexing
member(X, [X|_]) :- !.
member(X, [_|T]) :- member(X, T).

% 5. Use table/1 for memoization (SWI-Prolog)
:- use_module(library(tabling)).

:- table fib/2.
fib(0, 0).
fib(1, 1).
fib(N, F) :- 
    N > 1,
    N1 is N - 1,
    N2 is N - 2,
    fib(N1, F1),
    fib(N2, F2),
    F is F1 + F2.

% 6. Use clause indexing
predicate(A, B) :- A == 1, !, B = 'Case 1'.
predicate(A, B) :- A == 2, !, B = 'Case 2'.
predicate(_, B) :- B = 'Default'.

% 7. Use prolog flags for optimization
:- set_prolog_flag(optimise, true).

% 8. Use compiled predicates
:- dynamic my_predicate/1.
assert_compiled(my_predicate(42)).

% 9. Use memoization with dynamic predicates
:- dynamic cache/2.

memo_fib(0, 0).
memo_fib(1, 1).
memo_fib(N, F) :- 
    N > 1,
    (cache(N, F) -> true;
     N1 is N - 1, memo_fib(N1, F1),
     N2 is N - 2, memo_fib(N2, F2),
     F is F1 + F2,
     assertz(cache(N, F))).

% Usage
% sum_tail([1,2,3,4,5], 0, S).
Advanced
27. How to do networking in Prolog?

Prolog provides networking through HTTP clients, sockets, and WebSocket libraries.

  • HTTP client: http_get, http_post
  • WebSocket: library(websocket)
  • TCP sockets: tcp_connect, tcp_server
  • DNS: dns_lookup
  • HTTP server: http_server
prolog
% Networking in Prolog
% Using SWI-Prolog HTTP client
:- use_module(library(http/http_client)).

% HTTP GET request
http_get(URL, Response) :-
    http_get(URL, Response, []).

% HTTP GET with headers
http_get_with_headers(URL, Headers, Response) :-
    http_get(URL, Response, [headers(Headers)]).

% HTTP POST request
http_post(URL, Data, Response) :-
    http_post(URL, Data, Response, []).

% HTTP POST JSON
http_post_json(URL, JSON, Response) :-
    http_post(URL, json(JSON), Response, [content_type('application/json')]).

% WebSocket client
:- use_module(library(websocket)).

ws_connect(URL, Stream) :-
    ws_connect(URL, Stream, []).

% TCP client
tcp_connect(Host, Port, Stream) :-
    tcp_connect(Host, Port, Stream).

% TCP server
tcp_server(Port, Handler) :-
    tcp_server(Port, Handler).

% Socket creation
create_socket(Stream) :-
    tcp_socket(Stream).

% Socket bind and listen
bind_socket(Stream, Port) :-
    tcp_bind(Stream, Port),
    tcp_listen(Stream, 5).

% Accept connection
accept_connection(Stream, ClientStream) :-
    tcp_accept(Stream, ClientStream).

% Send data
send_data(Stream, Data) :-
    write(Stream, Data).

% Receive data
receive_data(Stream, Data) :-
    read_line_to_string(Stream, Data).

% Usage
% http_get('https://api.github.com', Response).
Advanced
28. How to work with JSON in Prolog?

Prolog provides JSON support through the HTTP library for encoding and decoding JSON.

  • Encode: json_write
  • Decode: json_read
  • Pretty print: json_write(Stream, Term, [width(80)])
  • File: json_write_file, json_read_file
  • Error handling: catch(json_read(...), Error, Handler)
prolog
% Working with JSON in Prolog
% Using SWI-Prolog JSON library
:- use_module(library(http/json)).

% Encode to JSON
encode_json(Term, JSON) :-
    json_write(JSON, Term).

% Encode with pretty print
encode_json_pretty(Term, JSON) :-
    json_write(JSON, Term, [width(80)]).

% Decode from JSON
decode_json(JSON, Term) :-
    json_read(JSON, Term).

% Decode from string
decode_json_string(String, Term) :-
    atom_string(String, Atom),
    json_read(atom(Atom), Term).

% Working with JSON objects
create_json_object(Fields, Object) :-
    json_object(Fields, Object).

% JSON array
create_json_array(Elements, Array) :-
    json_array(Elements, Array).

% Nested JSON
nested_json :-
    User = _{id:1, profile:_{name:'Alice', email:'alice@example.com'}},
    json_write(current_output, User).

% Read JSON from file
read_json_file(File, Term) :-
    open(File, read, Stream),
    json_read(Stream, Term),
    close(Stream).

% Write JSON to file
write_json_file(File, Term) :-
    open(File, write, Stream),
    json_write(Stream, Term),
    close(Stream).

% JSON with custom formatting
format_json(Term) :-
    json_write(current_output, Term, [width(80)]).

% JSON error handling
safe_json_read(Stream, Term) :-
    catch(json_read(Stream, Term), Error, 
          (write('JSON Error: '), write(Error), nl, fail)).

% Usage
% decode_json_string('{"name":"Bob","age":30}', Term).
Advanced
29. How to test code in Prolog?

Prolog testing is done using the PlUnit testing framework.

  • PlUnit: library(plunit)
  • Test suite: begin_tests(name)
  • Test cases: test(name) :- ...
  • Assertions: assertion/1
  • Run tests: run_tests
prolog
% Testing in Prolog
% Using PlUnit (SWI-Prolog)
:- use_module(library(plunit)).

% Basic test
:- begin_tests(math_tests).

test(add) :-
    add(1, 1, Result),
    Result == 2.

test(multiply) :-
    multiply(2, 3, Result),
    Result == 6.

:- end_tests(math_tests).

% Test with floating point
:- begin_tests(float_tests).

test(float_approx) :-
    X is 0.1 + 0.2,
    abs(X - 0.3) < 0.001.

:- end_tests(float_tests).

% Test with exceptions
:- begin_tests(exception_tests).

test(divide_by_zero, [throws(division_by_zero)]) :-
    divide(10, 0).

:- end_tests(exception_tests).

% Test with lists
:- begin_tests(list_tests).

test(length) :-
    length([1,2,3,4,5], 5).

test(member) :-
    member(3, [1,2,3,4,5]).

:- end_tests(list_tests).

% Test with predicates
:- begin_tests(predicate_tests).

test(factorial) :-
    factorial(5, 120).

test(fibonacci) :-
    fibonacci(8, 21).

:- end_tests(predicate_tests).

% Property-based testing
:- begin_tests(property_tests).

test(associative) :-
    forall(between(1, 100, X), 
           (X + 0 =:= X)).

test(commutative) :-
    forall((between(1, 100, A), between(1, 100, B)),
           (A + B =:= B + A)).

:- end_tests(property_tests).

% Running tests
% run_tests.
% run_tests(math_tests).

% Usage
% run_tests.
Advanced
30. How to debug in Prolog?

Prolog provides various debugging tools including trace, spy points, and the GUI debugger.

  • Trace: trace.
  • Spy points: spy(predicate/arity).
  • Debug: debug(module, message)
  • Print: print(Term), nl.
  • GTrace: gtrace.
prolog
% Debugging in Prolog
% Using trace for debugging
% trace.
% predicate.
% notrace.

% Using spy points
% spy(predicate/2).
% nospy(predicate/2).

% Using debug
:- use_module(library(debug)).

debug_message(Message) :-
    debug(debug, Message).

debug_with_value(Variable) :-
    debug(debug, 'Value: ~w', [Variable]).

% Using print for debugging
debug_print(Term) :-
    print(Term), nl.

% Using format for debugging
debug_format(Format, Args) :-
    format(Format, Args).

% Using write for simple debugging
debug_write(Term) :-
    write(Term), nl.

% Using assertions
assertion(Condition) :-
    (Condition -> true;
     throw(error(assertion_failed(Condition), _))).

% Using catch/throw for error handling
safe_call(Goal) :-
    catch(Goal, Error, 
          (write('Error: '), write(Error), nl, fail)).

% Using current_prolog_flag
debugging_state :-
    current_prolog_flag(debug, Debug),
    write('Debug mode: '), write(Debug), nl.

% Using leash
leash([trace, spy, retry, fail, exception]).

% Using gtrace (SWI-Prolog GUI debugger)
gtrace.

% Usage
% trace.
% factorial(5, F).
% notrace.
Advanced
31. What are abstract types in Prolog?

Abstract types in Prolog are defined using predicates that represent type hierarchies and interfaces.

  • Abstract type: animal(Animal)
  • Concrete types: dog(dog(Name, Age))
  • Interface predicates: make_sound(Animal, Sound)
  • Type checking: is_animal/1
  • Polymorphism: Multiple clauses for different types
prolog
% Abstract Types and Interfaces in Prolog
% Defining abstract type using predicates
animal(Animal) :- animal_predicate(Animal).

% Concrete types
dog(dog(Name, Age)).
cat(cat(Name, Age)).
bird(bird(Name, Wingspan)).

% Interface predicates
make_sound(Animal, Sound) :- 
    animal(Animal),
    sound_predicate(Animal, Sound).

% Dog implementation
sound_predicate(dog(_), 'Woof!').

% Cat implementation
sound_predicate(cat(_), 'Meow!').

% Bird implementation
sound_predicate(bird(_), 'Chirp!').

% Interface with abstract type
:- use_module(library(record)).

% Record definition
:- record animal_info(
    name:atom,
    age:integer,
    type:atom
).

% Using records
create_dog(Name, Age, Animal) :-
    animal_info(name(Name), age(Age), type(dog), Animal).

% Type checking
is_animal(animal_info(_, _, _)).

% Predicate for type hierarchy
mammal(Animal) :- animal(Animal), is_mammal(Animal).
is_mammal(dog(_)).
is_mammal(cat(_)).

% Polymorphic predicates
describe_animal(Animal, Description) :-
    animal(Animal),
    (dog(Dog) -> description_dog(Dog, Description);
     cat(Cat) -> description_cat(Cat, Description);
     Description = 'Unknown animal').

description_dog(dog(Name, Age), 
                atom_concat('Dog: ', Name, Desc1),
                atom_concat(Desc1, ', age ', Desc2),
                atom_concat(Desc2, Age, Description)).

description_cat(cat(Name, Age),
                atom_concat('Cat: ', Name, Desc1),
                atom_concat(Desc1, ', age ', Desc2),
                atom_concat(Desc2, Age, Description)).

% Usage
% dog(dog('Rex', 3)).
% make_sound(dog('Rex', 3), Sound).
Advanced
32. What are parameterized types in Prolog?

Prolog uses compound terms and polymorphic predicates for generic programming.

  • Polymorphic types: option(Value)
  • Generic functions: map(Predicate, List1, List2)
  • Type constraints: number(A), number(B)
  • Higher-order: call(Predicate, Arg1, Arg2)
  • GADT: Using compound terms
prolog
% Parameterized Types (Polymorphic) in Prolog
% Using compound terms for parametric types
option(none).
option(some(X)).

% Polymorphic list operations
% Lists are inherently polymorphic

% Generic functions
identity(X, X).

% Polymorphic predicate
length([], 0).
length([_|T], N) :- 
    length(T, N1),
    N is N1 + 1.

% Generic map
map(_, [], []).
map(F, [H|T], [FH|FT]) :- 
    call(F, H, FH),
    map(F, T, FT).

% Generic filter
filter(_, [], []).
filter(P, [H|T], [H|FT]) :- 
    call(P, H),
    filter(P, T, FT).
filter(P, [H|T], FT) :- 
    + call(P, H),
    filter(P, T, FT).

% Generic fold
fold(_, [], Acc, Acc).
fold(F, [H|T], Acc, Result) :- 
    call(F, H, Acc, Acc1),
    fold(F, T, Acc1, Result).

% Type constraints
add_numbers(A, B, C) :- 
    number(A), number(B),
    C is A + B.

% GADT-like using compound terms
expr(int(I)) :- integer(I).
expr(add(X, Y)) :- expr(X), expr(Y).
expr(bool(B)) :- boolean(B).
expr(if(Cond, Then, Else)) :- 
    expr(Cond), expr(Then), expr(Else).

% Evaluation
eval(int(I), I).
eval(add(X, Y), Result) :- 
    eval(X, XV), eval(Y, YV),
    Result is XV + YV.
eval(bool(B), B).
eval(if(Cond, Then, Else), Result) :- 
    eval(Cond, CondV),
    (CondV == true -> eval(Then, Result); eval(Else, Result)).

% Higher-order functions using call
apply(F, Arg, Result) :- 
    call(F, Arg, Result).

% Usage
% map(double, [1,2,3], R).
% eval(add(int(2), add(int(3), int(4))), R).
Advanced
33. What are macros in Prolog?

Prolog supports macros through term expansion and goal expansion for metaprogramming.

  • Term expansion: term_expansion/2
  • Goal expansion: goal_expansion/2
  • Dynamic creation: assertz/1
  • Meta-call: call/1
  • Code generation: findall/3
prolog
% Macros and Metaprogramming in Prolog
% Using term expansion for macros
:- use_module(library(term_expansion)).

% Simple macro example
term_expansion(debug(Goal), (Goal, format('Debug: ~q~n', [Goal]))).

% Usage: debug(write('Hello')).

% Using goal_expansion for optimization
goal_expansion(member(X, List), 
               (member(X, List) -> true)).

% Creating macros with variables
term_expansion(define(Function, Parameters, Body),
               (Function(Parameters) :- Body)).

% Using =.. for dynamic predicate creation
make_predicate(Name, Arity, Body) :-
    functor(Head, Name, Arity),
    assertz((Head :- Body)).

% Using forall for code generation
generate_facts(Data) :-
    forall(member(X, Data), 
           assertz(fact(X))).

% Using findall for collection
collect_clauses(Predicate, Clauses) :-
    findall((Head, Body), clause(Head, Body), Clauses).

% Dynamic predicate manipulation
% assertz(Goal), asserta(Goal)
% retract(Goal), retractall(Goal)

% Metacall predicates
call_with_args(Goal, Args) :-
    Goal =.. [Functor|Args],
    call(Goal).

% Using term expansion for logging
term_expansion(log(Goal), 
               (format('Entering: ~q~n', [Goal]),
                Goal,
                format('Exiting: ~q~n', [Goal]))).

% Usage
% define(add, (A, B), (C is A + B)).
% add(5, 3, Result).
Advanced
34. What are generators in Prolog?

Prolog uses between, findall, and threads for generator-like behavior.

  • Generator: between(1, 10, X)
  • Lazy evaluation: freeze
  • Stream: read/2 with repeat
  • Coroutine: thread_create
  • Producer/Consumer: Using threads
prolog
% Generators and Coroutines in Prolog
% Using between as a generator
generate_numbers(Start, End, N) :- 
    between(Start, End, N).

% Fibonacci generator
fibonacci(0, 0) :- !.
fibonacci(1, 1) :- !.
fibonacci(N, F) :- 
    N > 1,
    N1 is N - 1,
    N2 is N - 2,
    fibonacci(N1, F1),
    fibonacci(N2, F2),
    F is F1 + F2.

% Using freeze for lazy evaluation
lazy_fibonacci(N, F) :- 
    freeze(N, fibonacci(N, F)).

% Generator using difference lists
fibonacci_list(N, List) :-
    N >= 0,
    fib_list(N, [0,1], List).

fib_list(0, _, []).
fib_list(1, [X|_], [X]).
fib_list(N, [X,Y|T], [X|Rest]) :-
    N > 1,
    Z is X + Y,
    N1 is N - 1,
    fib_list(N1, [Y,Z|T], Rest).

% Stream using open/3 and repeat
stream_numbers(Stream, N) :-
    repeat,
    read(Stream, N).

% Coroutine using threads
:- use_module(library(thread)).

coroutine(Goal, Thread) :-
    thread_create(Goal, Thread).

% Producer/Consumer
producer(Data) :- 
    forall(member(X, Data), 
           (write(X), nl, sleep(1))).

consumer :-
    repeat,
    read(X),
    process(X),
    (X == end_of_file -> ! ; fail).

% Lazy list using difference lists
lazy_ints(N, [N|T]) :-
    N1 is N + 1,
    lazy_ints(N1, T).

% Using delay for coroutines
:- use_module(library(delay)).

% delay(Goal) - delays evaluation until needed

% Usage
% lazy_ints(1, List).
Advanced
35. What are advanced list operations in Prolog?

Prolog provides advanced list operations including matrix operations and element-wise transformations.

  • Matrix ops: mat_mul
  • Element-wise: maplist
  • Transpose: transpose
  • Norm: Frobenius norm
  • Trace/Diagonal: Custom functions
prolog
% Advanced List Operations
% List initialization
zeros(0, []).
zeros(N, [0|T]) :- 
    N > 0,
    N1 is N - 1,
    zeros(N1, T).

ones(0, []).
ones(N, [1|T]) :- 
    N > 0,
    N1 is N - 1,
    ones(N1, T).

% Matrix operations
create_matrix(Rows, Cols, Matrix) :-
    length(Matrix, Rows),
    maplist(create_row(Cols), Matrix).

create_row(Cols, Row) :-
    length(Row, Cols).

% Element-wise addition
elementwise_add([], [], []).
elementwise_add([H1|T1], [H2|T2], [H|T]) :-
    H is H1 + H2,
    elementwise_add(T1, T2, T).

% Matrix transpose
transpose_matrix([], []).
transpose_matrix([[]|_], []).
transpose_matrix(Matrix, [Col|Cols]) :-
    get_matrix_column(Matrix, Col, Rest),
    transpose_matrix(Rest, Cols).

get_matrix_column([], [], []).
get_matrix_column([[H|T]|Rows], [H|Col], [T|Rest]) :-
    get_matrix_column(Rows, Col, Rest).

% Matrix multiplication
matrix_multiply(A, B, Result) :-
    transpose_matrix(B, BT),
    maplist(row_multiply(BT), A, Result).

row_multiply(BT, Row, RowResult) :-
    maplist(dot_product(Row), BT, RowResult).

% Matrix norm
matrix_norm(Matrix, Norm) :-
    flatten(Matrix, Flat),
    foldl(square_sum, Flat, 0, Sum),
    Norm is sqrt(Sum).

square_sum(X, Acc, Result) :-
    Result is Acc + X * X.

% Matrix trace
matrix_trace(Matrix, Trace) :-
    diag(Matrix, Diag),
    sum_list(Diag, Trace).

diag([], []).
diag([[H|_]|Rest], [H|DRest]) :-
    diag_without_first(Rest, RestWithoutFirst),
    diag(RestWithoutFirst, DRest).

diag_without_first([], []).
diag_without_first([[_|T]|Rest], [T|DRest]) :-
    diag_without_first(Rest, DRest).

% Usage
% zeros(5, Z).
Advanced
36. How to handle missing data in Prolog?

Prolog handles missing data using special atoms, the option type, or maybe.

  • Undefined: undefined
  • Option type: none or some(Value)
  • Filter: remove_missing/2
  • Default: replace_missing/3
  • Skip: sum_skip_missing/2
prolog
% Working with Missing Data
% Using atom 'undefined' for missing values
data([1, 2, undefined, 4, 5, undefined, 7]).

% Check for missing values
has_missing([]) :- false.
has_missing([undefined|_]) :- true.
has_missing([_|T]) :- has_missing(T).

% Remove missing values
remove_missing([], []).
remove_missing([undefined|T], R) :- 
    remove_missing(T, R).
remove_missing([H|T], [H|R]) :- 
    H = undefined,
    remove_missing(T, R).

% Replace missing values
replace_missing([], _, []).
replace_missing([undefined|T], Default, [Default|R]) :-
    replace_missing(T, Default, R).
replace_missing([H|T], Default, [H|R]) :-
    H = undefined,
    replace_missing(T, Default, R).

% Operations with missing values
add_with_missing([], [], []).
add_with_missing([H1|T1], [H2|T2], [H|R]) :-
    (H1 == undefined ; H2 == undefined ->
        H = undefined
    ;
        H is H1 + H2
    ),
    add_with_missing(T1, T2, R).

% Skip missing values
sum_skip_missing([], 0).
sum_skip_missing([undefined|T], Sum) :-
    sum_skip_missing(T, Sum).
sum_skip_missing([H|T], Sum) :-
    H = undefined,
    sum_skip_missing(T, Sum1),
    Sum is H + Sum1.

% Using maybe/1
maybe(Value) :- nonvar(Value), !.
maybe(_).

% Using option type
option_value(none, Default, Default).
option_value(some(Value), _, Value).

% Missing data in facts
person(name(alice), age(25), city(nyc)).
person(name(bob), age(30), city(unknown)).
person(name(charlie), city(london)).

% Usage
% data(D), remove_missing(D, Clean).
Advanced
37. How to do sorting and searching in Prolog?

Prolog provides built-in sort, predsort, and custom search predicates.

  • Sort: sort/2
  • Custom sort: predsort/3
  • Search: member/2, findall/3
  • Binary search: Custom implementation
  • Contains: member/2
prolog
% Sorting and Searching
% Basic sorting using built-in
sort_list(List, Sorted) :- 
    sort(List, Sorted).

% Sorting with custom comparator
sort_by_length(List, Sorted) :-
    predsort(compare_length, List, Sorted).

compare_length(R, A, B) :-
    length(A, LA),
    length(B, LB),
    (LA < LB -> R = (<) ; LA > LB -> R = (>) ; R = (=)).

% Sorting descending
sort_descending(List, Sorted) :-
    sort(0, List, Sorted).

% Searching
search_greater_than([], _, []).
search_greater_than([H|T], N, [H|R]) :-
    H > N,
    search_greater_than(T, N, R).
search_greater_than([H|T], N, R) :-
    H =< N,
    search_greater_than(T, N, R).

% Find first
find_first([], _, none).
find_first([H|_], N, H) :- H > N.
find_first([_|T], N, Result) :-
    find_first(T, N, Result).

% Find last
find_last(List, N, Result) :-
    findall(X, (member(X, List), X > N), Greater),
    (Greater = [] -> Result = none ; last(Greater, Result)).

% Contains
contains(List, Element) :-
    member(Element, List).

% Binary search
binary_search(List, Element, Index) :-
    length(List, Len),
    binary_search(List, Element, 0, Len, Index).

binary_search(_, _, Low, High, Index) :-
    Low >= High,
    Index = -1.
binary_search(List, Element, Low, High, Index) :-
    Mid is (Low + High) // 2,
    nth0(Mid, List, MidVal),
    (MidVal = Element -> Index = Mid ;
     MidVal < Element -> 
         NewLow is Mid + 1,
         binary_search(List, Element, NewLow, High, Index) ;
         binary_search(List, Element, Low, Mid, Index)).

% Usage
% sort_list([5,2,8,1,9,3], S).
Advanced
38. What are mathematical operations in Prolog?

Prolog provides arithmetic operations and mathematical functions through is/2 and built-in functions.

  • Arithmetic: +, -, *, /, mod
  • Trigonometric: sin, cos, tan
  • Random: random/3
  • Statistics: sum_list, custom stats
  • Complex: 1+2i
prolog
% Mathematical Operations
% Basic arithmetic
math_example :-
    X = 10,
    Y = 3,
    Plus is X + Y,
    Minus is X - Y,
    Times is X * Y,
    Div is X / Y,
    Mod is X mod Y,
    Pow is X ** Y,
    format('X + Y = ~w~n', [Plus]),
    format('X - Y = ~w~n', [Minus]),
    format('X * Y = ~w~n', [Times]),
    format('X / Y = ~w~n', [Div]),
    format('X mod Y = ~w~n', [Mod]),
    format('X ^ Y = ~w~n', [Pow]).

% Mathematical functions
math_functions :-
    Pi is pi,
    format('sin(pi/4) = ~w~n', [sin(Pi / 4)]),
    format('cos(pi/4) = ~w~n', [cos(Pi / 4)]),
    format('tan(pi/4) = ~w~n', [tan(Pi / 4)]),
    format('exp(1) = ~w~n', [exp(1)]),
    format('log(e) = ~w~n', [log(exp(1))]),
    format('log10(100) = ~w~n', [log10(100)]),
    format('sqrt(9) = ~w~n', [sqrt(9)]).

% Special functions
special_functions :-
    format('abs(-5) = ~w~n', [abs(-5)]),
    format('ceil(3.14) = ~w~n', [ceiling(3.14)]),
    format('floor(3.14) = ~w~n', [floor(3.14)]),
    format('round(3.14) = ~w~n', [round(3.14)]),
    format('max(1,3,5,2,4) = ~w~n', [max(1, max(3, max(5, max(2, 4))))]),
    format('min(1,3,5,2,4) = ~w~n', [min(1, min(3, min(5, min(2, 4))))]).

% Random numbers
random_example :-
    random(0, 100, Rand1),
    random(0, 100, Rand2),
    format('Random 1: ~w~n', [Rand1]),
    format('Random 2: ~w~n', [Rand2]).

% Statistics
statistics_example(Data) :-
    sum_list(Data, Sum),
    length(Data, N),
    Mean is Sum / N,
    min_list(Data, Min),
    max_list(Data, Max),
    format('Sum: ~w~n', [Sum]),
    format('Mean: ~w~n', [Mean]),
    format('Min: ~w~n', [Min]),
    format('Max: ~w~n', [Max]).

% Complex numbers (SWI-Prolog)
complex_example :-
    C1 = 1+2i,
    C2 = 3+4i,
    C3 is C1 + C2,
    C4 is C1 * C2,
    format('C1 + C2 = ~w~n', [C3]),
    format('C1 * C2 = ~w~n', [C4]).

% Usage
% math_example.
Advanced
39. How to do data serialization in Prolog?

Prolog provides various serialization methods including term I/O, JSON, XML, and YAML.

  • Term I/O: write_term, read_term
  • JSON: json_write, json_read
  • XML: xml_write, xml_read
  • YAML: yaml_write
  • Portable: write_term with options
prolog
% Data Serialization
% Using term I/O
write_term_file(File, Term) :-
    open(File, write, Stream),
    write_term(Stream, Term, []),
    close(Stream).

read_term_file(File, Term) :-
    open(File, read, Stream),
    read_term(Stream, Term, []),
    close(Stream).

% Using read/write
serialize_to_string(Term, String) :-
    with_output_to(string(String), write_term(Term, [])).

deserialize_from_string(String, Term) :-
    string_to_atom(String, Atom),
    term_string(Term, Atom).

% Using number vars
write_vars(File, Term, Vars) :-
    open(File, write, Stream),
    write_term(Stream, Term, [variable_names(Vars)]),
    close(Stream).

% Using quoted output
write_quoted(Term) :-
    write_term(Term, [quoted(true)]).

% Using JSON serialization
:- use_module(library(http/json)).

to_json(Term, JSON) :-
    json_write(JSON, Term).

from_json(JSON, Term) :-
    json_read(JSON, Term).

% Using XML serialization (SWI-Prolog)
:- use_module(library(http/xml)).

to_xml(Term, XML) :-
    xml_write(XML, Term).

from_xml(XML, Term) :-
    xml_read(XML, Term).

% Using YAML (requires library)
% :- use_module(library(yaml)).
% to_yaml(Term, YAML) :- yaml_write(YAML, Term).

% Portable serialization
portable_write(Term) :-
    write_term(Term, [quoted(true), ignore_ops(false)]).

% Usage
% serialize_to_string(person(name(alice), age(25)), S).
Advanced
40. How to interface with external systems in Prolog?

Prolog can interface with databases, Redis, SOAP services, and execute shell commands.

  • Database: ODBC library
  • SQLite: library(sqlite)
  • Redis: library(redis)
  • SOAP: library(soap/soap_client)
  • Shell: shell/1
prolog
% Interfacing with External Systems
% Database connections (SWI-Prolog ODBC)
:- use_module(library(odbc)).

% Connect to database
db_connect(DSN, User, Pass, Connection) :-
    odbc_connect(DSN, Connection, [user(User), password(Pass)]).

% Query database
db_query(Connection, Query, Rows) :-
    odbc_query(Connection, Query, Rows).

% Executing shell commands
shell_command(Command, Output) :-
    with_output_to(string(Output), shell(Command)).

% Using process (SWI-Prolog)
:- use_module(library(process)).

% Execute with process
process_execute(Command, Output) :-
    process_create(Command, [], [stdout(pipe(Out))]),
    read_string(Out, _, Output).

% HTTP client (SWI-Prolog)
:- use_module(library(http/http_client)).

http_request(Method, URL, Response) :-
    http_request(Method, URL, Response, []).

% SOAP client
:- use_module(library(soap/soap_client)).

% SOAP request
soap_request(WSDL, Method, Input, Output) :-
    soap_client(WSDL, Method, Input, Output).

% RPC (using SWI-Prolog RPC)
:- use_module(library(http/http_jsonrpc)).

% Redis (using SWI-Prolog redis)
:- use_module(library(redis)).

redis_connect(Host, Port, Client) :-
    redis_connect(Host, Port, Client).

% Executing external programs
run_command(Command) :-
    shell(Command).

% Reading environment variables
get_env_var(Name, Value) :-
    getenv(Name, Value).

% Usage
% shell_command('ls -la', Output).
Coding Round
41. Reverse a string (atom)

Reverse an atom by converting to characters, reversing, and converting back.

  • Method: atom_chars, reverse, atom_chars
  • Recursive: reverse_atom_recursive
  • Performance: O(n) time
  • Unicode: Works with characters
prolog
% Reverse a string (atom)
reverse_atom(Atom, Reversed) :-
    atom_chars(Atom, Chars),
    reverse(Chars, RevChars),
    atom_chars(Reversed, RevChars).

reverse_string(String, Reversed) :-
    string_chars(String, Chars),
    reverse(Chars, RevChars),
    string_chars(Reversed, RevChars).

reverse_atom_recursive('', '').
reverse_atom_recursive(Atom, Rev) :-
    atom_chars(Atom, [H|T]),
    atom_chars(T, TailAtom),
    reverse_atom_recursive(TailAtom, TailRev),
    atom_concat(TailRev, H, Rev).

% Usage
% reverse_atom('hello', R).
Coding Round
42. Check palindrome

Check if an atom is a palindrome by comparing it to its reverse.

  • Method: atom_chars(Atom, Chars), reverse(Chars, Rev), Chars == Rev
  • Case insensitive: atom_lower
  • Ignore spaces: delete
  • Recursive: Two-pointer comparison
prolog
% Check palindrome
is_palindrome(Atom) :-
    atom_chars(Atom, Chars),
    reverse(Chars, RevChars),
    Chars = RevChars.

is_palindrome_case_insensitive(Atom) :-
    atom_lower(Atom, Lower),
    is_palindrome(Lower).

is_palindrome_ignore_spaces(Atom) :-
    atom_chars(Atom, Chars),
    delete(Chars, ' ', Clean),
    reverse(Clean, RevClean),
    Clean = RevClean.

is_palindrome_recursive(Atom) :-
    atom_chars(Atom, Chars),
    is_palindrome_chars(Chars).

is_palindrome_chars([]).
is_palindrome_chars([_]).
is_palindrome_chars([H|T]) :-
    last(T, H),
    init(T, Init),
    is_palindrome_chars(Init).

last([X], X).
last([_|T], X) :- last(T, X).

init([], []).
init([_], []).
init([H|T], [H|IT]) :- init(T, IT).

% Usage
% is_palindrome('racecar').
Coding Round
43. Find max in list

Find the maximum value in a list using recursion or fold.

  • Recursive: max_list/2
  • Fold: foldl(max_fold, List, -inf, Max)
  • Built-in: max_list/2
  • Edge cases: Empty list
prolog
% Find max in list
max_list([H|T], Max) :-
    max_list(T, H, Max).

max_list([], Max, Max).
max_list([H|T], Acc, Max) :-
    (H > Acc -> NewAcc = H ; NewAcc = Acc),
    max_list(T, NewAcc, Max).

max_list_fold(List, Max) :-
    foldl(max_fold, List, -inf, Max).

max_fold(X, Acc, Max) :-
    (X > Acc -> Max = X ; Max = Acc).

max_list_manual([H|T], Max) :-
    max_loop(T, H, Max).

max_loop([], Max, Max).
max_loop([H|T], Acc, Max) :-
    Acc1 is max(H, Acc),
    max_loop(T, Acc1, Max).

% Usage
% max_list([1,5,3,9,2], M).
Coding Round
44. Remove duplicates

Remove duplicates from a list using recursion or list_to_set.

  • Recursive: remove_duplicates/2
  • Set: list_to_set(List, Unique)
  • Preserve order: remove_duplicates_ordered/2
  • Time: O(n²) recursion
prolog
% Remove duplicates from list
remove_duplicates([], []).
remove_duplicates([H|T], [H|R]) :-
    delete(T, H, T1),
    remove_duplicates(T1, R).

remove_duplicates_ordered([], []).
remove_duplicates_ordered([H|T], [H|R]) :-
    member(H, T),
    !,
    remove_duplicates_ordered(T, R).
remove_duplicates_ordered([H|T], [H|R]) :-
    remove_duplicates_ordered(T, R).

remove_duplicates_set(List, Unique) :-
    list_to_set(List, Unique).

% Usage
% remove_duplicates([a,b,a,c,b,d], R).
Coding Round
45. Merge lists

Merge two sorted lists using recursion or append.

  • Sorted merge: merge/3
  • Concatenate: append/3
  • Performance: O(n) time
  • Edge cases: Empty lists
prolog
% Merge lists
merge_lists([], L, L).
merge_lists(L, [], L).
merge_lists([H1|T1], [H2|T2], [H1|R]) :-
    H1 =< H2,
    merge_lists(T1, [H2|T2], R).
merge_lists([H1|T1], [H2|T2], [H2|R]) :-
    H1 > H2,
    merge_lists([H1|T1], T2, R).

append_lists(L1, L2, Result) :-
    append(L1, L2, Result).

concat_lists(L1, L2, Result) :-
    concat(L1, L2, Result).

% Usage
% merge_lists([1,3,5,7], [2,4,6,8], R).
Coding Round
46. Convert atom to number

Convert an atom to a number using atom_number/2.

  • Atom to number: atom_number(Atom, Number)
  • Safe conversion: catch(atom_number(...), _, Handler)
  • String to number: string_to_number
  • Error handling: catch
prolog
% Convert atom to number
atom_to_number(Atom, Number) :-
    atom_number(Atom, Number).

atom_to_integer(Atom, Integer) :-
    atom_number(Atom, Integer),
    integer(Integer).

atom_to_float(Atom, Float) :-
    atom_number(Atom, Float),
    float(Float).

string_to_number(String, Number) :-
    string_to_atom(String, Atom),
    atom_number(Atom, Number).

safe_atom_to_number(Atom, Number) :-
    catch(atom_number(Atom, Number), 
          error(_, _), 
          (Number = 0, fail)).

% Usage
% atom_to_number('42', N).
Coding Round
47. Loop through association list

Iterate through an association list using recursion or forall.

  • Recursive: print_assoc/1
  • Forall: forall(member(Key-Value, List), ...)
  • Find key: find_key/3
  • Map: map_assoc/3
prolog
% Loop through association list
print_assoc([]).
print_assoc([Key-Value|T]) :-
    format('~w => ~w~n', [Key, Value]),
    print_assoc(T).

find_key(Key, [Key-Value|_], Value) :- !.
find_key(Key, [_|T], Value) :-
    find_key(Key, T, Value).

iterate_with_foreach(List) :-
    forall(member(Key-Value, List), 
           format('~w => ~w~n', [Key, Value])).

map_assoc(F, [], []).
map_assoc(F, [Key-Value|T], [Key-NewValue|NT]) :-
    call(F, Value, NewValue),
    map_assoc(F, T, NT).

% Usage
% print_assoc([name-alice, age-25, city-nyc]).
Coding Round
48. Delay function execution

Delay execution using sleep or thread_create for async.

  • Blocking: sleep(Seconds)
  • Async: thread_create((sleep(Seconds), Goal), _)
  • Timer: set_timer(Seconds, Goal)
  • Callback: delay_with_callback/3
prolog
% Delay function execution
sleep_seconds(N) :-
    sleep(N).

delay_predicate(Seconds, Goal) :-
    sleep(Seconds),
    call(Goal).

delay_with_callback(Seconds, Goal, Callback) :-
    sleep(Seconds),
    call(Goal, Result),
    call(Callback, Result).

% Async delay using thread
:- use_module(library(thread)).

async_delay(Seconds, Goal) :-
    thread_create((
        sleep(Seconds),
        call(Goal)
    ), _).

% Timer using SWI-Prolog
:- use_module(library(timer)).

timer_delay(Seconds, Goal) :-
    set_timer(Seconds, Goal).

% Usage
% delay_predicate(2, write('After 2 seconds')).
Coding Round
49. HTTP GET request

Make HTTP requests using http_get or http_post.

  • GET: http_get(URL, Response)
  • POST: http_post(URL, Data, Response)
  • Headers: http_get(URL, Response, [headers(Headers)])
  • JSON: http_post(URL, json(JSON), Response)
prolog
% HTTP GET request
:- use_module(library(http/http_client)).

http_get(URL, Response) :-
    http_get(URL, Response, []).

http_get_with_headers(URL, Headers, Response) :-
    http_get(URL, Response, [headers(Headers)]).

http_post(URL, Data, Response) :-
    http_post(URL, Data, Response, []).

http_post_json(URL, JSON, Response) :-
    http_post(URL, json(JSON), Response, [content_type('application/json')]).

fetch_github :-
    http_get('https://api.github.com', Response),
    format('Response: ~w~n', [Response]).

% Usage
% fetch_github.
Coding Round
50. Create a promise-like task

Create promise-like behavior using threads with message queues.

  • Promise: create_promise/2
  • Thread: thread_create/2
  • Message: thread_send_message/2
  • Wait: thread_get_message/2
prolog
% Create a promise-like task
:- use_module(library(thread)).

% Promise using thread with message queue
create_promise(ShouldResolve, Result) :-
    thread_create((
        sleep(1),
        (ShouldResolve -> 
            thread_send_message(main, success('Success!')) ;
            thread_send_message(main, error('Failed!'))),
        thread_exit(0)
    ), _),
    thread_get_message(main, Result).

% Promise with delay
create_delayed_promise(Delay, ShouldResolve, Result) :-
    thread_create((
        sleep(Delay),
        (ShouldResolve -> 
            thread_send_message(main, success('Success!')) ;
            thread_send_message(main, error('Failed!'))),
        thread_exit(0)
    ), _),
    thread_get_message(main, Result).

% Promise chain
chain_promises(P1, P2, Result) :-
    thread_create((
        thread_get_message(main, Result1),
        format('First: ~w~n', [Result1]),
        thread_get_message(main, Result2),
        format('Second: ~w~n', [Result2]),
        Result = (Result1, Result2)
    ), _).

% Usage
% create_promise(true, Result).
Coding Round
51. Factorial

Calculate factorial using recursion, tail recursion, or iteration.

  • Recursive: factorial(0, 1). factorial(N, F) :- ...
  • Tail recursive: factorial_tail(N, F)
  • Iterative: factorial_iterative(N, F)
  • Edge cases: 0! = 1
prolog
% Factorial
factorial(0, 1).
factorial(N, F) :-
    N > 0,
    N1 is N - 1,
    factorial(N1, F1),
    F is N * F1.

factorial_tail(N, F) :-
    factorial_tail(N, 1, F).

factorial_tail(0, Acc, Acc).
factorial_tail(N, Acc, F) :-
    N > 0,
    N1 is N - 1,
    Acc1 is Acc * N,
    factorial_tail(N1, Acc1, F).

factorial_iterative(N, F) :-
    factorial_iterative(1, 1, N, F).

factorial_iterative(I, Acc, N, Acc) :- I > N.
factorial_iterative(I, Acc, N, F) :-
    I =< N,
    Acc1 is Acc * I,
    I1 is I + 1,
    factorial_iterative(I1, Acc1, N, F).

% Usage
% factorial(5, F).
Coding Round
52. Fibonacci

Calculate Fibonacci numbers using recursion, iteration, or memoization.

  • Recursive: fibonacci(N, F)
  • Tail recursive: fibonacci_tail(N, F)
  • Memoized: fib_memo/2
  • Time: O(2^n) recursive, O(n) iterative
prolog
% Fibonacci
fibonacci(0, 0).
fibonacci(1, 1).
fibonacci(N, F) :-
    N > 1,
    N1 is N - 1,
    N2 is N - 2,
    fibonacci(N1, F1),
    fibonacci(N2, F2),
    F is F1 + F2.

fibonacci_tail(N, F) :-
    fibonacci_tail(N, 0, 1, F).

fibonacci_tail(0, _, Acc, Acc).
fibonacci_tail(N, A, B, F) :-
    N > 0,
    C is A + B,
    N1 is N - 1,
    fibonacci_tail(N1, B, C, F).

% Fibonacci with memoization
:- dynamic fib_memo/2.

fib_memo(0, 0).
fib_memo(1, 1).
fib_memo(N, F) :-
    N > 1,
    (fib_memo(N, F) -> true ;
     N1 is N - 1, fib_memo(N1, F1),
     N2 is N - 2, fib_memo(N2, F2),
     F is F1 + F2,
     assertz(fib_memo(N, F))).

% Usage
% fibonacci(10, F).
Coding Round
53. FizzBuzz

Print numbers with FizzBuzz logic using conditional predicates.

  • If-else: Using -> operator
  • Multiple clauses: fizzbuzz_value/2
  • List: fizzbuzz_list/2
  • Output: write/1
prolog
% FizzBuzz
fizzbuzz(N) :-
    between(1, N, I),
    fizzbuzz_number(I),
    fail.
fizzbuzz(_).

fizzbuzz_number(I) :-
    I mod 15 =:= 0,
    !,
    write('FizzBuzz'), nl.
fizzbuzz_number(I) :-
    I mod 3 =:= 0,
    !,
    write('Fizz'), nl.
fizzbuzz_number(I) :-
    I mod 5 =:= 0,
    !,
    write('Buzz'), nl.
fizzbuzz_number(I) :-
    write(I), nl.

% FizzBuzz using findall
fizzbuzz_list(N, List) :-
    findall(Result, 
            (between(1, N, I),
             fizzbuzz_value(I, Result)),
            List).

fizzbuzz_value(I, 'FizzBuzz') :- I mod 15 =:= 0.
fizzbuzz_value(I, 'Fizz') :- I mod 3 =:= 0.
fizzbuzz_value(I, 'Buzz') :- I mod 5 =:= 0.
fizzbuzz_value(I, I) :- 
    I mod 3 == 0,
    I mod 5 == 0.

% Usage
% fizzbuzz(15).
Coding Round
54. Find missing number

Find missing number using sum formula or XOR operation.

  • Sum: total - sum
  • XOR: xor_range ^ xor_list
  • Time: O(n)
  • Edge cases: Empty list
prolog
% Find missing number
find_missing(List, Missing) :-
    length(List, N),
    N1 is N + 1,
    sum_list(List, Sum),
    Total is N1 * (N1 + 1) // 2,
    Missing is Total - Sum.

find_missing_xor(List, Missing) :-
    length(List, N),
    N1 is N + 1,
    xor_range(1, N1, XOR1),
    xor_list(List, XOR2),
    Missing is XOR1 xor XOR2.

xor_range(Start, End, XOR) :-
    xor_range(Start, End, 0, XOR).

xor_range(Start, End, Acc, XOR) :-
    Start > End,
    XOR is Acc.
xor_range(Start, End, Acc, XOR) :-
    Start =< End,
    Acc1 is Acc xor Start,
    Start1 is Start + 1,
    xor_range(Start1, End, Acc1, XOR).

xor_list([], 0).
xor_list([H|T], XOR) :-
    xor_list(T, XOR1),
    XOR is H xor XOR1.

% Usage
% find_missing([1,2,4,5,6], M).
Coding Round
55. Find duplicates

Find duplicates using member, count, or sorting.

  • Count: count_member/3
  • Findall: findall(X, (member(X, List), count > 1), Dups)
  • Sorted: find_duplicates_sorted/2
  • Time: O(n²) or O(n log n)
prolog
% Find duplicates
find_duplicates(List, Duplicates) :-
    findall(X, (member(X, List), count_member(List, X, Count), Count > 1), Duplicates).

count_member([], _, 0).
count_member([H|T], X, Count) :-
    (H == X -> Count is 1 + Count1 ; Count = Count1),
    count_member(T, X, Count1).

find_duplicates_unique(List, Duplicates) :-
    findall(X, (member(X, List), count_member(List, X, Count), Count > 1), All),
    sort(All, Duplicates).

find_duplicates_sorted(List, Duplicates) :-
    sort(List, Sorted),
    find_duplicates_sorted(Sorted, Duplicates).

find_duplicates_sorted([], []).
find_duplicates_sorted([X,X|T], [X|R]) :-
    find_duplicates_sorted(T, R).
find_duplicates_sorted([_|T], R) :-
    find_duplicates_sorted(T, R).

% Usage
% find_duplicates([1,2,3,2,4,3,5,6,5], D).
Coding Round
56. Sum of list

Sum list elements using recursion, tail recursion, or fold.

  • Recursive: sum_list([], 0). sum_list([H|T], S) :- ...
  • Tail recursive: sum_list_tail(List, Sum)
  • Fold: foldl(plus, List, 0, Sum)
  • Empty: Returns 0
prolog
% Sum of list
sum_list([], 0).
sum_list([H|T], Sum) :-
    sum_list(T, Sum1),
    Sum is H + Sum1.

sum_list_tail(List, Sum) :-
    sum_list_tail(List, 0, Sum).

sum_list_tail([], Acc, Acc).
sum_list_tail([H|T], Acc, Sum) :-
    Acc1 is Acc + H,
    sum_list_tail(T, Acc1, Sum).

sum_list_fold(List, Sum) :-
    foldl(plus, List, 0, Sum).

% Usage
% sum_list([1,2,3,4,5], S).
Coding Round
57. Average of list

Calculate average by dividing sum by length.

  • Method: sum_list(List, Sum), length(List, N), Avg is Sum / N
  • Integer: Avg is Sum // N
  • Empty: Return 0
  • Float: Avg is Sum / N
prolog
% Average of list
average_list(List, Avg) :-
    sum_list(List, Sum),
    length(List, N),
    Avg is Sum / N.

average_list_float(List, Avg) :-
    sum_list(List, Sum),
    length(List, N),
    Avg is Sum / N.

average_integer(List, Avg) :-
    sum_list(List, Sum),
    length(List, N),
    Avg is Sum // N.

% Usage
% average_list([1,2,3,4,5], A).
Coding Round
58. Sort list ascending

Sort lists using sort or custom quick sort.

  • Built-in: sort(List, Sorted)
  • Quick sort: sort_ascending_manual
  • Time: O(n log n)
  • Preserve duplicates: Use sort
prolog
% Sort list ascending
sort_ascending(List, Sorted) :-
    sort(List, Sorted).

sort_ascending_manual([], []).
sort_ascending_manual([X], [X]).
sort_ascending_manual([H|T], Sorted) :-
    partition(H, T, Small, Big),
    sort_ascending_manual(Small, SortedSmall),
    sort_ascending_manual(Big, SortedBig),
    append(SortedSmall, [H|SortedBig], Sorted).

partition(_, [], [], []).
partition(Pivot, [H|T], [H|Small], Big) :-
    H =< Pivot,
    partition(Pivot, T, Small, Big).
partition(Pivot, [H|T], Small, [H|Big]) :-
    H > Pivot,
    partition(Pivot, T, Small, Big).

% Usage
% sort_ascending([5,2,8,1,9,3], S).
Coding Round
59. Sort list descending

Sort descending using sort and reverse or custom quick sort.

  • Built-in: sort(List, Sorted1), reverse(Sorted1, Sorted)
  • Manual: sort_descending_manual/2
  • Time: O(n log n)
  • Preserve duplicates: Use sort
prolog
% Sort list descending
sort_descending(List, Sorted) :-
    sort(List, Sorted1),
    reverse(Sorted1, Sorted).

sort_descending_manual([], []).
sort_descending_manual([X], [X]).
sort_descending_manual([H|T], Sorted) :-
    partition_desc(H, T, Small, Big),
    sort_descending_manual(Small, SortedSmall),
    sort_descending_manual(Big, SortedBig),
    append(SortedBig, [H|SortedSmall], Sorted).

partition_desc(_, [], [], []).
partition_desc(Pivot, [H|T], [H|Small], Big) :-
    H > Pivot,
    partition_desc(Pivot, T, Small, Big).
partition_desc(Pivot, [H|T], Small, [H|Big]) :-
    H =< Pivot,
    partition_desc(Pivot, T, Small, Big).

% Usage
% sort_descending([5,2,8,1,9,3], S).
Coding Round
60. Flatten nested list

Flatten nested lists using recursion or flatten.

  • Recursive: flatten_list/2
  • One level: append/2
  • Depth: Handle arbitrary depth
  • Time: O(n)
prolog
% Flatten nested list
flatten_list([], []).
flatten_list([H|T], Flat) :-
    flatten_list(H, FlatH),
    flatten_list(T, FlatT),
    append(FlatH, FlatT, Flat).
flatten_list(H, [H]) :-
    not(is_list(H)).

flatten_depth(List, Flat) :-
    flatten_depth(List, [], Flat).

flatten_depth([], Acc, Acc).
flatten_depth([H|T], Acc, Flat) :-
    flatten_depth(H, Acc, Acc1),
    flatten_depth(T, Acc1, Flat).
flatten_depth(H, Acc, [H|Acc]) :-
    not(is_list(H)).

flatten_one_level(List, Flat) :-
    append(List, Flat).

% Usage
% flatten_list([[1,2],[3,4,5],[6],[7,8,9,10]], F).
Coding Round
61. Chunk list

Split a list into chunks of specified size using recursion.

  • Recursive: chunk_list/3
  • Length-based: chunk_list_length/4
  • Use case: Batch processing
  • Time: O(n)
prolog
% Chunk list
chunk_list(List, Size, Chunks) :-
    chunk_list(List, Size, [], Chunks).

chunk_list([], _, Acc, [Acc]).
chunk_list(List, Size, Acc, Chunks) :-
    length(Acc, Len),
    (Len = Size -> 
        Chunks = [Acc|Rest],
        chunk_list(List, Size, [], Rest)
    ;
        List = [H|T],
        append(Acc, [H], NewAcc),
        chunk_list(T, Size, NewAcc, Chunks)
    ).

chunk_list_length(List, Size, Chunks) :-
    length(List, Len),
    chunk_list_length(List, Size, Len, 0, [], Chunks).

chunk_list_length(_, _, Len, Len, Acc, [Acc]).
chunk_list_length([], _, _, _, Acc, [Acc]).
chunk_list_length([H|T], Size, Len, Index, Acc, Chunks) :-
    NewIndex is Index + 1,
    append(Acc, [H], NewAcc),
    (NewIndex mod Size =:= 0 ->
        Chunks = [NewAcc|Rest],
        chunk_list_length(T, Size, Len, NewIndex, [], Rest)
    ;
        chunk_list_length(T, Size, Len, NewIndex, NewAcc, Rest)
    ).

% Usage
% chunk_list([1,2,3,4,5,6,7,8,9,10], 3, C).
Coding Round
63. Quick sort

Implement quick sort with partitioning and recursion.

  • Recursive: quick_sort/2
  • In-place: quick_sort_array/2
  • Pivot: First element
  • Time: O(n log n) average
prolog
% Quick sort
quick_sort([], []).
quick_sort([Pivot|Rest], Sorted) :-
    partition_quick(Pivot, Rest, Small, Big),
    quick_sort(Small, SortedSmall),
    quick_sort(Big, SortedBig),
    append(SortedSmall, [Pivot|SortedBig], Sorted).

partition_quick(_, [], [], []).
partition_quick(Pivot, [H|T], [H|Small], Big) :-
    H =< Pivot,
    partition_quick(Pivot, T, Small, Big).
partition_quick(Pivot, [H|T], Small, [H|Big]) :-
    H > Pivot,
    partition_quick(Pivot, T, Small, Big).

% In-place quick sort (using array)
quick_sort_array(Array, Sorted) :-
    length(Array, N),
    quick_sort_array(Array, 0, N-1),
    Sorted = Array.

quick_sort_array(_, Low, High) :-
    Low >= High.
quick_sort_array(Array, Low, High) :-
    partition_array(Array, Low, High, PivotIndex),
    quick_sort_array(Array, Low, PivotIndex - 1),
    quick_sort_array(Array, PivotIndex + 1, High).

partition_array(Array, Low, High, PivotIndex) :-
    nth0(High, Array, Pivot),
    I is Low - 1,
    partition_array_loop(Array, Low, High, Pivot, I, PivotIndex).

partition_array_loop(Array, J, High, Pivot, I, PivotIndex) :-
    J < High,
    nth0(J, Array, Value),
    (Value =< Pivot ->
        I1 is I + 1,
        swap(Array, I1, J),
        J1 is J + 1,
        partition_array_loop(Array, J1, High, Pivot, I1, PivotIndex)
    ;
        J1 is J + 1,
        partition_array_loop(Array, J1, High, Pivot, I, PivotIndex)
    ).
partition_array_loop(Array, High, High, Pivot, I, PivotIndex) :-
    PivotIndex is I + 1,
    swap(Array, PivotIndex, High).

swap(Array, I, J) :-
    nth0(I, Array, VI),
    nth0(J, Array, VJ),
    replace(Array, I, VJ, Array1),
    replace(Array1, J, VI, Array).

replace([_|T], 0, X, [X|T]).
replace([H|T], I, X, [H|R]) :-
    I > 0,
    I1 is I - 1,
    replace(T, I1, X, R).

% Usage
% quick_sort([5,3,8,4,2,7,1,6], S).
Coding Round
64. Merge sort

Implement merge sort using divide and conquer with merge.

  • Divide: split_at/4
  • Merge: merge/3
  • In-place: merge_sort_array/2
  • Time: O(n log n)
prolog
% Merge sort
merge_sort([], []).
merge_sort([X], [X]).
merge_sort(List, Sorted) :-
    length(List, N),
    N > 1,
    Mid is N // 2,
    split_at(List, Mid, Left, Right),
    merge_sort(Left, SortedLeft),
    merge_sort(Right, SortedRight),
    merge(SortedLeft, SortedRight, Sorted).

split_at(List, 0, [], List).
split_at([H|T], N, [H|Left], Right) :-
    N > 0,
    N1 is N - 1,
    split_at(T, N1, Left, Right).

merge([], L, L).
merge(L, [], L).
merge([H1|T1], [H2|T2], [H1|R]) :-
    H1 =< H2,
    merge(T1, [H2|T2], R).
merge([H1|T1], [H2|T2], [H2|R]) :-
    H1 > H2,
    merge([H1|T1], T2, R).

% In-place merge sort using array
merge_sort_array(Array, Sorted) :-
    length(Array, N),
    copy_term(Array, Temp),
    merge_sort_array(Array, 0, N-1, Temp),
    Sorted = Array.

merge_sort_array(Array, Low, High, Temp) :-
    Low < High,
    Mid is (Low + High) // 2,
    merge_sort_array(Array, Low, Mid, Temp),
    merge_sort_array(Array, Mid + 1, High, Temp),
    merge_arrays(Array, Low, Mid, High, Temp).

merge_arrays(Array, Low, Mid, High, Temp) :-
    copy_part(Array, Low, High, Temp),
    merge_loop(Array, Low, Mid, High, Low, Low, Temp).

merge_loop(_, Low, Mid, High, Low, K, _) :-
    Low > Mid,
    K > High.
merge_loop(Array, Low, Mid, High, I, J, Temp) :-
    I =< Mid,
    J > High,
    nth0(I, Temp, Value),
    replace(Array, K, Value, Array1),
    I1 is I + 1,
    K1 is K + 1,
    merge_loop(Array1, Low, Mid, High, I1, J, K1, Temp).

% Usage
% merge_sort([5,3,8,4,2,7,1,6], S).
Coding Round
65. Bubble sort

Implement bubble sort with optimization to stop early if no swaps occur.

  • Basic: bubble_sort/2
  • Optimized: bubble_sort_optimized/2
  • Time: O(n²) worst case
  • Use case: Small datasets
prolog
% Bubble sort
bubble_sort(List, Sorted) :-
    copy_term(List, Sorted1),
    bubble_sort_loop(Sorted1, Sorted).

bubble_sort_loop(List, Sorted) :-
    bubble_pass(List, List1, Swapped),
    (Swapped = false -> Sorted = List ;
     bubble_sort_loop(List1, Sorted)).

bubble_pass([], [], false).
bubble_pass([X], [X], false).
bubble_pass([X,Y|T], [X|Rest], Swapped) :-
    X =< Y,
    bubble_pass([Y|T], Rest, Swapped).
bubble_pass([X,Y|T], [Y|Rest], true) :-
    X > Y,
    bubble_pass([X|T], Rest, Swapped).

% Bubble sort optimized
bubble_sort_optimized(List, Sorted) :-
    copy_term(List, Sorted1),
    bubble_sort_optimized_loop(Sorted1, 0, Sorted).

bubble_sort_optimized_loop(List, I, Sorted) :-
    length(List, N),
    I >= N - 1,
    Sorted = List.
bubble_sort_optimized_loop(List, I, Sorted) :-
    length(List, N),
    I < N - 1,
    bubble_pass_optimized(List, 0, N - I - 1, List1, Swapped),
    (Swapped = false -> Sorted = List1 ;
     I1 is I + 1,
     bubble_sort_optimized_loop(List1, I1, Sorted)).

bubble_pass_optimized([], _, _, [], false).
bubble_pass_optimized([X], _, _, [X], false).
bubble_pass_optimized([X,Y|T], J, N, [X|Rest], Swapped) :-
    J < N,
    X =< Y,
    J1 is J + 1,
    bubble_pass_optimized([Y|T], J1, N, Rest, Swapped).
bubble_pass_optimized([X,Y|T], J, N, [Y|Rest], true) :-
    J < N,
    X > Y,
    J1 is J + 1,
    bubble_pass_optimized([X|T], J1, N, Rest, Swapped).
bubble_pass_optimized([X,Y|T], J, N, [X,Y|T], false) :-
    J >= N.

% Usage
% bubble_sort([5,3,8,4,2,7,1,6], S).
Coding Round
66. Intersection of lists

Find intersection using member or set operations.

  • Member: intersection/3
  • Set: intersection_set/3
  • Multiple: intersection_multiple/2
  • Time: O(n*m)
prolog
% Intersection of lists
intersection([], _, []).
intersection([H|T], L2, [H|R]) :-
    member(H, L2),
    intersection(T, L2, R).
intersection([H|T], L2, R) :-
    not(member(H, L2)),
    intersection(T, L2, R).

intersection_set(L1, L2, Inter) :-
    list_to_set(L1, Set1),
    list_to_set(L2, Set2),
    intersection(Set1, Set2, Inter).

intersection_multiple([], []).
intersection_multiple([L|Lists], Inter) :-
    intersection_all(L, Lists, Inter).

intersection_all(L, [], L).
intersection_all(L, [H|T], Inter) :-
    intersection(L, H, Inter1),
    intersection_all(Inter1, T, Inter).

% Usage
% intersection([1,2,3,4,5], [4,5,6,7,8], I).
Coding Round
67. Union of lists

Union lists using member or set operations.

  • Member: union/3
  • Set: union_set/3
  • Multiple: union_multiple/2
  • Time: O(n*m)
prolog
% Union of lists
union([], L, L).
union([H|T], L2, [H|R]) :-
    not(member(H, L2)),
    union(T, L2, R).
union([H|T], L2, R) :-
    member(H, L2),
    union(T, L2, R).

union_set(L1, L2, Union) :-
    list_to_set(L1, Set1),
    list_to_set(L2, Set2),
    union(Set1, Set2, Union).

union_multiple(Lists, Union) :-
    union_all(Lists, []).

union_all([], Acc, Acc).
union_all([H|T], Acc, Union) :-
    union(H, Acc, Acc1),
    union_all(T, Acc1, Union).

% Usage
% union([1,2,3,4], [4,5,6,7], U).
Coding Round
68. Difference of lists

Find difference using member or symmetric difference.

  • Member: difference/3
  • Symmetric: symmetric_difference/3
  • Multiple: difference_multiple/2
  • Time: O(n*m)
prolog
% Difference of lists
difference(L1, L2, Diff) :-
    difference(L1, L2, [], Diff).

difference([], _, Acc, Acc).
difference([H|T], L2, Acc, Diff) :-
    not(member(H, L2)),
    difference(T, L2, [H|Acc], Diff).
difference([H|T], L2, Acc, Diff) :-
    member(H, L2),
    difference(T, L2, Acc, Diff).

symmetric_difference(L1, L2, Diff) :-
    difference(L1, L2, D1),
    difference(L2, L1, D2),
    append(D1, D2, Diff).

difference_multiple(Lists, Diff) :-
    difference_all(Lists, []).

difference_all([], Acc, Acc).
difference_all([H|T], Acc, Diff) :-
    difference(H, Acc, Acc1),
    difference_all(T, Acc1, Diff).

% Usage
% difference([1,2,3,4,5], [4,5,6,7,8], D).
Coding Round
69. Group by property

Group facts by a property using findall and setof.

  • Group: findall(Key-Values, ..., Group)
  • By age: group_by_age/1
  • By city: group_by_city/1
  • Generic: group_by/2
prolog
% Group by property
% Using facts for people
person(1, alice, 25, nyc).
person(2, bob, 30, la).
person(3, charlie, 25, nyc).
person(4, david, 35, chicago).
person(5, eve, 30, la).

% Group by age
group_by_age(Group) :-
    findall(Key, person(_, _, Age, _), AgeList),
    list_to_set(AgeList, Ages),
    findall(Age-Names, 
            (member(Age, Ages), 
             findall(Name, person(_, Name, Age, _), Names)),
            Group).

% Group by city
group_by_city(Group) :-
    findall(Key, person(_, _, _, City), CityList),
    list_to_set(CityList, Cities),
    findall(City-Names, 
            (member(City, Cities), 
             findall(Name, person(_, Name, _, City), Names)),
            Group).

% Group by property (generic)
group_by(Predicate, Group) :-
    findall(Key, (call(Predicate, _, _, Key)), KeyList),
    list_to_set(KeyList, Keys),
    findall(Key-Values, 
            (member(Key, Keys), 
             findall(Value, (call(Predicate, Value, _, Key)), Values)),
            Group).

% Usage
% group_by_age(G).
Coding Round
70. Deep clone

Create deep copies of terms using copy_term or recursive copying.

  • Built-in: copy_term(Term, Clone)
  • Manual: deep_clone/2
  • Lists: Recursive copy
  • Records: deep_clone_record/2
prolog
% Deep clone
deep_clone(Term, Clone) :-
    copy_term(Term, Clone).

deep_clone_list([], []).
deep_clone_list([H|T], [CH|CT]) :-
    deep_clone(H, CH),
    deep_clone_list(T, CT).

deep_clone_term(Term, Clone) :-
    (atomic(Term) -> Clone = Term ;
     compound(Term) -> 
         Term =.. [Functor|Args],
         deep_clone_list(Args, CloneArgs),
         Clone =.. [Functor|CloneArgs]).

% Deep clone for records
deep_clone_record(Record, Clone) :-
    record(Record, Fields),
    deep_clone_fields(Fields, CloneFields),
    record(Clone, CloneFields).

deep_clone_fields([], []).
deep_clone_fields([Field-Value|T], [Field-CloneValue|CT]) :-
    deep_clone(Value, CloneValue),
    deep_clone_fields(T, CT).

% Usage
% deep_clone(person(name(alice), age(25)), Clone).
Coding Round
71. Immutable update

Perform immutable updates on nested data structures using path-based updates.

  • Method: update_immutable/4
  • Path: Dot notation
  • Recursive: Helper function
  • Use case: State management
prolog
% Immutable update
update_immutable(List, Path, Value, NewList) :-
    split_path(Path, Parts),
    update_path(List, Parts, Value, NewList).

split_path(Path, Parts) :-
    split_string(Path, '.', '', Parts).

update_path(List, [], Value, Value) :-
    !.
update_path(List, [Key|Rest], Value, NewList) :-
    update_field(List, Key, Rest, Value, NewList).

update_field([], Key, Rest, Value, [Key-NewValue]) :-
    update_path([], Rest, Value, NewValue).
update_field([Key-Existing|T], Key, Rest, Value, [Key-NewValue|T]) :-
    update_path(Existing, Rest, Value, NewValue).
update_field([H|T], Key, Rest, Value, [H|NT]) :-
    H = Key-_,
    update_field(T, Key, Rest, Value, NT).

% Usage
% update_immutable([user-[name-alice, age-25]], 'user.age', 26, NewState).
Coding Round
72. Pipe function

Implement pipe function for left-to-right function composition.

  • Method: pipe/3
  • Implementation: Recursive
  • Direction: Left to right
  • Use case: Function chaining
prolog
% Pipe function
pipe(Value, [], Value).
pipe(Value, [F|Fs], Result) :-
    call(F, Value, Intermediate),
    pipe(Intermediate, Fs, Result).

% Compose functions
compose(F, G, X, Result) :-
    call(G, X, Intermediate),
    call(F, Intermediate, Result).

compose_list([], X, X).
compose_list([F|Fs], X, Result) :-
    compose_list(Fs, X, Intermediate),
    call(F, Intermediate, Result).

% Pipe operator style
pipe_op(X, F, Result) :-
    call(F, X, Result).

% Usage
% double(X, Y) :- Y is X * 2.
% add_ten(X, Y) :- Y is X + 10.
% square(X, Y) :- Y is X * X.
% pipe(5, [double, add_ten, square], R).
Coding Round
73. Compose function

Implement compose function for right-to-left function composition.

  • Method: compose/4
  • Implementation: compose_list/3
  • Direction: Right to left
  • Use case: Function composition
prolog
% Compose function
compose(F, G, X, Result) :-
    call(G, X, Intermediate),
    call(F, Intermediate, Result).

compose_list([], X, X).
compose_list([F|Fs], X, Result) :-
    compose_list(Fs, X, Intermediate),
    call(F, Intermediate, Result).

compose_right(F, G, X, Result) :-
    call(F, X, Intermediate),
    call(G, Intermediate, Result).

% Usage
% double(X, Y) :- Y is X * 2.
% add_ten(X, Y) :- Y is X + 10.
% square(X, Y) :- Y is X * X.
% compose(square, add_ten, 5, R).
Coding Round
74. Memoization

Implement memoization to cache predicate results based on arguments.

  • Method: memoize/3
  • Cache: memo_cache
  • Limit: memoize_limit/4
  • Clear: clear_cache/0
prolog
% Memoization
:- dynamic memo_cache/2.

memoize(F, X, Result) :-
    (memo_cache(F:X, Result) -> true ;
     call(F, X, Result),
     asserta(memo_cache(F:X, Result))).

% Memoize Fibonacci
fib_memo(0, 0).
fib_memo(1, 1).
fib_memo(N, F) :-
    N > 1,
    (memo_cache(fib_memo:N, F) -> true ;
     N1 is N - 1, fib_memo(N1, F1),
     N2 is N - 2, fib_memo(N2, F2),
     F is F1 + F2,
     asserta(memo_cache(fib_memo:N, F))).

% Memoize with limit
:- dynamic memo_cache_limit/3.

memoize_limit(F, X, Result, Limit) :-
    (memo_cache_limit(F:X, Result, _) -> true ;
     call(F, X, Result),
     current_cache_count(Count),
     (Count >= Limit -> clear_cache ; true),
     asserta(memo_cache_limit(F:X, Result, Count))).

current_cache_count(Count) :-
    findall(_, memo_cache_limit(_, _, _), List),
    length(List, Count).

clear_cache :-
    retractall(memo_cache_limit(_, _, _)).

% Usage
% fib_memo(35, F).
Coding Round
75. Once function

Implement once function that ensures a predicate is called only once.

  • Method: once_function/3
  • Flag: called/2
  • Reset: reset_once/0
  • Result: once_cache/2
prolog
% Once function
once_function(F, X, Result) :-
    (called(F, _) -> 
        (once_cache(F, Result) -> true)
    ;
        call(F, X, Result),
        asserta(called(F, X)),
        asserta(once_cache(F, Result))
    ).

:- dynamic called/2.
:- dynamic once_cache/2.

% Reset once function
reset_once :-
    retractall(called(_, _)),
    retractall(once_cache(_, _)).

% Once with reset
once_with_reset(F, X, Result) :-
    (called(F, _) -> 
        (once_cache(F, Result) -> true)
    ;
        call(F, X, Result),
        asserta(called(F, X)),
        asserta(once_cache(F, Result))
    ).

reset_function(F) :-
    retractall(called(F, _)),
    retractall(once_cache(F, _)).

% Usage
% once_function(initialize, 10, Result).
Coding Round
76. Debounce with leading edge

Implement debounce with leading edge execution using timers.

  • Method: debounce_leading/3
  • State: last_call/2
  • Timer: thread_create/2
  • Use case: Rate limiting
prolog
% Debounce with leading edge
:- use_module(library(thread)).

debounce_leading(F, Delay, X) :-
    get_time(Now),
    (last_call(F, Last) ->
        (Now - Last < Delay ->
            (timeout(F, _) -> 
                true
            ;
                thread_create((
                    sleep(Delay - (Now - Last)),
                    retractall(timeout(F, _)),
                    asserta(last_call(F, Now)),
                    call(F, X)
                ), _),
                asserta(timeout(F, _))
            )
        ;
            retractall(last_call(F, _)),
            asserta(last_call(F, Now)),
            call(F, X)
        )
    ;
        asserta(last_call(F, Now)),
        call(F, X)
    ).

:- dynamic last_call/2.
:- dynamic timeout/1.

% Usage
% debounce_leading(process, 2, Value).
Coding Round
77. Throttle with leading edge

Implement throttle with leading edge execution based on time since last call.

  • Method: throttle_leading/3
  • State: last_call/2
  • Pending: pending/1
  • Trailing: throttle_trailing/3
prolog
% Throttle with leading edge
throttle_leading(F, Delay, X) :-
    get_time(Now),
    (last_call(F, Last) ->
        (Now - Last >= Delay ->
            retractall(last_call(F, _)),
            asserta(last_call(F, Now)),
            call(F, X)
        ;
            true
        )
    ;
        asserta(last_call(F, Now)),
        call(F, X)
    ).

:- dynamic last_call/2.

% Throttle with trailing
throttle_trailing(F, Delay, X) :-
    get_time(Now),
    (last_call(F, Last) ->
        (Now - Last >= Delay ->
            retractall(last_call(F, _)),
            asserta(last_call(F, Now)),
            call(F, X)
        ;
            (pending(F, _) ->
                true
            ;
                thread_create((
                    sleep(Delay - (Now - Last)),
                    retractall(pending(F, _)),
                    retractall(last_call(F, _)),
                    asserta(last_call(F, Now)),
                    call(F, X)
                ), _),
                asserta(pending(F, _))
            )
        )
    ;
        asserta(last_call(F, Now)),
        call(F, X)
    ).

:- dynamic pending/1.

% Usage
% throttle_leading(process, 2, Value).
Coding Round
78. Deep equal

Implement deep equality comparison for nested structures.

  • Method: deep_equal/2
  • Primitive: ==
  • Lists: Recursive compare
  • Records: deep_equal_record/2
prolog
% Deep equal
deep_equal(X, X).

deep_equal_list([], []).
deep_equal_list([H1|T1], [H2|T2]) :-
    deep_equal(H1, H2),
    deep_equal_list(T1, T2).

deep_equal_term(T1, T2) :-
    (atomic(T1) -> T1 = T2 ;
     compound(T1), compound(T2) ->
         T1 =.. [F1|A1],
         T2 =.. [F2|A2],
         F1 = F2,
         deep_equal_list(A1, A2)).

% Deep equal for records
deep_equal_record(R1, R2) :-
    record(R1, Fields1),
    record(R2, Fields2),
    deep_equal_fields(Fields1, Fields2).

deep_equal_fields([], []).
deep_equal_fields([F1-V1|T1], [F2-V2|T2]) :-
    F1 = F2,
    deep_equal(V1, V2),
    deep_equal_fields(T1, T2).

% Usage
% deep_equal([1,2,3], [1,2,3]).
Coding Round
79. Observable pattern

Implement observable pattern with subscription and notification.

  • Observable: observable/2
  • Subscribe: subscribe/3
  • Notify: notify/2
  • Stateful: create_stateful_observable/2
prolog
% Observable pattern
:- use_module(library(thread)).

% Create observable
create_observable(Observable) :-
    Observable = observable([], _).

% Subscribe
subscribe(observable(Subscribers, State), Callback, observable([Callback|Subscribers], State)).

% Unsubscribe
unsubscribe(observable(Subscribers, State), Callback, observable(NewSubscribers, State)) :-
    delete(Subscribers, Callback, NewSubscribers).

% Notify
notify(observable(Subscribers, State), Data) :-
    forall(member(Callback, Subscribers), 
           (thread_create(call(Callback, Data), _))).

% Stateful observable
create_stateful_observable(InitialState, observable([], InitialState)).

get_state(observable(_, State), State).

set_state(observable(Subscribers, _), NewState, observable(Subscribers, NewState)) :-
    notify(observable(Subscribers, NewState), NewState).

% Usage
% create_observable(Obs),
% subscribe(Obs, my_callback, Obs1),
% notify(Obs1, 'Hello World').
Coding Round
80. Singleton pattern

Implement singleton pattern using dynamic predicates.

  • Instance: singleton_instance/1
  • Get: get_singleton/1
  • Data: singleton_data/2
  • Set: set_singleton_data/2
prolog
% Singleton pattern
:- dynamic singleton_instance/1.

singleton(Class, Instance) :-
    (singleton_instance(Instance) -> true ;
     create_instance(Class, Instance),
     asserta(singleton_instance(Instance))).

create_instance(Class, Instance) :-
    call(Class, Instance).

% Singleton class
singleton_class(instance(data([]))).

% Get singleton
get_singleton(Instance) :-
    singleton(singleton_class, Instance).

% Singleton with data
:- dynamic singleton_data/2.

get_singleton_data(Key, Value) :-
    get_singleton(instance(Data)),
    (memberchk(Key-Value, Data) -> true).

set_singleton_data(Key, Value) :-
    get_singleton(instance(Data)),
    (memberchk(Key-_, Data) ->
        delete(Data, Key-_, Data1),
        NewData = [Key-Value|Data1]
    ;
        NewData = [Key-Value|Data]
    ),
    retractall(singleton_instance(_)),
    asserta(singleton_instance(instance(NewData))).

% Usage
% get_singleton(Instance).
% set_singleton_data(name, 'Alice').
Coding Round
81. Factory pattern

Implement factory pattern for creating objects without specifying concrete classes.

  • Factory: create_user/3
  • Types: admin, guest, regular
  • Validation: create_user_valid/3
  • Permissions: create_user_permissions/4
prolog
% Factory pattern
% User types
user(admin(Name)).
user(guest(Name)).
user(regular(Name)).

create_user(Type, Name, User) :-
    (Type = admin -> User = admin(Name) ;
     Type = guest -> User = guest(Name) ;
     User = regular(Name)).

% User factory
user_factory(admin, Name, admin(Name)).
user_factory(guest, Name, guest(Name)).
user_factory(regular, Name, regular(Name)).

% Factory with validation
create_user_valid(Type, Name, User) :-
    valid_type(Type),
    user_factory(Type, Name, User).

valid_type(admin).
valid_type(guest).
valid_type(regular).

% Factory with permissions
create_user_permissions(Type, Name, Permissions, User) :-
    (Type = admin -> 
        User = admin(Name, Permissions) ;
     Type = guest -> 
        User = guest(Name, []) ;
     User = regular(Name, [])).

% Usage
% create_user(admin, 'Alice', User).
Coding Round
82. Strategy pattern

Implement strategy pattern with interchangeable payment methods.

  • Strategy: payment_strategy/2
  • Context: payment_context/2
  • Set: set_strategy/2
  • Execute: execute_payment/2
prolog
% Strategy pattern
% Payment strategies
payment_strategy(credit_card, Amount) :-
    format('Paid ~w with Credit Card~n', [Amount]).
payment_strategy(paypal, Amount) :-
    format('Paid ~w with PayPal~n', [Amount]).
payment_strategy(crypto, Amount) :-
    format('Paid ~w with Crypto~n', [Amount]).

% Payment context
payment_context(Strategy, Amount) :-
    payment_strategy(Strategy, Amount).

% Set strategy
set_strategy(Context, Strategy) :-
    retractall(payment_context(Context, _)),
    asserta(payment_context(Context, Strategy)).

% Execute payment
execute_payment(Context, Amount) :-
    payment_context(Context, Strategy),
    payment_strategy(Strategy, Amount).

% With discount
payment_with_discount(Strategy, Amount, Discount) :-
    Discounted is Amount * (1 - Discount),
    format('Applied discount of ~w%~n', [Discount * 100]),
    payment_strategy(Strategy, Discounted).

% Usage
% payment_context(context, credit_card),
% execute_payment(context, 100).
Coding Round
83. Observer pattern

Implement observer pattern with subject and observer predicates.

  • Subject: create_subject/1
  • Observer: attach_observer/2
  • Notify: set_subject_state/1
  • Derived: create_derived_observer/3
prolog
% Observer pattern
:- dynamic observer/2.

% Create subject
create_subject(InitialState) :-
    retractall(subject_state(_)),
    asserta(subject_state(InitialState)).

% Attach observer
attach_observer(Id, Callback) :-
    asserta(observer(Id, Callback)).

% Detach observer
detach_observer(Id) :-
    retractall(observer(Id, _)).

% Notify observers
notify_observers(Data) :-
    forall(observer(_, Callback), call(Callback, Data)).

% Set state
set_subject_state(NewState) :-
    retractall(subject_state(_)),
    asserta(subject_state(NewState)),
    notify_observers(NewState).

% Get state
get_subject_state(State) :-
    subject_state(State).

% Derived observer
create_derived_observer(Id, Transform, Callback) :-
    Callback = (Data -> 
                  call(Transform, Data, Transformed),
                  write('Derived: '), write(Transformed), nl),
    attach_observer(Id, Callback).

% Usage
% create_subject(0),
% attach_observer(1, (X -> write('Observer1: '), write(X), nl)),
% set_subject_state(10).
Coding Round
84. Decorator pattern

Implement decorator pattern for adding features to coffee.

  • Component: coffee/3
  • Decorators: milk_decorator/3, sugar_decorator/3
  • Chaining: apply_decorators/3
  • Cost: Cost is InnerCost + addition
prolog
% Decorator pattern
% Coffee interface
coffee(basic, 5.0, 'Coffee').

coffee_decorator(Inner, Cost, Description) :-
    coffee(Inner, InnerCost, InnerDesc),
    Cost is InnerCost,
    Description = InnerDesc.

% Milk decorator
milk_decorator(Inner, Cost, Description) :-
    coffee_decorator(Inner, InnerCost, InnerDesc),
    Cost is InnerCost + 2.0,
    Description = InnerDesc + ', Milk'.

% Sugar decorator
sugar_decorator(Inner, Cost, Description) :-
    coffee_decorator(Inner, InnerCost, InnerDesc),
    Cost is InnerCost + 1.0,
    Description = InnerDesc + ', Sugar'.

% Caramel decorator
caramel_decorator(Inner, Cost, Description) :-
    coffee_decorator(Inner, InnerCost, InnerDesc),
    Cost is InnerCost + 2.5,
    Description = InnerDesc + ', Caramel'.

% Whipped cream decorator
whipped_decorator(Inner, Cost, Description) :-
    coffee_decorator(Inner, InnerCost, InnerDesc),
    Cost is InnerCost + 1.5,
    Description = InnerDesc + ', Whipped Cream'.

% Apply decorators
apply_decorators(Inner, [], Inner).
apply_decorators(Inner, [D|Ds], Result) :-
    call(D, Inner, Intermediate),
    apply_decorators(Intermediate, Ds, Result).

% Usage
% coffee(basic, Cost, Desc),
% milk_decorator(basic, Cost1, Desc1).
Coding Round
85. Command pattern

Implement command pattern with execute, undo, and redo operations.

  • Command: add_command/3, subtract_command/3
  • History: command_history/1
  • Execute: execute_command/1
  • Undo/Redo: undo_last/0, redo_last/0
prolog
% Command pattern
:- dynamic command_history/1.
:- dynamic command_redo/1.

% Command interface
command(execute, Cmd) :- call(Cmd).
command(undo, Cmd) :- undo_cmd(Cmd).
command(redo, Cmd) :- redo_cmd(Cmd).

% Add command
add_command(Receiver, Value, Command) :-
    Command = add(Receiver, Value).

execute(add(Receiver, Value)) :-
    atom_concat('_', Receiver, VarName),
    current_prolog_flag(VarName, Old),
    New is Old + Value,
    set_prolog_flag(VarName, New).

undo(add(Receiver, Value)) :-
    atom_concat('_', Receiver, VarName),
    current_prolog_flag(VarName, Old),
    New is Old - Value,
    set_prolog_flag(VarName, New).

redo(add(Receiver, Value)) :-
    execute(add(Receiver, Value)).

% Subtract command
subtract_command(Receiver, Value, Command) :-
    Command = subtract(Receiver, Value).

execute(subtract(Receiver, Value)) :-
    atom_concat('_', Receiver, VarName),
    current_prolog_flag(VarName, Old),
    New is Old - Value,
    set_prolog_flag(VarName, New).

undo(subtract(Receiver, Value)) :-
    atom_concat('_', Receiver, VarName),
    current_prolog_flag(VarName, Old),
    New is Old + Value,
    set_prolog_flag(VarName, New).

redo(subtract(Receiver, Value)) :-
    execute(subtract(Receiver, Value)).

% Command history
execute_command(Cmd) :-
    call(execute, Cmd),
    assertz(command_history(Cmd)).

undo_last :-
    command_history(Cmd),
    retract(command_history(Cmd)),
    call(undo, Cmd),
    assertz(command_redo(Cmd)).

redo_last :-
    command_redo(Cmd),
    retract(command_redo(Cmd)),
    call(redo, Cmd),
    assertz(command_history(Cmd)).

% Usage
% set_prolog_flag(_counter, 0),
% add_command('counter', 5, Cmd),
% execute_command(Cmd).
Coding Round
86. Memento pattern

Implement memento pattern for state capture and restoration.

  • Originator: originator/1
  • Memento: mementos/1
  • Caretaker: save_memento/1
  • Undo/Redo: undo_last/0, redo_last/0
prolog
% Memento pattern
:- dynamic originator_state/1.
:- dynamic mementos/1.

% Originator
originator(State) :-
    originator_state(State).

set_originator_state(State) :-
    retractall(originator_state(_)),
    asserta(originator_state(State)).

% Save state
save_state(State) :-
    originator(State).

% Restore state
restore_state(State) :-
    set_originator_state(State).

% Caretaker
save_memento(Memento) :-
    assertz(mementos(Memento)).

undo_last :-
    mementos(Last),
    retract(mementos(Last)),
    restore_state(Last).

redo_last :-
    (mementos(Last) -> 
        restore_state(Last),
        assertz(mementos(Last))
    ;
        true
    ).

% Usage
% originator(0),
% save_memento(0),
% set_originator_state(1),
% save_memento(1),
% undo_last.
Coding Round
87. Mediator pattern

Implement mediator pattern for centralized communication between colleagues.

  • Mediator: mediator/1
  • Colleague: colleague/2
  • Register: register_colleague/2
  • Send: send_message/2
prolog
% Mediator pattern
:- dynamic colleague/2.
:- dynamic mediator/1.

% Create mediator
create_mediator :-
    retractall(mediator(_)),
    asserta(mediator([])).

% Register colleague
register_colleague(Name, Receiver) :-
    mediator(Colleagues),
    retract(mediator(Colleagues)),
    asserta(mediator([Name-Receiver|Colleagues])),
    asserta(colleague(Name, Receiver)).

% Send message
send_message(Message, Sender) :-
    mediator(Colleagues),
    forall(member(Name-Receiver, Colleagues),
           (Name = Sender -> call(Receiver, Message) ; true)).

% Create colleague
create_colleague(Name, Receiver) :-
    Receiver = (Message -> format('~w received: ~w~n', [Name, Message])),
    register_colleague(Name, Receiver).

% Usage
% create_mediator,
% create_colleague('Alice', _),
% create_colleague('Bob', _),
% send_message('Hello!', 'Alice').
Coding Round
88. Chain of Responsibility

Implement chain of responsibility with linked handlers.

  • Handler: handler_chain/1
  • Chain: set_next/2
  • Processing: handler/2
  • Concrete: auth_handler/1, logger_handler/1
prolog
% Chain of Responsibility
:- dynamic handler_chain/1.

% Handler
handler(next, Handler) :- handler_chain(Handler).
handler(handle, Handler, Request) :- 
    call(Handler, Request).
handler(handle, Handler, Request) :-
    handler_chain(Handler, Next),
    handler(handle, Next, Request).

% Create handler
create_handler(HandleFunc, Handler) :-
    Handler = handle_func(HandleFunc).

set_next(Handler, Next) :-
    retractall(handler_chain(Handler)),
    asserta(handler_chain(Handler, Next)).

% Auth handler
auth_handler(Request) :-
    memberchk(token, Request),
    !,
    format('Authentication passed~n').
auth_handler(_) :-
    format('Authentication failed~n').

% Logger handler
logger_handler(Request) :-
    memberchk(url, Request, URL),
    format('Logging request: ~w~n', [URL]).

% Validation handler
validation_handler(Request) :-
    memberchk(data, Request),
    !,
    format('Validation passed~n').
validation_handler(_) :-
    format('Validation failed~n').

% Usage
% create_handler(auth_handler, Auth),
% create_handler(logger_handler, Logger),
% create_handler(validation_handler, Validator),
% set_next(Auth, Logger),
% set_next(Logger, Validator),
% handler(handle, Auth, [token-valid]).
Coding Round
89. State pattern

Implement state pattern with context and state transitions.

  • State: state/1
  • Context: context/1
  • Transitions: ready_state/0, processing_state/0
  • Handle: handle_state/0
prolog
% State pattern
:- dynamic state/1.

% Context
context(State) :- state(State).

set_state(NewState) :-
    retractall(state(_)),
    asserta(state(NewState)).

% States
ready_state :-
    format('Ready: Waiting for input~n'),
    set_state(processing_state).

processing_state :-
    format('Processing: Working on task~n'),
    set_state(completed_state).

completed_state :-
    format('Completed: Task finished~n'),
    set_state(ready_state).

error_state :-
    format('Error: Something went wrong~n'),
    set_state(ready_state).

% Handle state
handle_state :-
    state(State),
    call(State).

% Stateful context
stateful_context(State) :-
    context(State),
    set_state_data(last_state, State).

% State data
:- dynamic state_data/2.

set_state_data(Key, Value) :-
    retractall(state_data(Key, _)),
    asserta(state_data(Key, Value)).

get_state_data(Key, Value) :-
    state_data(Key, Value).

% Usage
% set_state(ready_state),
% handle_state.
Coding Round
90. Proxy pattern

Implement proxy pattern for access control and lazy initialization.

  • Real subject: real_subject/2
  • Proxy: proxy/2
  • Logging: logging_proxy/2
  • Auth: auth_proxy/2
prolog
% Proxy pattern
% Real subject
real_subject(Request, Response) :-
    Response = 'RealSubject: Handling request'.

% Proxy
proxy(Request, Response) :-
    (cached_subject(_) -> 
        format('Proxy: Using cached real subject~n')
    ;
        format('Proxy: Creating real subject~n'),
        asserta(cached_subject(_))
    ),
    real_subject(Request, Response).

:- dynamic cached_subject/1.

% Logging proxy
logging_proxy(Request, Response) :-
    format('Logging: Request started~n'),
    real_subject(Request, Response),
    format('Logging: Request completed~n').

% Auth proxy
auth_proxy(Request, Response) :-
    (authenticate -> 
        format('Auth: Access granted~n'),
        real_subject(Request, Response)
    ;
        format('Auth: Access denied~n'),
        Response = 'Unauthorized'
    ).

authenticate.

% Usage
% proxy(request, Response).
Coding Round
91. Flyweight pattern

Implement flyweight pattern for sharing objects to save memory.

  • Flyweight: flyweight/2
  • Factory: get_flyweight/2
  • Cache: flyweight_cache/2
  • Operation: operation/3
prolog
% Flyweight pattern
:- dynamic flyweight_cache/2.

% Flyweight
flyweight(SharedState, Flyweight) :-
    Flyweight = flyweight(SharedState).

operation(Flyweight, UniqueState, Result) :-
    Flyweight = flyweight(SharedState),
    Result = ['Shared: ', SharedState, ', Unique: ', UniqueState].

% Flyweight factory
get_flyweight(SharedState, Flyweight) :-
    (flyweight_cache(SharedState, Flyweight) -> true ;
     flyweight(SharedState, NewFlyweight),
     asserta(flyweight_cache(SharedState, NewFlyweight)),
     Flyweight = NewFlyweight).

% Usage
% get_flyweight('state1', F1),
% get_flyweight('state1', F2),
% operation(F1, 'unique1', R).
Coding Round
92. Bridge pattern

Implement bridge pattern for separating abstraction from implementation.

  • Implementation: implementation/2
  • Abstraction: abstraction/2
  • Extended: extended_abstraction/2
  • Alternative: alternative_abstraction/2
prolog
% Bridge pattern
% Implementation
implementation(A, Operation) :-
    Operation = 'ConcreteImplementationA: Operation'.
implementation(B, Operation) :-
    Operation = 'ConcreteImplementationB: Operation'.

% Abstraction
abstraction(Impl, Operation) :-
    implementation(Impl, ImplOp),
    Operation = ['Abstraction: Additional logic - ', ImplOp].

% Extended abstraction
extended_abstraction(Impl, Operation) :-
    implementation(Impl, ImplOp),
    Operation = ['Extended: More logic - ', ImplOp].

% Alternative abstraction
alternative_abstraction(Impl, Operation) :-
    implementation(Impl, ImplOp),
    Operation = ['Alternative: Different logic - ', ImplOp].

% Usage
% abstraction(A, Op).
Coding Round
93. Adapter pattern

Implement adapter pattern for converting interfaces.

  • Target: target/2
  • Adaptee: adaptee/2
  • Adapter: adapter/2
  • Logging: logging_adapter/2
prolog
% Adapter pattern
% Target
target(Request, Response) :-
    Response = 'Target: Request'.

% Adaptee
adaptee(Request, Response) :-
    Response = 'Adaptee: Specific Request'.

% Adapter
adapter(Request, Response) :-
    adaptee(Request, Response).

% Logging adapter
logging_adapter(Request, Response) :-
    format('Adapter: Logging request~n'),
    adaptee(Request, Response).

% Usage
% target(request, R1),
% adapter(request, R2).
Coding Round
94. Facade pattern

Implement facade pattern for simplifying complex subsystems.

  • Subsystems: subsystem_a/1, subsystem_b/1
  • Facade: facade/2
  • Simple: simplified_facade/1
  • Complex: facade(complex, Operation)
prolog
% Facade pattern
% Subsystems
subsystem_a(Operation) :-
    Operation = 'SubsystemA: Operation'.

subsystem_b(Operation) :-
    Operation = 'SubsystemB: Operation'.

subsystem_c(Operation) :-
    Operation = 'SubsystemC: Operation'.

% Facade
facade(simple, Operation) :-
    subsystem_a(Operation).

facade(complex, Operation) :-
    subsystem_a(A),
    subsystem_b(B),
    subsystem_c(C),
    Operation = [A, '
', B, '
', C].

% Simplified facade
simplified_facade(Operation) :-
    facade(simple, Operation).

% Usage
% facade(simple, Op).
Coding Round
95. Composite pattern

Implement composite pattern for tree structures.

  • Component: component/3
  • Leaf: create_leaf/2
  • Composite: create_composite/3
  • Operation: component_operation/2
prolog
% Composite pattern
% Component
component(leaf, Name, Operation) :-
    Operation = ['Leaf ', Name, ': Operation'].

component(composite, Name, Children, Operation) :-
    component_operation(Children, ChildOps),
    Operation = ['Composite ', Name, ': Operation
', ChildOps].

component_operation([], '').
component_operation([C|Cs], Operation) :-
    component(C, _, Op1),
    component_operation(Cs, Op2),
    Operation = [Op1, '
', Op2].

% Create leaf
create_leaf(Name, leaf(Name)).

% Create composite
create_composite(Name, Children, composite(Name, Children)).

% Add child
add_child(composite(Name, Children), Child, composite(Name, [Child|Children])).

% Remove child
remove_child(composite(Name, Children), Child, composite(Name, NewChildren)) :-
    delete(Children, Child, NewChildren).

% Count leaves
count_leaves(leaf(_), 1).
count_leaves(composite(_, Children), Count) :-
    sum_leaves(Children, 0, Count).

sum_leaves([], Acc, Acc).
sum_leaves([H|T], Acc, Count) :-
    count_leaves(H, C),
    Acc1 is Acc + C,
    sum_leaves(T, Acc1, Count).

% Usage
% create_leaf('A', L1),
% create_composite('Comp1', [L1], Comp).
Coding Round
96. Visitor pattern

Implement visitor pattern for adding operations to objects.

  • Visitor: visitor/3
  • Element: element/3
  • Accept: accept/3
  • Counting: visitor(counting, ...)
prolog
% Visitor pattern
% Elements
element(A, Data, element_a(Data)).
element(B, Data, element_b(Data)).

% Visitor
visitor(concrete, element_a(Data), Result) :-
    Result = ['Visiting ElementA: ', Data].
visitor(concrete, element_b(Data), Result) :-
    Result = ['Visiting ElementB: ', Data].

% Counting visitor
visitor(counting, element_a(Data), Result) :-
    retract(a_count(C)),
    C1 is C + 1,
    asserta(a_count(C1)),
    Result = ['Visiting ElementA (', C1, '): ', Data].
visitor(counting, element_b(Data), Result) :-
    retract(b_count(C)),
    C1 is C + 1,
    asserta(b_count(C1)),
    Result = ['Visiting ElementB (', C1, '): ', Data].

:- dynamic a_count/1, b_count/1.
a_count(0).
b_count(0).

% Extended visitor
visitor(extended, element_a(Data), Result) :-
    Result = ['Extended: ', Data, ' (A)'].
visitor(extended, element_b(Data), Result) :-
    Result = ['Extended: ', Data, ' (B)'].

% Accept
accept(Element, Visitor, Result) :-
    visitor(Visitor, Element, Result).

% Usage
% element(A, 'Hello', E),
% accept(E, concrete, R).
Coding Round
97. Iterator pattern

Implement iterator pattern for sequential access.

  • Iterator: iterator/2
  • Reverse: reverse_iterator/2
  • Filter: filtered_iterator/3
  • Skip: skip_iterator/3
prolog
% Iterator pattern
:- dynamic iterator/2.

% Create iterator
create_iterator(Collection, Iterator) :-
    Iterator = iterator(Collection, 0).

% Has next
has_next(iterator(Collection, Index)) :-
    length(Collection, Len),
    Index < Len.

% Next
next(iterator(Collection, Index), Next, iterator(Collection, NewIndex)) :-
    nth0(Index, Collection, Next),
    NewIndex is Index + 1.

% Reverse iterator
create_reverse_iterator(Collection, Iterator) :-
    length(Collection, Len),
    Iterator = reverse_iterator(Collection, Len - 1).

has_next(reverse_iterator(Collection, Index)) :-
    Index >= 0.

next(reverse_iterator(Collection, Index), Next, reverse_iterator(Collection, NewIndex)) :-
    nth0(Index, Collection, Next),
    NewIndex is Index - 1.

% Filtered iterator
create_filtered_iterator(Collection, Predicate, Iterator) :-
    findall(X, (member(X, Collection), call(Predicate, X)), Filtered),
    create_iterator(Filtered, Iterator).

% Skip iterator
create_skip_iterator(Collection, N, Iterator) :-
    length(Collection, Len),
    N < Len,
    drop(N, Collection, Rest),
    create_iterator(Rest, Iterator).

drop(0, L, L).
drop(N, [_|T], R) :-
    N > 0,
    N1 is N - 1,
    drop(N1, T, R).

% Usage
% create_iterator([a,b,c,d,e], Iter),
% has_next(Iter).
Coding Round
98. Template Method pattern

Implement template method with customizable steps.

  • Template: template_method/0
  • Steps: step1/0, step2/0, step3/0
  • Logging: logging_template/0
  • Data: data_template/1
prolog
% Template Method pattern
% Template
template_method :-
    step1,
    step2,
    step3.

% Default steps
step1 :- format('Step 1~n').
step2 :- format('Step 2~n').
step3 :- format('Step 3~n').

% Logging template
logging_template :-
    step1_logging,
    step2_logging,
    step3_logging.

step1_logging :-
    step1,
    format('Logging: Step 1~n').
step2_logging :-
    step2,
    format('Logging: Step 2~n').
step3_logging :-
    step3,
    format('Logging: Step 3~n').

% Data processing template
data_template(Data) :-
    data_step1(Data),
    data_step2(Data),
    data_step3(Data).

data_step1(Data) :-
    format('Processing data: ~w - Step 1~n', [Data]).
data_step2(Data) :-
    format('Processing data: ~w - Step 2~n', [Data]).
data_step3(Data) :-
    format('Processing data: ~w - Step 3~n', [Data]).

% Usage
% template_method.
Coding Round
99. Builder pattern

Implement builder pattern for constructing complex objects.

  • Builder: build_step_a/0, build_step_b/0
  • Director: build_minimal/0, build_full/0
  • Product: product_parts/1
  • Result: get_result/1
prolog
% Builder pattern
:- dynamic product_parts/1.

% Product
create_product :-
    retractall(product_parts(_)),
    asserta(product_parts([])).

add_part(Part) :-
    product_parts(Parts),
    retract(product_parts(Parts)),
    asserta(product_parts([Part|Parts])).

list_parts :-
    product_parts(Parts),
    reverse(Parts, Ordered),
    format('~w~n', [Ordered]).

% Builder
reset_builder :-
    create_product.

build_step_a :-
    add_part('Part A').

build_step_b :-
    add_part('Part B').

build_step_c :-
    add_part('Part C').

get_result(Parts) :-
    product_parts(Parts),
    reverse(Parts, Ordered),
    Parts = Ordered.

% Director
build_minimal :-
    reset_builder,
    build_step_a.

build_full :-
    reset_builder,
    build_step_a,
    build_step_b,
    build_step_c.

build_custom(Steps) :-
    reset_builder,
    forall(member(Step, Steps), build_step(Step)).

build_step(A) :- build_step_a.
build_step(B) :- build_step_b.
build_step(C) :- build_step_c.

% Usage
% build_minimal,
% list_parts.
Coding Round
100. Prototype pattern

Implement prototype pattern for cloning objects.

  • Prototype: prototype/2
  • Clone: clone/2
  • Deep clone: deep_clone/2
  • Mutable: Mutable Prototype
prolog
% Prototype pattern
% Prototype
prototype(Data, Data).

% Clone
clone(Data, Data).

% Deep clone
deep_clone(Data, Data) :-
    (atomic(Data) -> true ;
     compound(Data) -> 
         Data =.. [Functor|Args],
         deep_clone_list(Args, CloneArgs),
         Data =.. [Functor|CloneArgs]).

deep_clone_list([], []).
deep_clone_list([H|T], [CH|CT]) :-
    deep_clone(H, CH),
    deep_clone_list(T, CT).

% Mutable prototype
:- dynamic mutable_data/1.

create_mutable(Data) :-
    retractall(mutable_data(_)),
    asserta(mutable_data(Data)).

get_mutable(Data) :-
    mutable_data(Data).

set_mutable(Data) :-
    retractall(mutable_data(_)),
    asserta(mutable_data(Data)).

clone_mutable(Clone) :-
    get_mutable(Data),
    clone(Data, Clone).

deep_clone_mutable(Clone) :-
    get_mutable(Data),
    deep_clone(Data, Clone).

% Prototype with cache
:- dynamic prototype_cache/2.

cached_prototype(Data, Prototype) :-
    (prototype_cache(Data, Prototype) -> true ;
     prototype(Data, NewProto),
     asserta(prototype_cache(Data, NewProto)),
     Prototype = NewProto).

% Usage
% prototype([1,2,3], P1),
% clone(P1, P2).