[Suggestion] Add stream/generator type to scarpet
Ghoulboy78 opened this issue · 4 comments
Basically this would be a type of value where you could call next(stream)
and it would evaluate the next one along. I suppose you could implement this in regular scarpet in the following way:
//Defining functions
new_stream(function)->{
'ret' -> function
};
next(stream)-> call(stream:'ret', stream);
//Example functions
fibonacci = stream(_(args)->(
if(args:'before_last' == null, args:'last' = 0 args:'before_last' = 1);
{
'ret'->_(args)->(args:'last'+args:'before_last'),
'last'->args:'last'+args:'before_last',
'before_last'->args:'last'
}
));
Here, the function returns the new state of the stream, and then you could access it each time via fibonnacci = next(fibonnacci); print(fibonacci:'ret')
Thing is that this is useful for procedurally generating things, or being able to calculate infinite things without having to resort to tricks like pre-generating and caching, which are slower and less elegant. You can simply compute it as you go along, and if need be store in a list cache for future retrieval. My method works, but is kinda clunky, though probably functional.
recently had to do a multithreaded search system script. the pain of that, since it doesn’t have task_kill
, if location is found, ending the script is not really possible. having a multithreaded stream would have helped in that regard and made the threaded stuff super easier since i could just have one stream to pull coords
Here it is, in all it's glory.
I will put this in a pr at some point.
import('classes','new_class','new_object','call_function', 'global_interface_class', 'global_iterator_class');
global_stream_class = new_class('Stream', {
'filter'->_(self, predicate)->(
[self, new_object(new_class('',{
'parent'->copy(self),
'next'->_(self, outer(predicate))->(
next = call_function(self:'parent', 'next');
while(!call(predicate, next), 1000, next = call_function(self:'parent', 'next'));
[self, next]
),
}, global_stream_class))];
),
//returns next element in this stream
'next'->_(self)->null,
'map'->_(self, mapper)->(//A mapping which conserves the original map, hence why we copy
[self, new_object(new_class('',{
'parent'->copy(self),
'next'->_(self, outer(mapper))->[self, call(mapper, call_function(self:'parent', 'next'))],
}, global_stream_class))];
),
'map_to_number'->_(self, mapper)->(
[self, new_object(new_class('',{
'parent'->copy(self),
'next'->_(self, outer(mapper))->(
new_val = call(mapper, call_function(self:'parent', 'next'));
if(type(new_val)!='number', throw('Stream::map_to_number must map objects to a number, found '+type(new_val)+' instead. For generic mapping, use Stream::map'));
[self, new_val]
),
}, global_stream_class))];
),
'distinct'->_(self)->(
[self, new_object(new_class('', {
'previous_values'->{},
'parent'->copy(self),
'next'->_(self)->(
next = call_function(self:'parent', 'next');
while(has(self:'previous_values', next), 100, next = call_function(self:'parent', 'next'));
self:'previous_values'+=next;
[self, next]
),
}, global_stream_class))];
),
'reduce'->_(self, identity, accumulator)->(
result = identity;
call_function(self,'for_each', _(v, outer(result))->result = call(accumulator, result, v));
[self, result]
),
'max'->_(self)->[self, call_function(self, 'reduce', 0x8000000000000000, _(a, v)->max(a, v))],
'min'->_(self)->[self, call_function(self, 'reduce', 0x7FFFFFFFFFFFFFFF, _(a, v)->(print('a: '+a+' v: '+v);min(a, v)))],
'count'->_(self)->call(self:'reduce', self, 0, _(a, v)->a+1),
'any_match'->_(self, predicate)->(
call_function(self, 'for_each', _(v, outer(predicate))->if(call(predicate, v), return([self, true])));
[self, false]
),
'all_match'->_(self, predicate)->[self, !call_function(self,'any_match', _(v, outer(predicate))->!call(predicate, v))],
'none_match'->_(self, predicate)->[self, !call_function(self,'any_match', _(v, outer(predicate))->call(predicate, v))],
}, global_interface_class, global_iterator_class);
//Simple implementation of streams
global_array_stream_class = new_class('ArrayStream', {
'items'->[],
'index'->-1,
'__init__'->_(self, ...items)->(
self:'items'=items;
self
),
'next'->_(self)->(
self:'index'+=1;
[self, self:'items':(self:'index')]
),
'has_next'->_(self)->[self, self:'index'<length(self:'item')],
}, global_stream_class);