Skip to content

Commit

Permalink
Add Streams library (#381)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
remo5000 authored Oct 16, 2018
1 parent a5d4e07 commit 8a32c0e
Show file tree
Hide file tree
Showing 5 changed files with 338 additions and 2 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "cadet-frontend",
"version": "1.0.13",
"version": "1.0.14",
"scripts-info": {
"format": "Format source code",
"start": "Start the Webpack development server",
Expand Down
2 changes: 2 additions & 0 deletions public/externalLibs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ function loadAllLibs() {
'/externalLibs/visualizer/visualizer.js',
// binary tree library
'/externalLibs/tree.js',
// streams
'/externalLibs/streams/stream.js',
];

for (var i = 0; i < files.length; i++) {
Expand Down
308 changes: 308 additions & 0 deletions public/externalLibs/streams/stream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
// stream.js: Supporting streams in the Scheme style, following
// "stream discipline"
// A stream is either the empty list or a pair whose tail is
// a nullary function that returns a stream.

// Author: Martin Henz

// stream_tail returns the second component of the given pair
// throws an exception if the argument is not a pair
// LOW-LEVEL FUNCTION, NOT JEDISCRIPT
function stream_tail(xs) {
var tail;
if (is_pair(xs)) {
tail = xs[1];
} else {
throw new Error("stream_tail(xs) expects a pair as "
+ "argument xs, but encountered " + xs);
}

if (typeof tail === "function") {
return tail();
} else {
throw new Error("stream_tail(xs) expects a function as "
+ "the tail of the argument pair xs, "
+ "but encountered " + tail);
}
}

// is_stream recurses down the stream and checks that it ends with
// the empty list []; does not throw any exceptions
// LOW-LEVEL FUNCTION, NOT JEDISCRIPT
// Lazy? No: is_stream needs to go down the stream
function is_stream(xs) {
return (array_test(xs) && xs.length === 0)
|| (is_pair(xs) && typeof tail(xs) === "function" &&
is_stream(stream_tail(xs)));
}

// list_to_stream transforms a given list to a stream
// Lazy? Yes: list_to_stream goes down the list only when forced
function list_to_stream(xs) {
if (is_empty_list(xs)) {
return [];
} else {
return pair(head(xs),
function() {
return list_to_stream(tail(xs));
});
}
}

// stream_to_list transforms a given stream to a list
// Lazy? No: stream_to_list needs to force the whole stream
function stream_to_list(xs) {
if (is_empty_list(xs)) {
return [];
} else {
return pair(head(xs), stream_to_list(stream_tail(xs)));
}
}

// stream makes a stream out of its arguments
// LOW-LEVEL FUNCTION, NOT JEDISCRIPT
// Lazy? No: In this implementation, we generate first a
// complete list, and then a stream using list_to_stream
function stream() {
var the_list = [];
for (var i = arguments.length - 1; i >= 0; i--) {
the_list = pair(arguments[i], the_list);
}
return list_to_stream(the_list);
}

// stream_length returns the length of a given argument stream
// throws an exception if the argument is not a stream
// Lazy? No: The function needs to explore the whole stream
function stream_length(xs) {
if (is_empty_list(xs)) {
return 0;
} else {
return 1 + stream_length(stream_tail(xs));
}
}

// stream_map applies first arg f to the elements of the second
// argument, assumed to be a stream.
// f is applied element-by-element:
// stream_map(f,list_to_stream([1,[2,[]]])) results in
// the same as list_to_stream([f(1),[f(2),[]]])
// stream_map throws an exception if the second argument is not a
// stream, and if the second argument is a non-empty stream and the
// first argument is not a function.
// Lazy? Yes: The argument stream is only explored as forced by
// the result stream.
function stream_map(f, s) {
if (is_empty_list(s)) {
return [];
} else {
return pair(f(head(s)),
function() {
return stream_map(f, stream_tail(s));
});
}
}

// build_stream takes a non-negative integer n as first argument,
// and a function fun as second argument.
// build_list returns a stream of n elements, that results from
// applying fun to the numbers from 0 to n-1.
// Lazy? Yes: The result stream forces the applications of fun
// for the next element
function build_stream(n, fun) {
function build(i) {
if (i >= n) {
return [];
} else {
return pair(fun(i),
function() {
return build(i + 1);
});
}
}
return build(0);
}

// stream_for_each applies first arg fun to the elements of the list
// passed as second argument. fun is applied element-by-element:
// for_each(fun,list_to_stream([1,[2,[]]])) results in the calls fun(1)
// and fun(2).
// stream_for_each returns true.
// stream_for_each throws an exception if the second argument is not a list,
// and if the second argument is a non-empty list and the
// first argument is not a function.
// Lazy? No: stream_for_each forces the exploration of the entire stream
function stream_for_each(fun, xs) {
if (is_empty_list(xs)) {
return true;
} else {
fun(head(xs));
return stream_for_each(fun, stream_tail(xs));
}
}

// stream_reverse reverses the argument stream
// stream_reverse throws an exception if the argument is not a stream.
// Lazy? No: stream_reverse forces the exploration of the entire stream
function stream_reverse(xs) {
function rev(original, reversed) {
if (is_empty_list(original)) {
return reversed;
} else {
return rev(stream_tail(original),
pair(head(original), function() { return reversed; }));
}
}
return rev(xs,[]);
}

// stream_to_vector returns vector that contains the elements of the argument
// stream in the given order.
// stream_to_vector throws an exception if the argument is not a stream
// LOW-LEVEL FUNCTION, NOT JEDISCRIPT
// Lazy? No: stream_to_vector forces the exploration of the entire stream
function stream_to_vector(lst){
var vector = [];
while (!is_empty_list(lst)) {
vector.push(head(lst));
lst = stream_tail(lst);
}
return vector;
}

// stream_append appends first argument stream and second argument stream.
// In the result, the [] at the end of the first argument stream
// is replaced by the second argument stream
// stream_append throws an exception if the first argument is not a
// stream.
// Lazy? Yes: the result stream forces the actual append operation
function stream_append(xs, ys) {
if (is_empty_list(xs)) {
return ys;
} else {
return pair(head(xs),
function() {
return stream_append(stream_tail(xs), ys);
});
}
}

// stream_member looks for a given first-argument element in a given
// second argument stream. It returns the first postfix substream
// that starts with the given element. It returns [] if the
// element does not occur in the stream
// Lazy? Sort-of: stream_member forces the stream only until the element is found.
function stream_member(x, s) {
if (is_empty_list(s)) {
return [];
} else if (head(s) === x) {
return s;
} else {
return stream_member(x, stream_tail(s));
}
}

// stream_remove removes the first occurrence of a given first-argument element
// in a given second-argument list. Returns the original list
// if there is no occurrence.
// Lazy? Yes: the result stream forces the construction of each next element
function stream_remove(v, xs) {
if (is_empty_list(xs)) {
return [];
} else if (v === head(xs)) {
return stream_tail(xs);
} else {
return pair(head(xs),
function() {
return stream_remove(v, stream_tail(xs));
});
}
}

// stream_remove_all removes all instances of v instead of just the first.
// Lazy? Yes: the result stream forces the construction of each next element
function stream_remove_all(v, xs) {
if (is_empty_list(xs)) {
return [];
} else if (v === head(xs)) {
return stream_remove_all(v, stream_tail(xs));
} else {
return pair(head(xs), function() {
return stream_remove_all(v, stream_tail(xs));
});
}
}

// filter returns the substream of elements of given stream s
// for which the given predicate function p returns true.
// Lazy? Yes: The result stream forces the construction of
// each next element. Of course, the construction
// of the next element needs to go down the stream
// until an element is found for which p holds.
function stream_filter(p, s) {
if (is_empty_list(s)) {
return [];
} else if (p(head(s))) {
return pair(head(s),
function() {
return stream_filter(p, stream_tail(s));
});
} else {
return stream_filter(p, stream_tail(s));
}
}

// enumerates numbers starting from start,
// using a step size of 1, until the number
// exceeds end.
// Lazy? Yes: The result stream forces the construction of
// each next element
function enum_stream(start, end) {
if (start > end) {
return [];
} else {
return pair(start,
function() {
return enum_stream(start + 1, end);
});
}
}

// integers_from constructs an infinite stream of integers
// starting at a given number n
// Lazy? Yes: The result stream forces the construction of
// each next element
function integers_from(n) {
return pair(n,
function() {
return integers_from(n + 1);
});
}

// eval_stream constructs the list of the first n elements
// of a given stream s
// Lazy? Sort-of: eval_stream only forces the computation of
// the first n elements, and leaves the rest of
// the stream untouched.
function eval_stream(s, n) {
if (n === 0) {
return [];
} else {
return pair(head(s),
eval_stream(stream_tail(s),
n - 1));
}
}

// Returns the item in stream s at index n (the first item is at position 0)
// Lazy? Sort-of: stream_ref only forces the computation of
// the first n elements, and leaves the rest of
// the stream untouched.
function stream_ref(s, n) {
if (n === 0) {
return head(s);
} else {
return stream_ref(stream_tail(s), n - 1);
}
}

3 changes: 2 additions & 1 deletion src/components/assessment/assessmentShape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ export enum ExternalLibraryNames {
TWO_DIM_RUNES = 'TWO_DIM_RUNES',
THREE_DIM_RUNES = 'THREE_DIM_RUNES',
CURVES = 'CURVES',
SOUND = 'SOUND'
SOUND = 'SOUND',
STREAMS = 'STREAMS'
}

export type ExternalLibraryName = keyof typeof ExternalLibraryNames
Expand Down
25 changes: 25 additions & 0 deletions src/reducers/externalLibraries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,31 @@ const libEntries: Array<[ExternalLibraryName, string[]]> = [
'violin',
'cello'
]
],
[
ExternalLibraryNames.STREAMS,
[
'stream_tail',
'is_stream',
'list_to_stream',
'stream_to_list',
'stream',
'stream_length',
'stream_map',
'build_stream',
'stream_for_each',
'stream_reverse',
'stream_to_vector',
'stream_append',
'stream_member',
'stream_remove',
'stream_remove_all',
'stream_filter',
'enum_stream',
'integers_from',
'eval_stream',
'stream_ref'
]
]
]

Expand Down

0 comments on commit 8a32c0e

Please sign in to comment.