Skip to content

Commit 8a32c0e

Browse files
authored
Add Streams library (#381)
* Add stream library and test * Remove streams test Integrating another test tool into this will take a non-trivial amount of time, and can be done together with tests for the other libraries. * Add stream_max Also just copied the grader file for solidarity * Add file when loading website * Add streams library name * Add functions for streams * Remove stream_take_max * Remove stream_take_max from externalLibraries * Bump version
1 parent a5d4e07 commit 8a32c0e

File tree

5 files changed

+338
-2
lines changed

5 files changed

+338
-2
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "cadet-frontend",
4-
"version": "1.0.13",
4+
"version": "1.0.14",
55
"scripts-info": {
66
"format": "Format source code",
77
"start": "Start the Webpack development server",

public/externalLibs/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ function loadAllLibs() {
3434
'/externalLibs/visualizer/visualizer.js',
3535
// binary tree library
3636
'/externalLibs/tree.js',
37+
// streams
38+
'/externalLibs/streams/stream.js',
3739
];
3840

3941
for (var i = 0; i < files.length; i++) {

public/externalLibs/streams/stream.js

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
// stream.js: Supporting streams in the Scheme style, following
2+
// "stream discipline"
3+
// A stream is either the empty list or a pair whose tail is
4+
// a nullary function that returns a stream.
5+
6+
// Author: Martin Henz
7+
8+
// stream_tail returns the second component of the given pair
9+
// throws an exception if the argument is not a pair
10+
// LOW-LEVEL FUNCTION, NOT JEDISCRIPT
11+
function stream_tail(xs) {
12+
var tail;
13+
if (is_pair(xs)) {
14+
tail = xs[1];
15+
} else {
16+
throw new Error("stream_tail(xs) expects a pair as "
17+
+ "argument xs, but encountered " + xs);
18+
}
19+
20+
if (typeof tail === "function") {
21+
return tail();
22+
} else {
23+
throw new Error("stream_tail(xs) expects a function as "
24+
+ "the tail of the argument pair xs, "
25+
+ "but encountered " + tail);
26+
}
27+
}
28+
29+
// is_stream recurses down the stream and checks that it ends with
30+
// the empty list []; does not throw any exceptions
31+
// LOW-LEVEL FUNCTION, NOT JEDISCRIPT
32+
// Lazy? No: is_stream needs to go down the stream
33+
function is_stream(xs) {
34+
return (array_test(xs) && xs.length === 0)
35+
|| (is_pair(xs) && typeof tail(xs) === "function" &&
36+
is_stream(stream_tail(xs)));
37+
}
38+
39+
// list_to_stream transforms a given list to a stream
40+
// Lazy? Yes: list_to_stream goes down the list only when forced
41+
function list_to_stream(xs) {
42+
if (is_empty_list(xs)) {
43+
return [];
44+
} else {
45+
return pair(head(xs),
46+
function() {
47+
return list_to_stream(tail(xs));
48+
});
49+
}
50+
}
51+
52+
// stream_to_list transforms a given stream to a list
53+
// Lazy? No: stream_to_list needs to force the whole stream
54+
function stream_to_list(xs) {
55+
if (is_empty_list(xs)) {
56+
return [];
57+
} else {
58+
return pair(head(xs), stream_to_list(stream_tail(xs)));
59+
}
60+
}
61+
62+
// stream makes a stream out of its arguments
63+
// LOW-LEVEL FUNCTION, NOT JEDISCRIPT
64+
// Lazy? No: In this implementation, we generate first a
65+
// complete list, and then a stream using list_to_stream
66+
function stream() {
67+
var the_list = [];
68+
for (var i = arguments.length - 1; i >= 0; i--) {
69+
the_list = pair(arguments[i], the_list);
70+
}
71+
return list_to_stream(the_list);
72+
}
73+
74+
// stream_length returns the length of a given argument stream
75+
// throws an exception if the argument is not a stream
76+
// Lazy? No: The function needs to explore the whole stream
77+
function stream_length(xs) {
78+
if (is_empty_list(xs)) {
79+
return 0;
80+
} else {
81+
return 1 + stream_length(stream_tail(xs));
82+
}
83+
}
84+
85+
// stream_map applies first arg f to the elements of the second
86+
// argument, assumed to be a stream.
87+
// f is applied element-by-element:
88+
// stream_map(f,list_to_stream([1,[2,[]]])) results in
89+
// the same as list_to_stream([f(1),[f(2),[]]])
90+
// stream_map throws an exception if the second argument is not a
91+
// stream, and if the second argument is a non-empty stream and the
92+
// first argument is not a function.
93+
// Lazy? Yes: The argument stream is only explored as forced by
94+
// the result stream.
95+
function stream_map(f, s) {
96+
if (is_empty_list(s)) {
97+
return [];
98+
} else {
99+
return pair(f(head(s)),
100+
function() {
101+
return stream_map(f, stream_tail(s));
102+
});
103+
}
104+
}
105+
106+
// build_stream takes a non-negative integer n as first argument,
107+
// and a function fun as second argument.
108+
// build_list returns a stream of n elements, that results from
109+
// applying fun to the numbers from 0 to n-1.
110+
// Lazy? Yes: The result stream forces the applications of fun
111+
// for the next element
112+
function build_stream(n, fun) {
113+
function build(i) {
114+
if (i >= n) {
115+
return [];
116+
} else {
117+
return pair(fun(i),
118+
function() {
119+
return build(i + 1);
120+
});
121+
}
122+
}
123+
return build(0);
124+
}
125+
126+
// stream_for_each applies first arg fun to the elements of the list
127+
// passed as second argument. fun is applied element-by-element:
128+
// for_each(fun,list_to_stream([1,[2,[]]])) results in the calls fun(1)
129+
// and fun(2).
130+
// stream_for_each returns true.
131+
// stream_for_each throws an exception if the second argument is not a list,
132+
// and if the second argument is a non-empty list and the
133+
// first argument is not a function.
134+
// Lazy? No: stream_for_each forces the exploration of the entire stream
135+
function stream_for_each(fun, xs) {
136+
if (is_empty_list(xs)) {
137+
return true;
138+
} else {
139+
fun(head(xs));
140+
return stream_for_each(fun, stream_tail(xs));
141+
}
142+
}
143+
144+
// stream_reverse reverses the argument stream
145+
// stream_reverse throws an exception if the argument is not a stream.
146+
// Lazy? No: stream_reverse forces the exploration of the entire stream
147+
function stream_reverse(xs) {
148+
function rev(original, reversed) {
149+
if (is_empty_list(original)) {
150+
return reversed;
151+
} else {
152+
return rev(stream_tail(original),
153+
pair(head(original), function() { return reversed; }));
154+
}
155+
}
156+
return rev(xs,[]);
157+
}
158+
159+
// stream_to_vector returns vector that contains the elements of the argument
160+
// stream in the given order.
161+
// stream_to_vector throws an exception if the argument is not a stream
162+
// LOW-LEVEL FUNCTION, NOT JEDISCRIPT
163+
// Lazy? No: stream_to_vector forces the exploration of the entire stream
164+
function stream_to_vector(lst){
165+
var vector = [];
166+
while (!is_empty_list(lst)) {
167+
vector.push(head(lst));
168+
lst = stream_tail(lst);
169+
}
170+
return vector;
171+
}
172+
173+
// stream_append appends first argument stream and second argument stream.
174+
// In the result, the [] at the end of the first argument stream
175+
// is replaced by the second argument stream
176+
// stream_append throws an exception if the first argument is not a
177+
// stream.
178+
// Lazy? Yes: the result stream forces the actual append operation
179+
function stream_append(xs, ys) {
180+
if (is_empty_list(xs)) {
181+
return ys;
182+
} else {
183+
return pair(head(xs),
184+
function() {
185+
return stream_append(stream_tail(xs), ys);
186+
});
187+
}
188+
}
189+
190+
// stream_member looks for a given first-argument element in a given
191+
// second argument stream. It returns the first postfix substream
192+
// that starts with the given element. It returns [] if the
193+
// element does not occur in the stream
194+
// Lazy? Sort-of: stream_member forces the stream only until the element is found.
195+
function stream_member(x, s) {
196+
if (is_empty_list(s)) {
197+
return [];
198+
} else if (head(s) === x) {
199+
return s;
200+
} else {
201+
return stream_member(x, stream_tail(s));
202+
}
203+
}
204+
205+
// stream_remove removes the first occurrence of a given first-argument element
206+
// in a given second-argument list. Returns the original list
207+
// if there is no occurrence.
208+
// Lazy? Yes: the result stream forces the construction of each next element
209+
function stream_remove(v, xs) {
210+
if (is_empty_list(xs)) {
211+
return [];
212+
} else if (v === head(xs)) {
213+
return stream_tail(xs);
214+
} else {
215+
return pair(head(xs),
216+
function() {
217+
return stream_remove(v, stream_tail(xs));
218+
});
219+
}
220+
}
221+
222+
// stream_remove_all removes all instances of v instead of just the first.
223+
// Lazy? Yes: the result stream forces the construction of each next element
224+
function stream_remove_all(v, xs) {
225+
if (is_empty_list(xs)) {
226+
return [];
227+
} else if (v === head(xs)) {
228+
return stream_remove_all(v, stream_tail(xs));
229+
} else {
230+
return pair(head(xs), function() {
231+
return stream_remove_all(v, stream_tail(xs));
232+
});
233+
}
234+
}
235+
236+
// filter returns the substream of elements of given stream s
237+
// for which the given predicate function p returns true.
238+
// Lazy? Yes: The result stream forces the construction of
239+
// each next element. Of course, the construction
240+
// of the next element needs to go down the stream
241+
// until an element is found for which p holds.
242+
function stream_filter(p, s) {
243+
if (is_empty_list(s)) {
244+
return [];
245+
} else if (p(head(s))) {
246+
return pair(head(s),
247+
function() {
248+
return stream_filter(p, stream_tail(s));
249+
});
250+
} else {
251+
return stream_filter(p, stream_tail(s));
252+
}
253+
}
254+
255+
// enumerates numbers starting from start,
256+
// using a step size of 1, until the number
257+
// exceeds end.
258+
// Lazy? Yes: The result stream forces the construction of
259+
// each next element
260+
function enum_stream(start, end) {
261+
if (start > end) {
262+
return [];
263+
} else {
264+
return pair(start,
265+
function() {
266+
return enum_stream(start + 1, end);
267+
});
268+
}
269+
}
270+
271+
// integers_from constructs an infinite stream of integers
272+
// starting at a given number n
273+
// Lazy? Yes: The result stream forces the construction of
274+
// each next element
275+
function integers_from(n) {
276+
return pair(n,
277+
function() {
278+
return integers_from(n + 1);
279+
});
280+
}
281+
282+
// eval_stream constructs the list of the first n elements
283+
// of a given stream s
284+
// Lazy? Sort-of: eval_stream only forces the computation of
285+
// the first n elements, and leaves the rest of
286+
// the stream untouched.
287+
function eval_stream(s, n) {
288+
if (n === 0) {
289+
return [];
290+
} else {
291+
return pair(head(s),
292+
eval_stream(stream_tail(s),
293+
n - 1));
294+
}
295+
}
296+
297+
// Returns the item in stream s at index n (the first item is at position 0)
298+
// Lazy? Sort-of: stream_ref only forces the computation of
299+
// the first n elements, and leaves the rest of
300+
// the stream untouched.
301+
function stream_ref(s, n) {
302+
if (n === 0) {
303+
return head(s);
304+
} else {
305+
return stream_ref(stream_tail(s), n - 1);
306+
}
307+
}
308+

src/components/assessment/assessmentShape.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ export enum ExternalLibraryNames {
9393
TWO_DIM_RUNES = 'TWO_DIM_RUNES',
9494
THREE_DIM_RUNES = 'THREE_DIM_RUNES',
9595
CURVES = 'CURVES',
96-
SOUND = 'SOUND'
96+
SOUND = 'SOUND',
97+
STREAMS = 'STREAMS'
9798
}
9899

99100
export type ExternalLibraryName = keyof typeof ExternalLibraryNames

src/reducers/externalLibraries.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,31 @@ const libEntries: Array<[ExternalLibraryName, string[]]> = [
149149
'violin',
150150
'cello'
151151
]
152+
],
153+
[
154+
ExternalLibraryNames.STREAMS,
155+
[
156+
'stream_tail',
157+
'is_stream',
158+
'list_to_stream',
159+
'stream_to_list',
160+
'stream',
161+
'stream_length',
162+
'stream_map',
163+
'build_stream',
164+
'stream_for_each',
165+
'stream_reverse',
166+
'stream_to_vector',
167+
'stream_append',
168+
'stream_member',
169+
'stream_remove',
170+
'stream_remove_all',
171+
'stream_filter',
172+
'enum_stream',
173+
'integers_from',
174+
'eval_stream',
175+
'stream_ref'
176+
]
152177
]
153178
]
154179

0 commit comments

Comments
 (0)