# Difference Fanout¶

Given a list of numbers, for each number generate a list of the
differences between it and \(n_{fanout}\) (known as the **fanout**
value) following numbers in the list. Return a list of all the lists
generated for each number. For members in the list that have fewer than
\(n_{fanout}\) following members, calculate as many differences as
possible. For example, suppose we want to compute the difference fanout
on the list `[3, 2, 4, 6, 1]`

with a fanout value of 3. Then we would
compute:

- \(3 \rightarrow [2 - 3, 4 - 3, 6 - 3]\)
- \(2 \rightarrow [4 - 2, 6 - 2, 1 - 2]\)
- \(4 \rightarrow [6 - 4, 1 - 4]\)
- \(6 \rightarrow [1 - 6]\)
- \(1 \rightarrow []\)

```
# example behavior
>>> difference_fanout([3, 2, 4, 6, 1], 3)
[[-1, 1, 3], [2, 4, -1], [2, -3], [-5], []]
```

You will want to know about lists, indexing & slicing lists, and for-loops to solve this problem.

For extra credits (and some extra fun!), try to write your function only using list comprehension.

## Solution: difference_fanout using for-loops¶

We will naturally tackle this problem by performing nested for-loops. The outermost for-loop will loop over each number in the list. We will refer to this number as the “base number”. We will want the inner for-loop to iterate ahead of the base number so that we can compute the differences between it and its \(n_{fanout}\) neighbors. We will need to take care re-initialize our intermediate list of differences for each new base number, otherwise each subtraction will get appended to one long list.

```
def difference_fanout(l, fanout):
""" Return a list of differences for
each value with its following terms
Parameters
----------
l: List[Number]
Input list of base numbers.
fanout: int
Number of neighbors to compute differences against.
Returns
-------
List[List[Number]]
"""
all_fanouts = [] # will store each list of fanouts
for i, base_number in enumerate(l):
# `base_fanout` will store the differences between
# the base number's successive neighbors and base number
base_fanout = []
for neighbor in l[i+1: i+1+fanout]:
base_fanout.append(neighbor - base_number)
all_fanouts.append(base_fanout)
return all_fanouts
```

Note our use of
enumerate;
this permits us to simultaneously access our base number, which we use
in the subtraction, as well as its index-position within the list `l`

,
which we use to determine the neighbors.

Next, you may be concerned that our inner-loop will attempt to iterate
beyond the end of the list. Consider the case in which `base_number`

is the final element in `l`

, thus `l[i+1: i+1+fanout]`

would be
equivalent to `l[len(l): len(l)+fanout]`

- the stopping point for this
slice clearly reaches beyond the extent of `l`

(assuming
`fanout > 0`

). Fortunately, this is not an oversight on our part.
While indexing a list outside of its bounds will raise an error, recall
that a slice will automatically limit itself to be within bounds of a
given
sequence.
That is, `l[i+1: i+1+fanout]`

actually behaves like
`l[min(i, len(l)-1): min(len(l), i+1+fanout)]`

(assuming we are
dealing only with positive indices and non-empty lists). Thus our
inner-loop will naturally limit itself. In the case that `base_number`

is the final element in `l`

, the inner-loop will exit immediately,
leaving `base_fanout`

empty. Although somewhat obscure, this is an
important aspect of Python’s slicing mechanism to keep in mind.

## Solution: difference_fanout using list comprehensions¶

We can make judicious use of nested list comprehensions to simplify our solution. Although the syntax may appear to be convoluted at first glance, it permits us proceed without worrying about initializing multiple empty lists and appending to them at the right points in our nested for-loops

```
def difference_fanout(l, fanout):
""" Return a list of differences for
each value with its following terms
Parameters
----------
l: List[Number]
Input list
fanout: int
Number of neighbors to compute difference with
Returns
-------
List[Number]
"""
return [[neighbor - base for neighbor in l[i+1:i+1+fanout]]
for i,base in enumerate(l)]
```

See that the outermost list comprehension loops over the base number, as did the outer for-loop in the prior solution, and that the innermost list comprehension plays the same roll as the inner for-loop.

There are fewer potential points of failure in this solution, as its conciseness removes the “moving parts” that had to be managed in the previous solution. This should help demonstrate the power of the comprehension expression syntax.

## Extension¶

Recall from
earlier
that functions are, under the hood, just objects with some special
operations that allow you to “call” a function. This means that you can
pass other functions as parameters into a function. It is especially
powerful, since it enables us to generalize the purposes of our
functions. For example, we don’t have to limit our function to just
computing the **difference** between members and their following terms;
we can apply **any** *binary operation*. Instead of finding the
difference, we can calculate the sum or product or even concatenate two
strings for a list of string. The possibilities are limitless.

Armed with this knowledge, we can generalize the code.

```
def apply_fanout(l, fanout, op):
""" Return a list of outputs for each value
after applying a binary operation between
the value and its following terms
Parameters
----------
l: List[Any]
Input list
fanout: int
Number of neighbors to apply the operation with
op: Callable[[Any, Any], Any]
Any binary operation to be applied to fanout-pairs
of elements in `l`.
Returns
-------
List[List[Any]]
"""
return [[op(neighbor, base) for neighbor in l[i+1:i+1+fanout]]
for i,base in enumerate(l)]
```

Now, we can rewrite `difference_fanout`

simply as

```
def subtract(a, b):
return a - b
def difference_fanout(l, fanout):
return apply_fanout(l, fanout, subtract)
```

We can easily change `subtract`

to some other function for a totally
different use.