Skip to content

Commit 708ff96

Browse files
authored
Merge pull request #16 from arsalanc-v2/non-det-examples
Add non-det SICP JS solutions
2 parents a41d908 + cb769a5 commit 708ff96

File tree

6 files changed

+269
-0
lines changed

6 files changed

+269
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// chapter=3 variant=non-det executionMethod=interpreter
2+
const md = multiple_dwelling();
3+
let res = '';
4+
for (let i = 0; i < length(md); i=i+1) {
5+
const person_floor = list_ref(md, i);
6+
const person = head(person_floor);
7+
const floor = stringify(head(tail(person_floor)));
8+
res = res + person + floor + ',';
9+
}
10+
res;
11+
// 'baker3,cooper2,fletcher4,miller5,smith1,'
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// chapter=3 variant=non-det executionMethod=interpreter
2+
const liars = list(list('betty', betty), list('ethel', ethel), list('joan', joan),
3+
list('kitty', kitty), list('mary', mary));
4+
let res = '';
5+
for (let i = 0; i < length(liars); i=i+1) {
6+
const person_rank = list_ref(liars, i);
7+
const person = head(person_rank);
8+
const rank = stringify(head(tail(person_rank)));
9+
res = res + person + rank + ',';
10+
}
11+
res;
12+
// 'betty3,ethel5,joan2,kitty1,mary4,'
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// chapter=3 variant=non-det executionMethod=interpreter
2+
const queens = queens_fast(8, 8);
3+
let res = '';
4+
for (let i = 0; i < length(queens); i=i+1) {
5+
const position = list_ref(queens, i);
6+
const x = stringify(head(position));
7+
const y = stringify(tail(position));
8+
res = res + '(' + x + ',' + y + '),';
9+
}
10+
res;
11+
// '(4,8),(2,7),(7,6),(3,5),(6,4),(8,3),(5,2),(1,1),'
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* SICP JS Exercise 4.31
2+
3+
This file contains a fast(er) solution to the multiple dwelling puzzle.
4+
The key idea is that we perform every 'require' check immediately once
5+
the information it needs is available. This minimises wasteful backtracking.
6+
*/
7+
8+
function distinct(items) {
9+
return is_null(items)
10+
? true
11+
: is_null(tail(items))
12+
? true
13+
: is_null(member(head(items), tail(items)))
14+
? distinct(tail(items))
15+
: false;
16+
}
17+
18+
function multiple_dwelling() {
19+
const baker = amb(1, 2, 3, 4, 5);
20+
require(!(baker === 5));
21+
const cooper = amb(1, 2, 3, 4, 5);
22+
require(!(cooper === 1));
23+
const fletcher = amb(1, 2, 3, 4, 5);
24+
require(!(fletcher === 5));
25+
require(!(fletcher === 1));
26+
require(!(math_abs(fletcher - cooper) === 1));
27+
const miller = amb(1, 2, 3, 4, 5);
28+
require(miller > cooper);
29+
const smith = amb(1, 2, 3, 4, 5);
30+
require(!(math_abs(smith - fletcher) === 1));
31+
require(distinct(list(baker, cooper, fletcher, miller, smith)));
32+
33+
34+
return list(list("baker", baker),
35+
list("cooper", cooper),
36+
list("fletcher", fletcher),
37+
list("miller", miller),
38+
list("smith", smith));
39+
}
40+
// multiple_dwelling();
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
SICP JS Exercise 4.33
3+
4+
This file contains a solution to the Liars' puzzle
5+
*/
6+
7+
function distinct(items) {
8+
return is_null(items)
9+
? true
10+
: is_null(tail(items))
11+
? true
12+
: is_null(member(head(items), tail(items)))
13+
? distinct(tail(items))
14+
: false;
15+
}
16+
17+
const kitty = amb(1, 2, 3, 4, 5);
18+
const mary = amb(1, 2, 3, 4, 5);
19+
require(amb(kitty === 2, mary === 4));
20+
const betty = amb(1, 2, 3, 4, 5);
21+
require(amb(kitty === 2, betty === 3));
22+
require(amb(mary === 4, betty === 1));
23+
const ethel = amb(1, 2, 3, 4, 5);
24+
const joan = amb(1, 2, 3, 4, 5);
25+
require(amb(ethel === 1, joan === 2));
26+
require(amb(joan === 3, ethel === 5));
27+
28+
require(distinct(list(betty, ethel, joan, kitty, mary)));
29+
30+
// list(list('betty', betty), list('ethel', ethel), list('joan', joan),
31+
// list('kitty', kitty), list('mary', mary));
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
SICP JS Exercise 4.35
3+
4+
This file contains two solutions for the n-queens puzzle using non-determinism.
5+
Each of them makes use of the generate-and-test paradigm.
6+
7+
The first (queens_slow) uses non-determinism for generating both the row and column
8+
for each position. It does so in an optimized manner, testing the corresponding condition
9+
as soon as the choice is generated, thereby reducing the search space. However, this is not
10+
enough to yield a quick solution when N = 8.
11+
This solution also gives repeated solutions (i.e different permutations that have all the same positions) upon backtracking.
12+
13+
The second (queens_fast) uses non-determinism in picking only the row and not the column of each position, with the
14+
columns being fixed. This further reduces the search space, yielding a quick solution when N = 8.
15+
*/
16+
17+
const N = 8; // the number of queens and the size of the board
18+
const empty_positions = null;
19+
20+
/******************************************************************************/
21+
/* Slow version which uses non-determinism for both the columns and rows, */
22+
/* perform testing of a required condition as soon as the choice is generated */
23+
/******************************************************************************/
24+
25+
// const result_queens_slow = queens_slow(N, N);
26+
// pretty_print(result_queens_slow, N);
27+
// generate more solutions by entering 'try again' in the REPL
28+
29+
function queens_slow(board_size, num_queens) {
30+
require(num_queens <= board_size);
31+
const possible_positions = enum_list(1, board_size);
32+
33+
const result = accumulate(
34+
(_, so_far) => {
35+
const row = an_element_of(possible_positions);
36+
require(is_row_safe(row, so_far));
37+
38+
const col = an_element_of(possible_positions);
39+
require(is_col_safe(col, so_far));
40+
41+
const new_position = pair(row, col);
42+
require(is_diagonal_safe(new_position, so_far));
43+
44+
const new_positions = pair(new_position, so_far);
45+
return new_positions;
46+
},
47+
empty_positions,
48+
enum_list(1, num_queens)
49+
);
50+
51+
return result;
52+
}
53+
54+
function is_row_safe(new_row, queen_positions) {
55+
const rows = map(position => head(position), queen_positions);
56+
return member(new_row, rows) === null;
57+
}
58+
59+
function is_col_safe(new_col, queen_positions) {
60+
const cols = map(position => tail(position), queen_positions);
61+
return member(new_col, cols) === null;
62+
}
63+
64+
function is_diagonal_safe(new_position, queen_positions) {
65+
const new_sum = head(new_position) + tail(new_position);
66+
const new_sub = head(new_position) - tail(new_position);
67+
const sums = map(
68+
position => head(position) + tail(position),
69+
queen_positions
70+
);
71+
72+
return (
73+
member(new_sum, sums) === null &&
74+
member(
75+
new_sub,
76+
map(position => head(position) - tail(position), queen_positions)
77+
) === null
78+
);
79+
}
80+
81+
/******************************************************************************/
82+
/* Fast version which uses non-determinism only for the rows, */
83+
/* with the columns being hardcoded. */
84+
/******************************************************************************/
85+
86+
// const result_queens_fast = queens_fast(N, N);
87+
// pretty_print(result_queens_fast, N);
88+
// generate more solutions by entering 'try again' in the REPL
89+
90+
function queens_fast(board_size, num_queens) {
91+
require(num_queens <= board_size);
92+
const possible_positions = enum_list(1, board_size);
93+
94+
function queen_cols(k) {
95+
if (k === 0) {
96+
return empty_positions;
97+
} else {
98+
const so_far = queen_cols(k - 1);
99+
100+
const new_row = an_element_of(possible_positions);
101+
const new_position = pair(new_row, k);
102+
require(is_safe(new_position, so_far));
103+
104+
const new_positions = pair(new_position, so_far);
105+
return new_positions;
106+
}
107+
}
108+
return queen_cols(num_queens);
109+
}
110+
111+
function is_safe(new_position, positions) {
112+
const new_row = head(new_position);
113+
const new_col = tail(new_position);
114+
115+
return accumulate(
116+
(position, so_far) => {
117+
const row = head(position);
118+
const col = tail(position);
119+
120+
return (
121+
so_far &&
122+
new_row - new_col !== row - col &&
123+
new_row + new_col !== row + col &&
124+
new_row !== row
125+
);
126+
},
127+
true,
128+
positions
129+
);
130+
}
131+
132+
133+
/* Pretty prints a solution to the n-queens puzzle */
134+
function pretty_print(result, board_size) {
135+
function member_eq(v, xs) {
136+
return is_null(xs)
137+
? null
138+
: equal(v, head(xs))
139+
? xs
140+
: member_eq(v, tail(xs));
141+
}
142+
const possible_positions = enum_list(1, board_size);
143+
144+
let col_index_str = " ";
145+
for_each(i => {
146+
col_index_str = col_index_str + stringify(i) + " ";
147+
}, possible_positions);
148+
display(col_index_str);
149+
150+
for_each(row => {
151+
let row_str = stringify(row) + " ";
152+
for_each(col => {
153+
const position = pair(row, col);
154+
const contains_position = member_eq(position, result) !== null;
155+
if (contains_position) {
156+
row_str = row_str + "Q ";
157+
} else {
158+
row_str = row_str + ". ";
159+
}
160+
}, possible_positions);
161+
162+
display(row_str);
163+
}, possible_positions);
164+
}

0 commit comments

Comments
 (0)