<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 In this document I would like to go through some functional idioms in Python involving the use of iterators and highlight some parallels with the equivalent Haskell implementations.
Step1:  The ubiquitous zip
I must admit I severely underestimated the importance of zip when I first started learning functional style of programming. It comes up in a wide range of patterns, however one of the more elegant applications in Python is given in the following example
Step2:  In fact, it is mentioned in the documentation in the section on built-in functions. I guess, it is one of those bits that are easily skipped on first reading. The reason this example works the way it does, namely, partitioning the iterable into chunks of the given length, is that zip evaluates its arguments strictly left-to-right. Hence, all the copies of the iterator get advanced together and dereferenced one by one.
Now we take a deeper dive straight away. Haskell introduces the zipWith function which takes an additional argument, a function which is applied to the corresponding elements of the zipped iterables. Thus the output is in general no longer a list of tuples of the original elements.
A possible implementation in Python is given below. Note that we return a generator so that the result can be evaluated lazily as needed.
Step3:  Another, more generic, implementation is possible using the starmap function.
Step4:  Using zip_with we can express operations on sequences in a more functional way
Step5:  Before we proceed, let's introduce another bit of syntactical convenience
Step6:  @curry will be explained later, for now just think of it as a fancy way of applying the idea behind functools.partial to forge partial application in Python.
I discovered some of the less obvious applications of zipWith while working on the exercises from the canonical CIS194 course on Haskell. For example, in order to multiply every other element of a sequence by, say 2, we can generate a "masking" sequenc of 1s and 2s and zip it with the original sequence via multiplication
Step7:  Lazy evaluation allows us to work with infinite lists without much trouble
Step8:  In another amazing example which I first came across in a SO answer zip is used to rotate a (potentially empty or infinite) sequence. In order to emphasize the Haskell influence, let us first write it without all of the convenience functions defined above
Step9:  Now we rewrite the same with more Haskell flavour
Step10:  Contextual iterators and monads
Using a custom iterator for a class we can implement some fluent syntax for situations when operations on iterators need to be chained (inspiration taken from lodash.js).
Step11:  We may notice that the resulting flow has certain traits of a composable contextual computation - something that monads were introduced to deal with. Indeed, we start by putting the original data in a "streaming context". Each public method then extracts the actual data, applies some transformation and wraps it back into the context before passing on. This document is not going to descend into yet another tutorial on monads. Instead we will use the PyMonad in a somewhat crude manner to demonstrate how the same goal can be achieved in a more functional way.
First we define a function that will wrap raw data in a context. If we were implementing our own monad, this would be the unit
Step12:  Now express the operations performed by the methods as monadic functions.
Step13:  At the end of the chain we will also need a way out of the context to continue working with the data. We do not intend to be 100% pure after all.
Step14:  Now we can also partially apply our function for better readability and potential reuse.
Step15:  Finally, execute the flow.
Step16:  We can take a different approach and work with ordinary functions instead
Step17:  We can partially apply them, as before, and then use the overloaded * operator to denote curried function composition.
Step18:  Finally the transformation is applied to the "contextualized" data using the applicative style instead of the bind operator.
Step19:  Splitting and unpacking
We finish with a brief example showcasing the use of the tee function to create independent iterators and * (splat) operator to unpack sequences with zip.
We shall consider the task of plotting a histogram for a somewhat unusual data model. Say we are given a sequence of results of some measurements (scores) for a number of observables identified by labels. Suppose also that there may be more than one occurence (count) of results with exactly the same score. We are going to partition scores into a given number of bins and aggregate counts within this bins, for each label separately.
Before we proceed we shall define a convenience function inspired by the eponimous example from Clojure (also this answer provided a nice idiom for an infinite generator)
Step20:  Now let's produce some mock data. Although using effectful functions in comprehensions is generally frowned upon in Python, here it allows us to achieve a clean separation of the generating (repeatedly) and consuming (take) parts of the logic.
Step21:  Now we define a function formatting labels for the plot. This is a typical example of using tee to traverse a sequence in a pairwise manner.
Step22:  Here we aggregate data in the bins
Step23:  Finally we put together a dictionary containing all the data for the plot
<ASSISTANT_TASK:>
Python Code:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from random import random, randint, choice
from itertools import cycle, ifilter, imap, islice, izip, starmap, tee
from collections import defaultdict
from operator import add, mul
from pymonad.Maybe import *
from pymonad.Reader import *
Explanation: In this document I would like to go through some functional idioms in Python involving the use of iterators and highlight some parallels with the equivalent Haskell implementations.
End of explanation
a = range(0, 9)
zip(*[iter(a)]*4)
Explanation: The ubiquitous zip
I must admit I severely underestimated the importance of zip when I first started learning functional style of programming. It comes up in a wide range of patterns, however one of the more elegant applications in Python is given in the following example:
End of explanation
def zip_with_(f, a, b):
     return (f(*z) for z in izip(a, b))
Explanation: In fact, it is mentioned in the documentation in the section on built-in functions. I guess, it is one of those bits that are easily skipped on first reading. The reason this example works the way it does, namely, partitioning the iterable into chunks of the given length, is that zip evaluates its arguments strictly left-to-right. Hence, all the copies of the iterator get advanced together and dereferenced one by one.
Now we take a deeper dive straight away. Haskell introduces the zipWith function which takes an additional argument, a function which is applied to the corresponding elements of the zipped iterables. Thus the output is in general no longer a list of tuples of the original elements.
A possible implementation in Python is given below. Note that we return a generator so that the result can be evaluated lazily as needed.
End of explanation
def zip_with(f, *a):
    return starmap(f, izip(*a))
Explanation: Another, more generic, implementation is possible using the starmap function.
End of explanation
b = range(10, 20)
list(zip_with(add, a, reversed(b)))
Explanation: Using zip_with we can express operations on sequences in a more functional way:
End of explanation
@curry
def take(n, a):
    return islice(a, None, n)
@curry
def drop(n, a):
    return islice(a, n, None)
Explanation: Before we proceed, let's introduce another bit of syntactical convenience:
End of explanation
def double_every_other(a):
    return zip_with(mul, a, cycle([1,2]))
Explanation: @curry will be explained later, for now just think of it as a fancy way of applying the idea behind functools.partial to forge partial application in Python.
I discovered some of the less obvious applications of zipWith while working on the exercises from the canonical CIS194 course on Haskell. For example, in order to multiply every other element of a sequence by, say 2, we can generate a "masking" sequenc of 1s and 2s and zip it with the original sequence via multiplication:
End of explanation
x = cycle([1, 2, 3])
take15 = take(15)
list(take15(x))
y = double_every_other(x)
list(take15(y))
Explanation: Lazy evaluation allows us to work with infinite lists without much trouble:
End of explanation
def rotate(n, a):
    return (x for x, _ in izip(islice(cycle(a), n , None), a))
h = "_hello_lazy_world"
r = rotate(6, h)
''.join(list(r))
Explanation: In another amazing example which I first came across in a SO answer zip is used to rotate a (potentially empty or infinite) sequence. In order to emphasize the Haskell influence, let us first write it without all of the convenience functions defined above:
End of explanation
def const(x, y):
    return x
def rotate1(n, a):
    return zip_with(const, drop(n, cycle(a)), a)
r1 = rotate1(6, h)
''.join(list(r1))
Explanation: Now we rewrite the same with more Haskell flavour:
End of explanation
class Stream(object):
    
    def __init__(self, data):
        self.data = iter(data)
    
    def __iter__(self):
        class iterator(object):
            def __init__(self, it):
                self.it = it.data
            def next(self):
                return next(self.it)
        return iterator(self)
    
    def filter(self, pred):
        return Stream(ifilter(pred, self))
    
    def map(self, fn):
        return Stream(imap(fn, self))
s = Stream(range(20)). \
    filter(lambda x: x % 7 == 0). \
    map(lambda x: x * x)
list(s)
Explanation: Contextual iterators and monads
Using a custom iterator for a class we can implement some fluent syntax for situations when operations on iterators need to be chained (inspiration taken from lodash.js).
End of explanation
def make_stream(data):
    try:
        return Just(iter(data))
    except TypeError:
        return Nothing
Explanation: We may notice that the resulting flow has certain traits of a composable contextual computation - something that monads were introduced to deal with. Indeed, we start by putting the original data in a "streaming context". Each public method then extracts the actual data, applies some transformation and wraps it back into the context before passing on. This document is not going to descend into yet another tutorial on monads. Instead we will use the PyMonad in a somewhat crude manner to demonstrate how the same goal can be achieved in a more functional way.
First we define a function that will wrap raw data in a context. If we were implementing our own monad, this would be the unit
End of explanation
@curry
def filter_stream(pred, stream):
    return Just(ifilter(pred, stream))
@curry
def map_stream(fn, stream):
    return Just(imap(fn, stream))
Explanation: Now express the operations performed by the methods as monadic functions.
End of explanation
def eval_stream(stream):
    if isinstance(stream, Just):
        return stream.value
    else:
        return ()
Explanation: At the end of the chain we will also need a way out of the context to continue working with the data. We do not intend to be 100% pure after all.
End of explanation
my_filter = filter_stream(lambda x: x % 7 == 0)
my_map = map_stream(lambda x: x * x)
Explanation: Now we can also partially apply our function for better readability and potential reuse.
End of explanation
st = make_stream(range(20))
st1 = st >> my_filter >> my_map
list(eval_stream(st1))
Explanation: Finally, execute the flow.
End of explanation
@curry
def filter_stream1(pred, stream):
    return ifilter(pred, stream)
@curry
def map_stream1(fn, stream):
    return imap(fn, stream)
Explanation: We can take a different approach and work with ordinary functions instead:
End of explanation
my_filter1 = filter_stream1(lambda x: x % 7 == 0)
my_map1 = map_stream1(lambda x: x * x)
my_transform = my_filter1 * my_map1
Explanation: We can partially apply them, as before, and then use the overloaded * operator to denote curried function composition.
End of explanation
st2 = make_stream(range(20))
st3 = my_transform * st2
list(eval_stream(st3))
Explanation: Finally the transformation is applied to the "contextualized" data using the applicative style instead of the bind operator.
End of explanation
def repeatedly(f, *args, **kwargs):
    return (f(*args, **kwargs) for _ in iter(int, 1))
Explanation: Splitting and unpacking
We finish with a brief example showcasing the use of the tee function to create independent iterators and * (splat) operator to unpack sequences with zip.
We shall consider the task of plotting a histogram for a somewhat unusual data model. Say we are given a sequence of results of some measurements (scores) for a number of observables identified by labels. Suppose also that there may be more than one occurence (count) of results with exactly the same score. We are going to partition scores into a given number of bins and aggregate counts within this bins, for each label separately.
Before we proceed we shall define a convenience function inspired by the eponimous example from Clojure (also this answer provided a nice idiom for an infinite generator)
End of explanation
def generate_data():
    return (choice(['a', 'b', 'c']), 
            float("{:.2f}".format(random())), 
            randint(1, 20))
data = take(100, repeatedly(generate_data))
sample = take(10, repeatedly(generate_data))
list(sample)
Explanation: Now let's produce some mock data. Although using effectful functions in comprehensions is generally frowned upon in Python, here it allows us to achieve a clean separation of the generating (repeatedly) and consuming (take) parts of the logic.
End of explanation
def bin_labels(bins, label_format=None):
    a, b = tee(bins)
    next(b)
    label_format = label_format or '>= %.2f, < %.2f'
    return [label_format % b for b in izip(a, b)]
Explanation: Now we define a function formatting labels for the plot. This is a typical example of using tee to traverse a sequence in a pairwise manner.
End of explanation
def bin_data(x, y, bin_edges):
    data_to_bins = izip(np.digitize(x, bin_edges), y)
    bin_sums = defaultdict(int)
    for index, count in data_to_bins:
        bin_sums[index] += count
    return [bin_sums.get(index, 0) for index in xrange(len(bin_edges))]
Explanation: Here we aggregate data in the bins
End of explanation
by_label = defaultdict(list)
d1, d2 = tee(data)
for label, score, count in d1:
    by_label[label].append([score, count])
num_bins = 20
_, score, _ = izip(*d2)
bins = np.linspace(min(score), max(score), num_bins)
bin_lbl = bin_labels(bins)
series = []
for label, records in by_label.iteritems():
    _, count = izip(*records)
    series.append({'label': label, 'data': bin_data(score, count, bins)})
    
result = {'series': series,
          'bins': bin_lbl}
fig, ax = plt.subplots(figsize=(18,6))
bar_width = 0.2
colors = ['r', 'g', 'b']
for k, item in enumerate(result['series']):
    index = np.arange(len(item['data']))
    plt.bar(index + k * bar_width, 
            item['data'],
            color = colors[k],
            width = bar_width,
            label = item['label'])
plt.xlabel('Scores')
plt.ylabel('Count')
plt.title('Count by scores')
plt.xticks(index + bar_width, result['bins'], rotation=70)
plt.legend()
plt.show()
    
Explanation: Finally we put together a dictionary containing all the data for the plot
End of explanation
<END_TASK>