Carpet

Carpet

2M Downloads

[Suggestion] Add stream/generator type to scarpet

Ghoulboy78 opened this issue · 4 comments

commented

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')

commented

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.

commented

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

commented

I could implement this with classes.scl when it gets merged, I think

commented

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);