- Read Tutorial
- Watch Guide Video
So if you've never even heard of reducers I'm going to walk through exactly what's expected in this exercise. So I'm going to start off with a comment and let's first just talk about the inputs and outputs because any time that I have something challenging that I'm going to work on I boil it down to the most simple kind of explanation I can think of and that is knowing what I'm going to put into a function and then what I'm going to get out of it.
So let's say that we create a function here called dynamic_reducer. What I want is the ability to pass in two arguments. The first is going to be a list that has to be a list of numbers. So it might be something like 1 2 and 3 and then the second argument is going to be an operator and it's going to be an operator passed in as a string.
So here it might be the plus sign and then what I would expect to be returned here is the sum of each one of those values. So, in this case, it would be six.
Now if I passed in a different operator so I pass in all of those values and then I use the minus sign then I would expect something else I would expect whatever one minus two minus three would be. So I'd expect some kind of negative number, and then I also want to have this for multiplication and I want to have this for division.
So this function needs to be pretty flexible it needs to be able to take in each one of the main mathematical operators and then it should run through and then keep track and tally up each one of the elements so it needs to perform computation and then it should return whatever the value is.
So the easiest base case example is how you can use this with a plus sign so it'd be a sum and in this case, with the values 1 2 3 even with multiplication the value here would be 6. But as soon as you start adding other values then it's going to be a little different. So I'm going to give you some hints on where to research because this is going to combine a number of different technologies and skill sets and libraries that you are going to use.
So these are all going to be libraries within python but you are going to have to import some things. And so we're going to need to import the operator library and then from functools we're going to need to import the Reduce library and so that is what I'm going to give you. So what we need to do once again is build out a function called Dynamic reducer. It takes in a list and then an operator as a string and then it should perform computation on that list.
If you are still a little bit unsure I'm purposely leaving this a little bit vague because I want you to perform some research on these two libraries. First the operator library in python and then in functools the Reduce function. So this is going to be something incredibly helpful it's a part of functional programming where you can pass in a function and then have that function run over an entire collection.
So, in this case, imagine a function like reduce at its most simple kind of implementation reduce is usually used for summing up values and so as you can see right here we have a list and reduce can iterate through this list and tally up each one of the elements. We need it to be a little bit more flexible that's a reason why we're calling this dynamic reduce because you need to be able to pass in each one of these four operators.
So I hope that you have some fun researching those libraries. Both of these are very powerful ways of being able to work with python and be able to implement functional programming and create a pretty dynamic program. So I recommend that you right now pause the video you go and you try to build this out yourself and then come back and watch my own personal solution.
Welcome back, if you built that out congratulations this is definitely something that is on the non-trivial side. And so I highly recommend you if you are able to successfully build this out and if you were not able to do not worry we are going to walk through the solution. And I'll also explain why I chose this specific solution when I personally built this out.
I'm going to start off by creating a function so I'm going to create a dynamic reducer function here. It's going to take in a collection and then an operator. Now I'm purposefully calling this op because if you did reference the documentation for the operator library then if I called this operator then we would have a naming collision. And so we do not want to do that and I don't really feel like aliasing that import either. So I'm going to call it operator and then the argument op.
Now inside of here what I'm going to do is I'm going to create a dictionary. So this is how I'm going to be able to perform the lookup and say that a plus sign or a minus sign or anything like that is passed in I can treat it like a traditional dictionary and then I can use the operator library to actually call the function and so I'm going to create a dictionary here called operators and I'm going to use curly braces inside of it. And now I'm going to have four key-value pairs, the first is going to be the plus sign and this is just a standard dictionary. But what I'm going to use is the value here is calling the operator library and then calling the add a function inside of it.
def dynamic_reducer(collection, op): operators = { "+": operator.add,
So if that looks a little weird to you than what I was able to do is go into the operator library in python. And if you look through their documentation or even if you look through the sourcecode what you would find is it has a number of methods inside of it. One of them is Add, and that is how we're able to add values. Now let me just multiply this out a few more times. So the next one's going to be Subtract and we're going to have multiplying and then we're going to have divide.
Now inside of here add is already in place that is the method name the next one is sub and the next ones multiply but just mul. And then last one if you're using this with Python 3 it is a slightly different method name than it used to be back in Python 2 6. You were able to just call div but now you have to call truediv. And that is our full lookup table so that is our dictionary. Let me just kind of move and rearrange this just looks a little bit better.
def dynamic_reducer(collection, op): operators = { "+": operator.add, "-": operator.sub, "*": operator.mul, "/": operator.truediv }
Okay so we have a python dictionary now this is just a standard dictionary and like you would build with any kind of python program. And what we have is a set of key-value pairs and each one of the keys is a string and then the value is actually a function. So this is where it's a little bit different. So every time that we call a string and we look up the plus sign this is going to return the function of add, and when we pass in the minus sign it's going to return the function of subtract. So that is how I'm going to perform that Look-Up.
So now that we have the ability to perform a lookup with our operators now we can use the reduced library. So I'm going to say return and then let's call the reduce function. Now if you looked up the reduced function then you know that it takes in two arguments. The first argument it takes in is a function that takes in a lambda function specifically and so I'm going to pass in a lambda and this lambda is going to have a total and then an element. And then what I'm going to do here is I'm going to say operators. So this is if you look on line 12 this is our dictionary name and then with the operators I'm going to use just regular dictionary look up syntax and pass in the key which is op. Now, this op references the second argument that we passed into the function.
So I'm going to simply pass that in and then the way that this works because remember when if this is a little tricky for you. I recommend that you watch my solution a few times because what I'm doing here is if you remember when we perform this Look-Up remember what gets sent back to us, a function does, so because a function gets sent back to us that means that we can call it directly and then simply pass in what it expects.
If you went through the operator documentation then you know that add, subtract, multiply, and truediv all expect two arguments. And so they expect the first one and in our case, it's going to be total and then whatever the element is and that is it.
return reduce((lambda total, element: operators[op](total, element))
So this is pretty much the same as in just regular python. I'll make a comment so when we say operators op and op happens to be a plus, then this is really just the same as saying two plus two. If those are the two elements if total is set to two and the element is two that is all we're doing. I know the syntax looks a little weird and the reason is because we are calling the actual operator functions directly, that's how we're making this dynamic. There is another way in you may have personally done something like this.
If you didn't want to build out this kind of function you could have built a conditional. This is not exactly the best practice way of doing it but you could have done something like this and you could have said if op is equal to the plus sign then I want you to return this and then you'd have several else if statements for each one of these and then instead of having this little operators look up then you do something like sayn total and then, in this case, it would be a plus element and then that would be essentially what we're doing right there but it wouldn't really be dynamic because you'd need to do this for each one of the other conditions.
So whenever this is equal to minus and yes I know this is not a traditional else if statement because I'm not actually going to build out that solution but when it's minus then you're going to have to change it to a minus. Then when it's a multiple occasion it's here to change it to multiplication and you notice how much duplicate code you have right here. That would really be a red flag to anyone who knows python and they're looking at the code, you don't want to see that much duplicate code in there.
So that's not really the solution that I would recommend. What I would say is to build it out like this where you can look up and then pass in whatever that operator value is dynamically and then it is going to be able to iterate through the entire collection and then perform that operation. So whenever it's a plus it's going to sum them up, whenever it's multiplication that gets passed in then it's going to multiply all the elements by each other.
So we're not quite done with this we're almost done though, so remember that reduce takes in two arguments it takes in that lambda function and then it takes in whatever collection we have. So that is unless I have a typo or something that should be everything that we need. So I'm going to come up here and let me copy all of our example code to save this and I'm going to print except I should probably do each one of these at the same time so I'm going to print each one of these to the plus side, the minus, the multiplication, and we can get rid of all of our code there and just end each one of these items. Okay, so with all that in place we now should be able to run this and we should get for output elements one for when we add them, another for subtraction, another for multiplication, and then lastly for division.
Switch over and I'll run python originally was going to call this flexible counter. But I decided to change the name to dynamic reducer but I kept the file name. Now if I run this up. . . and looks like I have a little error. . . and let's see what it is. So I'm printing and it says syntax error invalid syntax.
Let's see what the bug is here. No, it's actually it's a bug with line 19. It would appear I need to close off. Notice how I have that parenthesis I didn't close off?
So now let's run this again and there you go that worked perfectly.
We have 6 for addition, -4 for subtraction, 6 for multiplication, and then we have that 0.1666 number for division. So this is working perfectly and if we passed in any other kind of value. So let's make this one 250 and then for multiplication lets make this one 55 and then for our division here will make this one 100 just so we can have some different values to look at.
and if I run this one now you can see that it has dynamically adjusted our sums working, our subtraction is still working, multiplication is working perfectly, and then so is our division.
So we did quite a bit here and let's kind of review just so it's clear because this is like I said earlier this is not a trivial exercise whatsoever. You had to combine a number of different Python libraries, you had to work with lambda functions, you had to implement a dictionary, you had to look up a dictionary with a value of a function that you had to call. That is also something that is not the easiest thing to do if you've never done it before.
But what we did is we called the operator library in Python. That's what allowed us right here to have this dictionary of operators where we could look up an operator and then simply pass in any of those operator methods like add, subtract, multiply, and divide then from there we also imported the reduced library.
Now reduce is what is called a functional component inside a python and what we mean by functional is it takes in a function and then instead of having to manually call each one of those elements and perform tasks such as you know another way that we could have done this and it would have been a lot more code to write is we could have said that we were going to set up some type of total variable here and set it equal to zero. And then we were going to take our collections so we do you know a for element in collection type of loop and then from there, we would do some things inside of it such as that's where we could perform that look up, and perform that computation.
We could tally up total and those kinds of things but that's not a functional approach. That would be just kind of an iterative manual way of building this out. What reduced does is it allows us to pass in a function and that function can perform any number of processes that we need it to in this case we wanted it to do something pretty basic where it keeps track of a total and then iterates over the elements.
Now, these are not special names I could have called this one X and this one Y. And then do the same thing over here this could have been X and this could have been y. There's nothing special about those names, just know that whenever you are working with reduce it expects a lambda function and whenever you have to implement reduce the first argument needs to be the total and whatever you want to call it is fine. I like calling total because I think it's clear whenever I'm coming back and I'm reading it so I'm going to use that and that is the first argument and then the second argument is the element that you're iterating over.
So the first time that it goes through this list element is going to be 1 and then the next time it goes there, it's going to be 2 and then 250. And so these are just arguments if you think of these like a normal function, then this would be just some function. And the way that it would work is the total and the element that would be iterated over would be those function arguments. So that would be the same as just saying total and then element.
When you're working with lambda functions that's how you pass in arguments you just say Lambda and then you pass in whatever values you want for those arguments. And then after that so when we pass in the semi or the colon here then this is the code block. So this is where we're performing our lookup on our operator's dictionary and then we're taking that total argument and then the element argument and then we're performing whatever operation there is and then from there we were doing that on the entire collection.
So if any of that did not make sense please let me or your instructor know and they can help walk you through every part of that because this is something that is important to understand. We walked through many different topics but they're all topics that are core things to understand about the Python programming language and working with advanced programs. So I highly recommend you go through until this starts to make sense for you and then you can move on to other tasks.