 Okay, so at this point, we've gone over a bunch of standard library collection functions. And we'll put some of them to use here in an example program. We've previously done rock paper scissors on the command line. Here's an implementation of a tic-tac-toe game on the command line. This one's a little bit more substantial. It's 10 functions long instead of just three functions long. But it's still quite simple, so let's walk through it function by function. So here are three of the first four functions. I'll start on the bottom one here. This function triples, which accepts an argument of the board. In our program, we're going to represent the board, the tic-tac-toe board, as a vector of nine elements where each element of the vector is one of the slots on the board starting from the top left and then going to the right row by row. So the first element of the vector is the top left element of the board. The second element of the vector is the top middle element of the board. The third element of the vector is the top right. And then the fourth element, we go down to the next row. And that's the left slot of the middle row. So we get to the last element of the vector, and that's the bottom right element of our tic-tac-toe board. And in this vector, an empty slot will have the value nil, and an x occupying a slot will be represented as the keyword x, and an o occupying a slot will be represented as keyword o. So in any case, what this triples function does is take our board, our vector of nine elements, and it returns a sequence of the triples of the board, the triples being the rows, columns, and the two diagonals of the board. Ignoring for now the call to concat and list, ignoring that part, look first just at this call to partition all. This call is where we get the rows, the three rows of our board. The partition all sequence function recall will return a sequence of sequences each made up of that number of elements, a specified number of elements in this case three. So it takes the first three elements of board, then the next three elements of board, and then the next three elements of board after that, and so on until it exhausts the entire sequence. And in this case, our board is made up of nine elements. So we get three sequences, each with three values. And of course, the first three elements of the board represent the first row, the next three elements of the board represent the middle row, and the last three elements of the board represent the last row. looking at these calls to take nth. First off, we get the first column with take nth3 board. The first column is made up of the values at index zero of the board index three of the board and index six. And the take nth function will return a sequence made up of the item at index zero, and then it gets the subsequent values by adding three each time until it exhausts a sequence. So we get the values at index zero three and six. Now to get the second column, what we want are the values at index one, four and seven. And we can do that if we first take our board and we drop the first element. So this call to drop one board that returns a new sequence, which is just like our board vector, except without the first element. So its index zero is index one of the board and so forth. So now when we call take nth three, we're again getting the elements at index zero, three and six, except now zero, three and six referred to index as one, four and seven of the original board. And then to get the last column, the third column, it's the same idea, we just dropped two. So now that what was index two of the board is index zero of this return sequence, which we passed to take nth. Now to get the diagonal, which runs from top left to bottom right, we want the values at index zero, four and eight. So we can get those values with a call to take nth, where the argument is four. But to get the diagonal that runs from top right to bottom left, we want the values at index two, four and six. And so once again, we drop two from the original board, yielding a sequence that starts at index two. And we could call take nth two on that new sequence and get back the values at two, four and six. But then we'd also have the value eight and we don't want that last value. So here after dropping the first two values of the board, we use the drop last function to drop the last two values of that returned sequence. And we end up with a sequence that runs from the original vectors index two up to index seven. We didn't need to drop last two, we could have just dropped lasted one, and gotten the same effect, it doesn't matter really. In any case, we end up with the top right to bottom left diagonal, the index is two, four and six of the original board vector. So now you should understand the call to partition all and the calls to take nth. What we need to do now is combine all of these sequences representing the rows columns and diagonals into one big sequence of these things. So first, we need to take all the individual columns and the diagonals and combine them into one sequence and a list in this case. And now that we have a list of all that stuff, and we have our sequence of the rows, we can combine them together into one big sequence with the concat function. So the concat function will return a sequence of all of our triples the rows columns and the diagonals. And so that's what this triples function will return the result of concat. So now that we can get a sequence of all the so called triples of our board, let's look at this top function triple winner question mark, which is passed in a triple. And if all the elements of the triple are X's, then it returns an X a keyword X value. If all the elements are O's, it returns a keyword O. And if it's neither case, if it's a mix of X's or O's or nils, then this triple winner function returns nil. First looking at the outer if form in this function, its condition is a call to every question mark. And the every question mark function is a sequence function that first takes a predicate, a function that returns out of true or false, and then the sequence in this case triple, every will invoke its predicate function once with each element from the sequence. And only if all of these calls to the predicate function return true will the every function return true. Otherwise, if any of the calls to the predicate function return false, then every will return false. So here the predicate function is actually a set. And as we discussed, a set can be invoked like a function. And when you call a set as a function, you pass in some value. And if that value is contained within the set, then the set will return that value. Otherwise, it returns nil. We could instead of this set, we could have a predicate function that takes in a parameter. And we test whether that parameter is equal to the value we're looking for, colon x, a keyword x. But this form, as you can see, is more compact to write. So this is the preferred idiom. And so in this condition, this call to every, if all the elements of the triple sequence are the value keyword x, then every invocation of the predicate function here, the set will return keyword x, which is considered truthy. And so that every function will return true. And when this call to every returns true, then this if will return the value keyword x. And so that's what gets returned from the triple winner function. If however, this condition tests false, if not all the values are equal to keyword x, then we reach this other if form, which has a very similar condition, except now we're testing whether every value in the triple sequence is the value keyword Oh, and if so, then we return keyword Oh, otherwise we return nil. So as we said, if all the elements of the triple sequence are the keyword x value, then this function returns keyword x, if all the values are the keyword Oh value, then this function returns keyword Oh. And if neither of these cases are true, then this function returns nil.