Preview:
//Streams II

/* 
What is a stream?
- rither null or a pair whose head is of that type and 
- tail is a nullary function that returns a stream of that type
*/

//Concept of  laziness
//stream_tail is used to defer computation


//Finite stream: fixed number of elements
const s3 = pair(1, ()=> pair(2, ()=> pair(3, null)));
s3;
head(s3);//returns 1
head(tail(s3)());//returns 2
head(tail(tail(s3)())());// returns 3

//Infinite stream: innfinite elements
function ones_stream(){
    return pair(1 ,ones_stream);
}

const ones = ones_stream();
// alternative: const ones = pair(1, ()=> ones); (wrapping around)

head(ones);//returns 1
head(tail(ones)());//returns 1
head(tail(tail(ones)())());//returns 1

//stream_tail: 
function stream_tail(stream){
    return tail(stream)();
}

head(ones);//returns 1
head(stream_tail(ones));//returns 1
head(stream_tail(stream_tail(ones)));//returns 1

//difference: stream_tail uses lazy evaluation

//enum_stream
function enum_stream(low,hi){
    return low > hi
           ? null
           : pair(low, () => enum_stream(low + 1, hi));
}

let s = enum_stream(1, 100);

head(s); //returns 1
head(stream_tail(s)); //returns 2
head(stream_tail(stream_tail(s))); //returns 3

//stream_ref
function stream_ref(s, n){
    return n === 0
           ? head(s) 
           : pair(head(s), () => stream_ref(stream_tail(s), n-1));
}

s = enum_stream(1, 100);

stream_ref(s, 0);   //returns 1
stream_ref(s, 10); //returns unevaluated stream
stream_ref(s, 99);//returns unevaluated stream

//stream_map
function stream_map(f, s){
    return is_null(s)
           ? null 
           : pair(f(head(s)), () => stream_map(f, stream_tail(s)));
}

//stream_filter
function stream_filter(p, s){
    return is_null(s)
           ? null 
           : p(head(s))
             ? pair(head(s), () => stream_filter(p, stream_tail(s)))
             : stream_filter(p, stream_tail(s));
}

//EXAMPLES:

//1. integers_from
function integers_from(n){
    return pair(n, () => integers_from(n+1));
}

const integers = integers_from(1);
stream_ref(integers, 0);   //returns 1
stream_ref(integers, 10);  //returns unevaluated stream
stream_ref(integers, 99);  //returns unevaluated stream

//2. no_fours

function is_divisible(x,y){
     return x % y ===0;
}

const no_fours= stream_filter(x => !is_divisible(x,4), integers);

stream_ref(no_fours, 3);
stream_ref(no_fours, 100);

//From Streams to Lists: eval_stream
function eval_stream(s, n){
    return n === 0
           ? null 
           : pair(head(s), eval_stream(stream_tail(s), n-1));
}

eval_stream(no_fours, 10); //returns [1, [2, [3, [5, [6, [7, [9, [10, [11, [13, null]]]]]]]]]]

//Fibonacci Numbers
function fibgen(a, b){
    return pair(a, () => fibgen(b, a + b));
    
let fibs = fibgen(1,1);

eval_stream(fibs,10);
}

//more_and_more
//wanted: Stream containing 1, 1, 2, 1, 2, 3, 1, 2, 3, 4, ...
function more(a, b){
    return (a > b)
           ? more (1, 1 + b)
           : pair (a, () => more(a + 1, b));
}
const more_and_more = more(1,1);

eval_stream(more_and_more, 15);
//returns: [1, [1, [2, [1, [2, [3, [1, [2, [3, [4, [1, [2, [3, [4, [5, null]]]]]]]]]]]]]]]

//Replace
function replace(g, a, b){
    return is_null(g)
           ? null 
           : pair((head(g) === a) ? b : head(g), 
                    () => replace(stream_tail(g), a, b));
}

const g = replace(more_and_more, 1, 0);
eval_stream(g, 15);
//returns [0, [0, [2, [0, [2, [3, [0, [2, [3, [4, [0, [2, [3, [4, [5, null]]]]]]]]]]]]]]]

//List to Infinite stream
function list_to_inf_stream(xs){
    function helper(ys){
        return is_null(ys)
               ? helper(xs) //reset list
               : pair(head(ys), () => helper(tail(ys)));
    }
    return is_null(xs)? null : helper(xs);
}

const z = list_to_inf_stream(list(1,2,3));
eval_stream(z, 10);
//returns: [1, [2, [3, [1, [2, [3, [1, [2, [3, [1, null]]]]]]]]]]

//Alternative: 
const rep123 = pair(1, ()=> pair(2, ()=> pair(3, ()=> rep123)));

//Adding Streams

function add_streams(s1, s2){
    return is_null(s1)
           ? s2
           : is_null(s2)
           ? s1
           : pair(head(s1) + head(s2), () => add_streams(stream_tail(s1), stream_tail(s2)));
}
//Fibonacci Numbers using add_streams
//const fibs = pair(0, ()=> pair(1, () => add_streams(stream_tail(fibs), fibs)));
//eval_stream(fibs, 10);

//result: [0, [1, [1, [2, [3, [5, [8, [13, [21, [34, null]]]]]]]]]]


///Integers Revisisted using add_streams
const one_s = pair(1, ()=> one_s);
const integer_s = pair(1, () => add_streams(one_s, integer_s));
eval_stream(integer_s, 10);
//returns [1, [2, [3, [4, [5, [6, [7, [8, [9, [10, null]]]]]]]]]]

//Memoization with lazy evaluation
function memo_fun(fun){
    let already_run = false; // Tracks if function has been called before
    let result = undefined; //Stores result of first call
    
    
    function mfun(){
        if (!already_run){ //If function has not been called yet
            result = fun(); // call function and store it
            already_run = true; //mark already run as true
            return result;
        } else {           //If function has already been called
            return result; //return cached result
        }
    }
    
    return mfun; //return memoized function
}

//Example 1 vs Example 2
//Example 1:

function ms(m, s){
    display(m);
    return s;
}

const onesA = pair(1, () => ms("A", onesA));
stream_ref(onesA, 3);

/* 
Output:
→stream_ref({display("A"); return onesA;}, 2); 
→ stream_ref(onesA, 2);
→ stream_ref({display("A"); return onesA;}, 1); 
→ stream_ref(onesA, 1);
→ stream_ref({display("A"); return onesA;}, 0); 
→ stream_ref(onesA, 0);
→ head(onesA);
→1
*/


const onesB = pair(1, memo_fun(() => ms("B", onesB)));
stream_ref(onesB, 3);


/* 
→ stream_ref({display("B"); memoize onesB; return onesB;}, 2); 
→ stream_ref(onesB, 2);
→ stream_ref({return memoized onesB;}, 1);
→ stream_ref(onesB, 1);
→ stream_ref({return memoized onesB;}, 0); 
→ stream_ref(onesB, 0);
→ head(onesB);
→1 
*/

//Example 2:
function m_integers_from(n){
    return pair(n, memo_fun(()=> ms( "M" + stringify(n), m_integers_from( n +1))));
}

const m_integers = m_integers_from(1);

stream_ref(m_integers, 0);
stream_ref(m_integers, 1);
stream_ref(m_integers, 2); //"M: 2"
// next time stream_ref(m_integers, 2); gives 2 straight from memory

//Generating Primes- Sieve of Eratosthenes
//Original:

function is_prime(n){
    function is_divisible_by_any(divisor){
        if (divisor * divisor > n){
            return false;
        } else if (n % divisor === 0){
            return true;
        } else {
            return is_divisible_by_any(divisor + 1);
        }
    }
    return n>1 && !is_divisible_by_any(2);
}

const primes = pair(2, () => stream_filter(is_prime, integers_from(3)));
eval_stream(primes, 10);
//returns: [2, [3, [5, [7, [11, [13, [17, [19, [23, [29, null]]]]]]]]]]

//SIEVE METHOD:
function sieve(s){
    return pair(head(s), () => sieve(stream_filter( x =>!is_divisible(x, head(s)), stream_tail(s))));
}

const primes2 = sieve(integers_from(2));

//Square Roots by Newton's Method
//Using Streams for Iteration

function average (x,y){
    return (x + y)/2;
}

function improve (guess, x){
    return average(guess, x /guess);
}
function sqrt_stream(x){
const guesses = pair(1.0, () => stream_map(guess => improve(guess, x), guesses));
    return guesses;
}


eval_stream(sqrt_stream(2),6);
/* 
returns:
[ 1,
[ 1.5,
[ 1.4166666666666665,
[1.4142156862745097, [1.4142135623746899, [1.414213562373095, null]]]]]]
*/
downloadDownload PNG downloadDownload JPEG downloadDownload SVG

Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!

Click to optimize width for Twitter