 Okay, so in the first video about dynamic pigeon, I covered really almost all the language. I left out a handful of operators. We'll get to some of those in this video, but this video is mainly going to be about just showing some practical examples of some very simple programming exercises using dynamic pigeon. Out of the gate though, I need to make some corrections because in the first video, I forgot I'd actually changed the language where instead of the append operator, it's called push. This is to avoid confusion down the line when you learn go because in go append is like push somewhat, but it's different. So instead we're going to call it push. That's how you tack things on to the end of a list in pigeon. And then the foredeck loop I explained incorrectly because it counts down through the end. It doesn't stop at the last, the end value it goes through it. So here's what that means. It means if we have foredeck i5-0, well, the first value is not five. It's actually one less than five. And then the last iteration, the value is going to be zero. So it's actually doing the test of whether i is less than or equal to this end value. I'm sorry, greater than or equal to this last value and it keeps iterating while that's true. So only once we get to negative one in this case is the loop going to end. So just want to make that clear. So our first practical example is there's a famous programming exercise called FizzBuzz. It's just a little test of very basic programming knowledge, very basic logic. And what we do in this exercise is we want to print all the numbers from one through 100, including 100, except for the special cases where numbers are evenly divisible by three, instead we'll print fizz. For numbers that are evenly divisible by five, we'll print buzz. And for the case where numbers are evenly divisible by both three and five, we're going to print fizzbuzz. So say 15 is evenly divisible by three and five. So instead we'll print fizzbuzz instead of 15. So first thing we need to do is obviously we need to iterate through the numbers from one to 100. So how do we do that? Well, okay, so here's just a main function, a program where we're iterating from one up through 100. We could write it like this where we have a local variable i to counter variable for a loop, initialize it to one, because that's the first value we want it to have through the loop. And then the last, to make sure this keeps going, while we're incrementing i here at the end, right? And to make sure this keeps going through 100, we make the condition less than or equal, not less than. For a less than, it would stop at 99. The last iteration would be 99, but we want it to print 100. So we want it to be less than or equal. So that's one way we could write the loop. A bit more compact though, is if we could use four ink, and just the important thing to keep in mind here is that for four ink, it starts with this value, i will have the value one, but then the condition is keep iterating while i is less than this value. So we actually have to make it 101 so that it will include, it's gonna be one greater than the last value that we wanted the i to half, right? So this is how we're gonna write our loop. Question is, okay, so how can I test if a number is evenly divisible by another number? Well, one way to do that is if you could get the remainder of division, if you could perform division and what you get back is not the result of division, but the remainder, then you could test if that remainder is zero. So if I divide three by three, the remainder should be zero, right? If I divide six by three, the remainder should be zero. So numbers that are evenly divisible by three, there's a remainder of zero when we do division. Well, the way the division operator works, the div operator works in Pigeon is that it always, it just gives you a single number, it doesn't give you the remainder. There's no remainder, it just gives you a fractional value. So if you divide, let's see, three by four, you get 0.75, right? So if you wanna get remainder though, there's an operator called mod, which stands for modulus, which is an operation in mathematics that's defined. It's basically, it's like giving you the remainder of division. It's a little more complicated than that, but for our purposes, that's close enough. So if we do mod n, these two variables, n and divisor here, whatever n is divided by divisor and we get back from this, the remainder, not the result of division, but the remainder. Anyway, so we've defined this function is evenly divisible and we're gonna pass in some number we wanna test for what is, if it's evenly divisible by something and whatever number we want to be evenly divisible by, we'll call the divisor. And so we do mod n divisor and test if the result is equal to zero. That's what this expression is doing and that's what we return. So this function will return either true or false. So here in main, for example, if we test is evenly divisible with seven for n and three for the divisor, well, let's see, seven divided by three is two with remainder one. So in here, this would return one and one is not equal to zero. So the function call would return false. So you should see this printed out. You should see seven is, oops, sorry. The condition will be false. So we're gonna skip down here to the else and this is what you're gonna see printed out. Seven is not evenly divisible by three. Okay, we don't really need, well, the question is, do we need this function? We'll come back to that in a second. But for now just, we'll have that function that we've defined here. And now we're going to go back to our loop, which is four inking from i starting at one up two, but not including one on one. And we're gonna need two local variables, by three and by five. And we're gonna test is, as we, each time we go through the loop, we test if i is evenly divisible by three and store the result in by three. And we test if i is evenly divisible by five, store the result in by five. And now we need to do our printing of like fizz, buzz and fizz, buzz. There's actually four possible cases. It's a mutual exclusive choice between four possibilities, print fuzz, print buzz, print fizz, buzz, or print the number itself, whatever i is currently. So this is the case where the number i is evenly divisible by both three and five and we're gonna print out fizz, buzz. Otherwise, if just by three is true, then we print out fizz. And if otherwise, if by five is true, then we print out buzz. And if none of the other things are true for number like two, which is not evenly divisible by three or five, then we're gonna print it out. So this is actually a working solution for fizz, buzz. There's a debatable point of style here of do we really need this function here? Do we, should we have this function or just take this expression and plug it in place? Is this really all that complicated? Well, they're competing schools with a lot about this question. In many cases, it'll seem useful to define functions even if they do something very simple because instead of having maybe a confusing expression where you scratch your head and look at it and say, well, what is that about? Instead, it'll have a more descriptive name. On the other hand, it's sometimes annoying to deal with a bunch of little functions or it's just not worth the bother. So, arguably, you might just wanna write the code this way where you directly put these expressions. Instead of having a separate function, we just plug this expression and just manually plug in the different values. This would be just totally equivalent. That's a stylistic question. But also here, we've changed something where this code is no longer correct because what we had up here, we had the test for by three and by five, that was first. Here, we're changing this around. We put the by three test first and now the code is wrong because what's gonna happen in the event that if we're a number that like say 15, which is evenly divisible by three and five, in that scenario, what's gonna happen is this condition's going to be true. So, we're gonna print out fizz and then skip over the rest of the stuff. We want it to print out fizz bus because this should be true as well but because this condition comes first, the order of these conditions are tested top to bottom, if you have an if or the bunch of elfs. And so, in some cases, like this one, it's gonna matter what the order is because in some cases, multiple conditions can be true and whichever one comes first, that's gonna be the one that executes. So, when you solve fizz bus, make sure to put this test first. Okay, here's finally something that's actually practical. A very common thing you want to do in code is take a list and extract out a range of elements from that list. So, like you have a list of 10 elements and you wanna get back another list which has all the same elements from say like index two up to say like index seven. You wanna extract out a sub list and so we're creating a function here called sub list which does exactly this and it takes as its first argument a list which we're just gonna call a and then it takes also an integer for the start index, we'll call that start and an integer for the end index, we'll call that end. And we're gonna get back, what we want back is a new list with all the same elements from start up to index but not including end. So say if start is two and end is six, then we're gonna get all the elements from index two up through but not index five. So we're not including six and is one past the last element we want. There's a reason for that. This is a common convention where you specify, when you specify ranges of things the last index is not inclusive, it's one past. That's, it works out to make common idioms in code, common patterns in code just a little more compact. It's something you just kind of have to see for yourself of why that's convention. Anyway, so looking through the logic here, well here, I'll just demonstrate some calls you have, we're gonna create some variable a that's gonna store a list. It's just a coincidence here that the list to sort of variable a has nothing to do with this a here, right? This is a totally separate variable of a separate function, right? They just, it's a coincidence. They don't have to be called the same thing. We have a variable a, we're just gonna store a list of strings that are just letters of the alphabet. So a, b, c, d, e, f, that's six elements. And we're gonna get a sub-list of a and if we specify starting index zero and end index four then we're gonna get back a list of a, b, c, d. So it's a list, you know, a is at index zero, b is at index one, c is at index two and d is at index three. And that's where we stop, one less than the number specified here. We don't go to four, we stop one before it. And then likewise, sub-list a, two, six, well it's starting at index two, which is c and ending at index five, which is f, et cetera. So I won't go to the rest. Well, okay, so here's an example. If we say two and two, then we get back an empty list. If the end index is less than the start value then we get back also an empty list. Okay, so looking at the logic in the function we create another local variable which we'll just call b and we'll create a new list and assign it to b. The list starts out empty and we want to iterate through all the elements in the list from index start to end. We're not gonna use a four each loop. That's, you know, if you're gonna iterate through an entire list it makes sense generally to use a four each loop. It's more convenient, but because we wanna go only through a sub-range and we're not necessarily going through the whole list then it makes sense to use a four rank where we can specify the start index and the end index. Okay, so what we then do in the loop is given, you know, we get the element at index i of a and we tack it on to the end of the list b with push. So we're pushing to be an item from a. So once we get to this loop that should be everything we need. b has been, it started out empty but each time to the loop we're adding another thing to it and by the time we get through the loop it now represents that sub-range specified by the caller of the function. So then we're done and we just returned b and that's the end of this function. So another very common thing we wanna be able to do in code is to take a list of strings and join all of those strings as we say into one string like going through them all concatenate them all together. But then often when we concatenate these strings together we wanna be able to say that in between all of them is some other string. So we have the join function here. We specify a list of strings which we call strings here and then a separator string. And if you look at the example usages down here we have our list which is a b and then cat and dog. If we join a join the list with an empty string an empty separator string we get back a b c cat dog with those spaces in between nothing in between. If we have join a with a string with a single space here that's what this is then we get back a string with all the strings of the list joined together but with spaces in between here the separator is comma space and so you see comma spaces looks like a comma separated list and then just for an arbitrary another example just two dots in between and here you go. So looking at the logic we're gonna have two variables s and last index we'll explain them in a second. But first thing we do is we check if the length of the strings is zero. If there are no strings in the strings list then we're just gonna return an empty string. We'll treat that sort of as a special case. And then we're gonna assign to s an empty string because in the logic here we're gonna go through the elements of strings and build up s each time through by concatenating whatever s currently has and then assigning that to s. It's gonna be very similar logic to what we did up here where we have this empty list and each time through the loop we're building up the list by pushing another thing to it. So logically it's the same thing down here. And we save two last index the length of the string minus one because it just works out if you, as you see we're using it in two places that's why I created a variable. Without the variable here I'd have to, I mean we could have written that way we could have just like plugged this in in place but I thought that looked a little ugly. So I created a variable and anyway so the reason we need last index here is because first thing we need to do is we need to go through all the strings of the list except for the last one. We need to treat the last string of this list like dog here as a special case because we wanna attack the separator. We wanna put the separator after each one of the strings except for the last one. So that last string is a special case. So we need to iterate through all of the lists and all the strings in the list except the last one what I've done is I've used sublist to get another list which is just like strings but has but is missing that last element. So this loop here is gonna iterate through all the element all the strings except the last one and each time through we're gonna concatenate what S is currently tack on V here is the string from the list of strings and then the separator and then that's the new S. So by the time we get done here S will be the joining of all of the strings with the separator except for the last string. That's the missing piece. And so what we do in the final statement here is we're gonna take what S is currently which is missing that last element and the last element and concatenate them together and that's our return value. So that's the join function. So very much like we wanna take a list and sometimes extract out a sublist a portion of elements, a range of elements from the list. We wanna do the same thing with a string where we extract out another string which is made up of some range of characters within the larger string. So for that purpose we create a function called subster as in substring and here for example we call it with hello world with index zero up to but not including index four then we get back hell and if we do so with index three to nine then it gets us back low comma space were that's six characters starting at index three up to but not including index nine and then if we call hello world with indexes five and six then we get back comma because that's index five right here, right? Yeah, zero, one, two, three, four, five. Okay and looking at the logic here we create a local variable called chars and we're introducing a new operator here. We didn't talk about this before the charlist operator given a string returns a list of strings where each string is a character of the input string. So given hello world you would get back a list where the first string is just a single character capital H the second is the single character lowercase e the third is just a single character lowercase l et cetera, right? So that's then stored in chars and we wanna get a subrange of that so we call the sublist function we defined before pass it chars and we want everything up from start at start up to but not including end and we're gonna store the result of that also in chars we're gonna clobber the previous list stored in chars with this new list and then finally we want to take all those strings and join them together so we get a single string, right? So we call join with chars oops and I forgot there should be an empty string for the separator there we go that's not proper. Okay, now there's a debatable point of style here about whether we need the local variable chars at all what you could do is you could take this expression this operation and plug it in where chars is used in the next line and it would logically be the same so we don't really need this line by itself at all we can get rid of that assignment and so it would look like this and I should fix the code up here so it's correct there we go and then we could do the same thing here we could take this and instead of assigning to variable just you know it's only used in one place later on so we could just plug it in right there and the results would look like this except we need to now take on there now this is correct. Yeah, so on the one hand this is more compact on the other hand you might look at this and say well that's kind of complicated for a single line of code I would look at this and say you know this is not really bad at all but if you did have a line that was too long then you would do the same thing we did here where you like take these intermediate operations assign their results to variables so they can spread the work onto multiple lines that's a very common thing to do with variables just be clear like strictly speaking we don't really need them you could just put this all in one line anyway so another thing we wanna do with strings is given a string we wanna search for a substring see if it's found somewhere within the first string and if so we wanna know where what index that substring is found so we define a function called index which does this it takes a string S and a substring SS and looks for SS somewhere in S and gives us back the index of where it's found so here for example if we call index with hello world and hell well hell there is a match inside hello world the sequence of characters does occur there starting at index zero so we get back index zero if we look for hell lowercase h well lowercase h and capital H are not the same character so there actually is no match anywhere in the substring index will then give you back negative one that's the convention to return negative one indicating that there's no match and then for world well there is a match starting at index what is that five six seven yeah that's index seven and then if we look for oh you can the substring can just be a single character that's valid and in this case well there's actually multiple matches there's index four and there's index let's say five six seven eight yeah index eight so the question is which one does give you back well it searches from the front so it gives you back always the first match if there are multiple matches in some cases you might wanna have another function like say called index all which will find all matches within a string and would return a list of numbers instead of just a single number but the index function just returns the first match from the left you also might want another function that searches from the end of the string and gives you if there's multiple matches it gives you the one at the end I don't know maybe call that like R index as in like right index anyway looking at the logic of the function we're gonna create local variable SS2 and this is very debatable people complain that you shouldn't have variables with numbers to distinguish them because we also have SS and now we have SS2 that is generally bad style but I would say in this case it's a very short function there's really not anything obvious that you would call this variable anyway what else you would call it it's just another substring so I'm calling it SS2 that's generally something you wanna avoid in particularly in longer functions where your variables like have to have meaning across a larger context and it can get confusing if you have a bunch of short variable names that are just have numbers after them but I think it's cool in this case anyway so the logic of what we're doing here is we're gonna have a loop where I iterates from zero up to but not including the increment of subtracting length of the substring from the length of the larger string and yes we are going to assume that the substring is smaller than S maybe we shouldn't actually we probably should actually what would happen in that case what would happen actually no, yeah if that were the case if our substring is longer than the string itself well then obviously there can't be a substring match we wanna get back negative one so if say substring were 10 and S were nine then the length of the string were nine this would be negative one you incremented to zero and then we'd go incrementing from I is zero up to but not including zero so it would never iterate because I would already be equal to the end index so that would actually work that would actually be okay anyway the reason for this complex logic is we're not we're not gonna iterate through all the characters of the string we only wanna we wanna start at index zero and keep going but we stop once we're at the last index where there could possibly be a match if our substring is four characters long then once we get to the index where there are only four characters left that's the last thing we check it doesn't make sense to keep going past that and in fact in this case because in the loop here we're gonna call substring we didn't talk about this before but the way we wrote the substring function is if we specify an end index which is or a start index this is outside the bounds of the string if it's, you know if you have a five character string you specify an index 10 that's outside the bounds well substring is gonna fail and it's gonna terminate our program actually it's a bad operation so we wanna avoid ever calling substring with indexes that aren't valid for the string so for that reason and also to avoid unnecessary work instead of iterating through all the indexes of the first string instead of just going up to the length of S we are going to do all this where so here's the logic so this is a hello world is 13 characters world is five characters subtract five from 13 you get eight okay so if we're incrementing up to we're not including index eight we would stop at index seven which would be five, six, seven we'd be stopping here when we wanna stop actually at the O it's a bad example I know it's confusing we wanna stop at the point at the index where there are five characters left so it's actually one greater than the subtracting length of SS from S that's why we have the ink here so now we have I which is gonna iterate through in this example from here all the way through R and each time it iterates we wanna get the substring in this case the four character substring starting at the position at I so we're gonna get the substring of S starting at I but then the end position that also changes along with I that's whatever the length of the substring is plus I that's the substring we're gonna check so the first time through here in this example for world we would get this substring of the first five characters and then the second iteration loop where I is one we get this substring of characters and third time through you get this substring of characters, okay that's what's being stored in SS too, okay and so with that substring we then check if it's equal to SS the substring we're looking for if so then we found a match and we wanna return whatever I is so in this first example in the first time through I is zero we get the substring and hey it's equal to SS so we return zero we return I otherwise what's gonna happen is just we keep going we go through all the indexes of the string up to the point where the substring is equal to the number of remaining characters and we're not going to find a match so this condition is never gonna be true the loop will end and okay it turns out we went through the whole thing match was not found so it must not be there so we return negative one so the logic of this one is slightly tricky you just have to keep in mind that you don't want to iterate through the entire original string that's what all this complication is otherwise it's not horribly complicated arguably you might want to remove the use of this other variable we don't really need it again we can do the same trick we just take this expression plug it in where SS2 is used cause it's only used in one place and so we could just write the code this way and it would function exactly the same and arguably this is easier to understand cause you don't have to think about what does SS2 mean what does its role there are trade-offs here in some cases introducing variables that you don't strictly need on the one hand maybe it helps you make individual lines easier to look at easier to read but on the other hand introducing a new thing with the name and if the reader of your code has to think well what is that about and you have to worry about what might happen to that variable as particularly in a longer function you have to worry about what might be done with that variable over many lines potentially in this case I don't really have a strong preference I think they're about equal in terms of readability on the one hand this is a little complicated but then over here you have to introduce another variable and always I find it a little displeasing so weighing the trade-offs I think they come out about the same now another objection to this function is that it's really not the most efficient way to implement this index operation because well the main culprit is the substar function because we're extracting out a whole substring which in fact the way we implemented the substring function is not the most efficient in the world because we're like creating a list creating another list from that and then join it into a string and yeah so I mean we don't have to sweat efficiency details certainly at this stage of your programming education I mean this is an educational language and we're doing trivial exercises but I do want to give you a sense of down the line if you do start writing real code you might want to avoid glaring inefficiencies like what we're doing here so our alternate implementation of index looks like this we're introducing a variable match which we'll talk about in a second and the first thing we do is we take the string and the substring and we get their chart lists in fact we're not going to need the string and the substrings themselves we just want their chart lists because we're going to take the corresponding characters individual characters and compare them against each other so the outer loop here looks exactly the same as in our original implementation it's going up to the position from zero up to the last possible position where the substring might be found that's what we're doing but then we're not using substr instead we set match to be true we're going to go through all the characters of SS we'll get it as CH here and then if we find a case where it's not equal to the corresponding character of the root of the full string then we know we don't have a match we're done for checking that particular position so okay to be clear here exactly J will be the index of SS so if SS is five characters long in this inner loop J goes to zero through four and it does that every time you know every time we go in the outer loop this inner loop J is always going to go to zero through four so we want to get the character which is at the corresponding position of the substring in the full string so that's what this is about we're taking whatever I is adding it to J and that gets us the corresponding character of the full string the original string is now S here which is a char list it's a list of individual character strings so we're going to compare it against CH and if they're not equal that's what any Q I don't know if we mentioned this before any Q is just a convenience it's an equality operation but logically inverted so if these things are not equal it returns true so if we see that they're not equal then we set match to false we break out of the loop I don't think we've mentioned break statements either a break statement is a convenience that lets us bail out of a loop early normally a loop you know there's a condition something governing how many times it's going to iterate well sometimes you just want to say I don't care just end the loop right now that's what a break statement says so because this break statement is inside this 4H loop it's going to break if executed it'll stop execution of the 4H execution will just jump from here to whatever's after the loop in this case it jumps down to this if statement so that's break it's quite convenient in some cases so when we get down here if this loop iterates and checks every character of the substring to the corresponding position in the original string and they're all equal then this condition is never going to be true match will never be set to false it'll still be true and so when we get down here it'll hey we found our match and so then we return I otherwise if it's not a match then match will have been set to false we'll have broken out of the loop and we get down here this is going to be false and we go on to the next index of the full string anyway so that's an alternate way of implementing index and when it comes to performance you always have to be careful about making these judgments you have to really measure things to know if you're making things better or worse but my guess is that this version would be considerably more efficient than the original version we had because it's only creating two lists it's not using charlist and join a bunch so it's almost certainly going to be more efficient anyway moving on so another thing you might want to do with a string is test whether it contains one or more certain characters and for that we have a contains any function which takes a string and then also a list of strings where those strings should be individual characters, single character strings and so here down in this example we have contains any with hello world we want to see if that string contains any character in this string, qwerty so we get a list of characters from qwerty and this call will be true because e is here right here and also r is also found there in world so that's true but then if we have zxcv if we try to find any one of those characters in hello world where it's none of those are found so we get false and then here we're testing if just an exclamation mark by itself is somewhere in the string and be clear this is a single character string already but the function is expecting a list of strings so even if you're looking for this one character you still have to have a list of strings not a string passed in the logic here is quite simple we're just iterating through all the characters of the outer list so s is a string right and we want to get the individual characters so we create a char list right so each time through ch will be a character of the outer string of the of the main string we're searching through and then we need to test we want to get the individual characters from the char list uh... from chars so we iterate through all those chars we call them ch2 we're just trying to find any match whatsoever right so the first time we find a match between ch and ch2 we know we're done so we just return true so if we get through uh... this outer loop and we haven't returned true then we know that there's no match and so we return false for our last exercise something we might want to do with strings is to trim them as we say which means to hack off certain characters found at the beginning of the string and at the end of the string most commonly you want to get rid of leading spaces and trailing spaces but sometimes you want to get rid of other kinds of characters so instead of just having a function that hacks off spaces will uh... generalize the solution to uh... work for other kinds of characters so the way the function works is expecting a string which we want to trim and a so-called cut set which is individual string but first thing we do in the cut set is uh... we create a charlist out of it because we just want to consider the individual characters uh... so looking down here uh... the first example trim it's a little hard to look at because the be clear this is the first string and then this is the second and the cut set string is just a single space character so we want to trim from this first string all the spaces from the beginning in the end uh... and so the result is hello world with no spaces around it whereas here if we're trimming E space exclamation mark H then we're going to hack off from the beginning everything all the characters that are in the cut set so we're going to hack all this stuff off and going from the end we're going to hack everything off up to the D so we get back low world uh... in this last example we're we're hacking everything off the same deal except we're not hacking off spaces and well we get back the same original string because if you if you go from the beginning and stop when you encounter something that's not one of these characters well space is not one of those characters so we're done uh... and if you work from the end well same deal uh... first characters of space and the spaces in the in the cut set so we don't actually cut anything in that case we don't trim anything so looking at the logic is probably the most complicated function so far our strategy is that we're gonna search from the start until we find a character which is not in the cut set and then we're going to search from the end and find the position of the first character that's not in the cut set once we have those two numbers start and end we have two local variables start and end and what we're trying to do is find what the start and end should be once we have those numbers then we can get a substring of S and that's our result at that point we know where to cut and so we use substar to uh... get this string like in this first example you want to extract out everything after the spaces in between the spaces so what we're going to do when we search from the start is we're going to iterate from zero up to but not including the length of S so the length of the full string and inside here we'll first draw attention to get char is another operator we haven't talked about before it just uh... given a string it gets us an individual character from that string it's like a subster but it only gets you a single character string instead of a substring and it's built into the language anyway so for each character we want to see we're starting from the left we want to see if like this first character is match matches anything in the cut set so we're going to use contains any which which we just defined uh... to see if this string the individual character of the the full string is matching anything in the cuts at any of the individual characters in the cut set and if not if we encounter character which is not in the cut set then we know we're done we found the start of the part of the string we want to keep and so we assigns start to whatever value of i is and we break out a loop because we're done now it's possible in this first loop that what if the string you're you're trimming consists entirely of characters in the cut set so you're going to trim everything well in that special case uh... will have iterated through this full list and we won't have found a start at all there is no start and so start will still be nil because start hasn't been assigned anything yet and so we check if start is equal to nil and if so we know we're done and we're going to return early out of the function we're going to say oh well you're going to return an empty string so we're trimming the whole thing we're trimming the whole string and so we return just an empty string otherwise assuming start has been assigned some number and it's not nil then we're going to continue on and we're going to start searching from the end and so now we need to iterate backwards through the string so that's why we have four deck so we can go backwards and we're going to have another counter i starting at one less than len s the first value of i is going to be whatever is len s minus one and then we're going to keep going through the value zero we're going to keep documenting until we get to negative one in effect and so we're effectively going through the string backwards right to left and the logic is exactly the same except when we find a character that's not in the cut set then we set end to whatever the value of i is plus one because when we set end because it's going to be used down here you know if this exclamation mark in this example is the last character of what's supposed to be included in the string then we want that position plus one when we call substar right because substar that last index is one past the character we keep anyway so now we have our start and end and last thing to do we just need to get a substring from s from start up to but not including end so that's the trim function and that's the last example for this video i just want to quickly remind you that we snuck in some new stuff the charlist operator and the get char operator and also break statements those are things we hadn't discussed in the previous video at all next video is going to cover the number guessing game and a hangman game so these are going to actually be interactive programs where at the console at least you'll type input hit enter and your program will process your input and spit out output in response and that's going to be next time