 As I make this video I respectfully acknowledge that I'm standing on the unceded traditional territory of the Comox First Nation and I thank them for the use of their lands. So in this video I'm going to sort of take things back to first principles. So if you're an experienced J user you may not be that interested although maybe you'll see some of the ways that I'm using to explain things and maybe they'll help for you to explain things to other people but it's not aimed at an experienced J user. This is aimed at a person who may not have seen J before and wants to take a chance and then try some things out and maybe see how a problem is approached. Well the problem that we're going to look at is consecutive ones and it's a problem that Bryce and Connor at the ADSP podcast have been looking at for the last couple of episodes and what they're trying to do is they're trying to come up with the longest contiguous which is all the ones in a row. So in this string you can see that you have one run here just a second. I'm going to make my cursor a little bigger. I'll just make that a little bigger and that'll be a little bit easier for everybody to see. Yeah that's a bit better. It gets to be crazy when you're trying to use it but it is way easier to see. So we've got this one here. We've got three ones, a zero, three ones, a zero, two ones. So the longest string of ones, in fact there are two of them, are these three ones here. And so using J I'm going to approach this problem and it breaks down to a number of things. So the first thing is that there is a conjunction called cut and what cut does is on this side and I'm just going to put in a U to symbolize a verb. Any verb there and a noun which I'll put in for N here, not any noun there, there's specific nouns that will affect how this cut is done. So what cut does is it takes a fret. Now what you take as a fret is a point at which you're going to break up this string and you can have choices about what you want to take as a fret depending on what the value of N is here. So say for instance I make the value of N2. If I make the value of N2 it's going to look at the end of this string and it's going to take this character as the fret. So there's going to be a break here, a break here, a break here and a break here and because it's two it's going to keep the zeros in the string. And so if I make it negative two it's going to do the same thing except it's going to take the zeros out. If I do a one, I do a one that's much better looks like a one, a negative one means that I'm going to look at the first character here and that's going to be my fret. So I would do a cut here, a cut here, here and here, a cut here and when I've done all those cuts I'm going to get rid of the ones because it's in the first string. And if I had just a positive one then it's going to do a cut here, here, here and it's going to keep the ones in. So how do I see that? Well I can use a verb which is going to take the place of you here and the verb I'm going to use is box and what box does is whatever I give it it's going to put a box around. So what's useful with cut is I'm going to put cut in the middle and now it's going to box in this case whatever this first character cut is and because it's a positive number it's going to keep them in. So let's just look and see what that that is for a. So there's a and there's our answer. So it's doing a cut on the one this one and this one and this one it's keeping them all in and then it goes all the way to the next one does another cut and a cut and a cut. So that's how it's cutting things up. Now if I was looking to find the strings in a row you know the contiguous strings it's pretty clear that I don't want to use a leading one the leading one as my my cut because it's cutting up all these strings. So let's take a look and see what happens if I do a two. So now I'm looking at the last character and I'm using that character as the cut and I think this is going to be a lot closer to what I want. So there we go it's got the ones and these three ones and then a zero it's it's doing a cut on the zeros but it's keeping the zeros in. Now you can take a look and see what happens if I do a negative two. I think it's going to come much closer to what I want and it's doing the cuts where the zeros were here here here and here and it's taking the zeros out. So now I've got boxed I've got one I've got three ones together I've got three ones together and I got two ones. So that's getting me a lot closer to where I want to be however there is a problem and what the problem is is if I use this value of cut and I don't have a zero on the end if I instead have a one on the end of this then this cut isn't going to work very well. So let's just see what that would look like. So I'm going to just put a I'm going to put a one on the end. Okay so one's on the end of it and so now I'm going to take this and I'm going to use this same cut here except now you've probably already figured it out. What's going to happen is it's going to take this last one as it's cut and I'm going to be back to chopping up all these ones again and there we go and that's what it's done. It's done all these cuts and then it's got rid of the ones and it's only left to zeros so that's not what I want. Next up is a trick that you actually get used fairly often in APL and the array programming languages and we're going to use it now in J. Before I put cut through on A I'm going to do something to A first and this is what I'm going to do. So I'm going to take care of this I'm going to take this out of the way for now I'll just keep A separate out here is the argument. So this thing here this whole thing here is now a verb it's a conjunction cut and it's going to box according to the end the last character and then it's going to get rid of those last characters those frets. So that's a verb and it's going to work on this A which is a noun which is just a string of ones and zeros and what I'm going to do is I am going to force the last character to be a zero well how do I do that well that's pretty simple actually I'm just going to do append and then I'm going to bind it to a zero okay you say well that's wonderful but isn't that really going to screw things up no not really and the reason is is because it's a negative two and if I put a zero at the end it's going to get rid of all these zeros at the beginning anyway so at the last step I shouldn't say at the beginning because the zero is at the end it's going to get rid of all these zeros when it takes this last step because of the negative sign in front of the two. So let's just try in fact let's just try this with us putting a one on the end we're forcing a one on the end and then we're going to put a zero after it and so it will give a different result than this because we'll have this extra one on the end but let's just take a look at it see what we get and there we go we've got the same first part of here and then we've got this extra one that we threw on the end here and that's why this shows up if I didn't do this if I went back to just doing it with A there we go I've got an empty space on the end for the zero that I threw in and then it got rid of the zero and when I got rid of the zero I just left an empty space so that's kind of my trick there so what what have I done I've taken A and interesting here let's take a look at this append verb just just really quickly so we can understand what that bind is doing so if I take um two two three and then I append six seven it's going to give me that list it's basically gluing stuff together nothing more than that but if I take um let's see that's the best way to show this if I bind this six seven to the append and then I take this argument this noun and I'm going to have to separate it because otherwise it's going to if I didn't uh if I didn't put it in parentheses here put it in parentheses and it's going to evaluate this and it's not going to mix them up with this six and seven if I had taken the parent parentheses out I'm basically going to not append but I'm going to ravel the um well actually I'm going to be waiting for an extra argument which is another story but essentially I have frozen I have bound this six seven to the append and so when I give it the argument of two two three I get two two three six seven so that's how this works A is going to come in it's going to bind to here and it's going to throw this zero on the end it's going to take that result and it's going to feed it to cut now let's find out about things called forks because when you try and put together verbs in J it's very important that you get them in the right order in the right positions and I'm going to show you an example of a fork it's a classic example of a fork and what that example of a fork is is this nope it's not that it's this okay and what this is doing and this sort of explains what a fork is about so I'm giving it a list of numbers this here everything within the parenthesis is called a fork and the reason it's called a fork is it has three verbs it has this verb it has this verb and this together makes one verb so it's got three verbs and the first verb here is called tally and what tally does is it counts the number of items in its argument and we have five items in this argument and this verb here it's addition and then with this insert it's going to insert a plus between each of these items in the list so I'm essentially going to sum up the items so I'll end up with two plus three plus five which is 10 plus say 14 which is 24 so this in the center now is going to take the result of this which is 24 and this is divided so it's 24 divided by the count which is five so let's see if we're right there you go 4.8 24 divided by 5 is 4.8 so that works important thing we're looking at here though is not these particular verbs but this verb takes this argument and produces a result and this verb takes this argument and produces a result and then argument and produces a result and this verb takes this argument and produces a result and this central verb takes the information from this argument and then processes it along with the information for this argument so this actually is getting the results of these two verbs on here so these outside tines are working on the argument and the inside one takes those results and then works on it so that's what is happening with a fork well take a look at this fork this is going to do something very different and what it's going to do is this verb here this is a single verb is called a cap and what a cap does is absolutely nothing it does not return any value so when you have a fork with a cap in it here you're going to do the number of items and then this is no longer going to be dyadic it's no longer to have two arguments it's only have one argument and when division only has one argument it becomes the reciprocal so what's going to happen is we're going to end up with this count which is going to give us five and we're going to take the reciprocal of five which is zero point two so i'm creating a fork but by putting this cap after it i'm now feeding the results of this onto the results of this why did i say that well take a look at what's happening here i've got a noun being fed to a verb being fed to another verb and so what i want to do is i want to create a fork i'll put parentheses around it and then i'll put a cap there and now the same as here the results of a having a zero appended is then fed to here and my result will be just what i wanted so that's the first step of solving this problem i have now broken the ones into groups but you know what i don't really want the ones i want the ones broken into groups but i don't want them just sitting there as ones in boxes i want to know how many ones there are in each box well all i was using box here it's a verb i was only only using box because i wanted to show you how it was breaking up the the of the string well it's easy enough for me i'll just get this down to the next line it's easy enough for me to change this box to tally now do you remember what tally was doing up here it's counting the number in the list and what cut is doing is it's breaking this list and getting rid of the zeros so that you see two ones and you see three ones three ones and one one well if instead of boxing which gave me this i do tally i'm already doing those those counts i'm looking at this and i see there's zero ones and there's two ones three ones three ones and one one so i've got again to repeat i'm putting a zero on the end and then i'm doing a cut according to the last item which is a zero and the cap allows me to feed through like this and now i've got one three three two zero so i have the numbers of ones in each of those sections and and because i've used the zero to cut the sections i know there's no zeros within those groups of ones there's zeros between the groups of ones but there's no zeros within them because if there was there would have been a cut in them so my next step is i want to take a look at this list and i want to find out what the greatest number in the list is the way i do that is i use a verb called greater of remember we looked at the insert before here i'm going to insert greater of between all of these numbers so greater of in between here is going to to the greater of two and three and then it's going to and that would be three and let's take the greater of three and four and that would be four and then the greater of four and five and that would be five but then it's going to keep on going it's going to say the the greater of five and three, well that's five, and the greater of, and then this five three would become five, the greater of five and two, well that's still five, so your answer would be five, as expected. So what I'm doing is I'm inserting this greater of in between all these numbers, and because I'm doing it for the full list, the only number that's going to come back is the number that wins out in every greater of contest. That's probably the easiest way to explain it. So looking back at this, what I'd really like to do is I'd like to take this verb and I'd like to feed it to greater of, insert it across my list. And if you look at this, look at what we got again. We again have a verb and a verb, which makes me think if I want to do a fork, and I do, because a fork is the easy way for me to define the verb I get in the end, put a fork here, and my answer is three. Now the other thing you might have noticed if you're really paying attention, is that these inside parentheses are not needed. Why? Well, because of the way J evaluates, and it evaluates right to left. So let's take a look at this, what it's doing. So I'm going to take this inside parentheses out. They're not going to be there anymore. So now what am I doing? I am putting A into this verb, and this verb here, and this becomes a fork. So this part here defines a single verb, because it's taking a group of three verbs. And the fork means it's just feeding the appended zero into the cut. And then this whole thing becomes a verb, and now I've got this verb and a cap. And so that means all of this verb here is feeding into here, which is what I wanted. And the reason I can do it this way is because J is looking for three verbs at a time. And so it'll process the first three verbs, and then it'll keep on moving. And because it's going to process the first three verbs, I don't need to have those extra parentheses in. I can put them in, obviously I had them in here. But if I take them out, I'm going to get the same result. So that is the way you get the answer to the contiguous ones. At least that's one way to do it. Now when Bryce and Connor were trying to solve this, they were trying to solve it based on using only one algorithm. And as much as I can figure, they're C++ programmers, but as much as I can figure from their explanations, they would regard this here, this cut, as an algorithm. And the algorithm there is partitioning my list into sections. And then they would regard this as a second algorithm. And the reason they regarded second algorithm is it's taking the result, and now it's applying this rule, the greater of rule, across that list. So you do one algorithm to do your cut, and then you do another algorithm to apply the greater of rule, and you'd use two algorithms to solve your problem. Now I have found a way that I only think that I only use one algorithm. And I can't be sure about that because I don't, again, I'm not absolutely sure about the way they're defining their algorithms. And it's just a derivative of this. So actually, first thing, now that we've got this as a verb, like this, I'm going to get rid of this argument, because now I can just, without an argument, I can just do an assignment. And that's the thing I can do in J. So say I call it T, and the equal dot is assignment. What I'm doing now is I'm assigning this verb to T. And that's one of the neat things about J. You can assign anything to anything else you want, and when you do it, that's what it is. So now if I went T, A, I get three, because now T is this verb. So I would like to take this verb now, and I want to change it just a little bit to see if I can make it do what I want with A. Now the first thing I'm going to do is I don't actually want this part anymore. The reason is, the reason I don't want this part anymore, is because that's a second algorithm. And so I'm going to get rid of that, and I'm back to this again. Well, there's really no way, if I have that list of integers, that I can actually get away without having the second algorithm. So the thing I'm going to change is actually here. So I'm going to change this tally. So I'm no longer taking the tally of the things that I'm breaking up. I'm using this, which is called self, and basically it returns itself. So I'm breaking it up into a group, and I'm just going to return itself. And when I apply that to A, this is what I get. When you look at that, and you go, well, with the negative two, I thought we got rid of all the zeros. Well, those zeros aren't coming in because of the negative two. They're coming in because J requires rectangular arrays. And that means that you have to do some padding. So if you're looking at a cut of this one, and you only get one out of it, it's just returning itself, then you have to pad it with two zeros to match up with this group of ones, which is three ones. And so this is three ones, and then this would be two ones and a zero, and this would be padded out with a zero. Now what's interesting about this, I've only applied one algorithm, and that algorithm is just return itself according to the last, cut according to the last character or the last digit in the string. And I force the digit, the N digit to be zero. So I've done those things. But look at this here. The largest string is always going to be all ones, right? So there's no way. If I had a longer string, it would go out one more, and then the rest of these would all be padded zeros. But the point is, is they would all have this array will always have the number of columns as the longest string. Which means this technically has already given me my answer. Wrapped up in this array, and it's a five by three array, and I can tell you that just by taking a look at this, and I'll use this dollar sign, which is the shape of verb. So it takes whatever it's fed, and it returns the shape of it. So it's five rows of three columns. And so all I need is that three for my answer. And if you're really ahead of the game, and I think you probably are, as soon as you saw this, a verb, and another verb, you knew right away that what I could do is put a cap on it, and it would still work. And now I've got almost there. I'm really almost there. In fact, if the thing I was looking for, if the answer I was looking for is the longest string of ones without zeros in the middle, and how many of them there were, I would be almost there. Well, I guess I really wouldn't, because these zeros here would really cross me up. But in any case, I've just got more information than I need. I just need this three at the end of it. And so the way I get the three here is I do this, which is take the last one. And it's called tail, and it's just taking the last of the list. And look again, I have a verb and a verb. And so to feed this verb to that verb, which is what I want to do, well, I'll tell you what. Let's just execute it once. Gives you my three. But we know already that if I really want to be clever, I'm going to take this, and I'm going to put a cap on it. And that is going to allow me to get the same answer. And this is actually a J verb. And doesn't that look funny? This is why some people call J line noise. There's an awful lot of colons in here. But those are just ways of differentiating this verb from one that doesn't have a colon, or might just have a dot. I mean, it just gives you a range of getting sort of more verbs in with the same character set. And also J quite often is cleverly taken, well, I'll show you, for instance, if I take this here, if instead of taking the tail, two dots, right, tail bigger than one dot, that's going to return five. So a lot of times J has grouped these things so that one dot and two dot kind of makes sense, along with the curly bracket on its own. But in any case, I wasn't that interested in the five. I just wanted the three. So now, again, what did I call it? Did I call it T? Yes, I called it T, the first one. I'll call it T1. Who said naming was hard? OK, so now I'm going to do T1, A. Great. So they both work. This uses, I'm going to say, because I really kind of look at it this way, it uses one algorithm and that one algorithm is this cut returning it same. And then once I've got that answer, all I'm looking for is properties of that answer. So I want the shape and I want the last or the rightmost number in the shape. And that gives me my answer. So that's why I'm saying it has one algorithm because really there's only one part that's acting on the argument. And that's this part. And then the rest of this is just working on the answer. You've already got the answer. You just have to break it out so you can get the shape and then take the last number in the shape. Whereas up here, this one is doing that breakup and then it's inserting something else across that list to come back with the answer. So what's interesting is that there is a verb that will take an argument. And the argument to its left is 1000. I can put any integer I want in there. And the argument to its right is going to be the sentence when I want to execute. And what time space does is it's going to execute TA 1000 times. And then it's going to return my average time to do it. And then it's also going to return the number of bytes that it took to do it. Oh, you know what? And it's not called time space. It's called time space X. Yeah, this was long before the days of space X, but who knows time space X. So that gives me 3.67 e to the minus seventh. So essentially 0.3 milliseconds. And it takes up a space of just over a kilobyte, just over 1000 bytes. So that's what time space X TA does. Let's compare it to T1A. Remember T1 was the one that only used one algorithm. And it uses up a lot more space and it uses up a lot more time. Now, earlier I created a noun called data, which is, you want to know how long it is? What do we do? We get the shape of data, right? So I get the shape of data. It's 10,000 characters, 10,000 integers. 10,000 binary integers, ones and zeros. And I will apply, well, let's do T data first. And it says the longest contiguous list of ones is 12. And I'll just try this as well with T1 data. And boy, I hope they're the same because if they're not, I'm in big trouble. They are, so that's good. And now let's take a look at the time it takes to do these. And you're going to see something that happens quite often. When you define something at a certain size and then as it gets bigger, different approaches end up with different efficiencies. So, still pretty quick here. I'm basically 11 milliseconds, 1.199 for e to the minus five. But I'm using a lot more space here. But let's try it with T1. The one pass algorithm. And this is like 2.2 times 10 to the minus four. I mean, it's essentially 20 times, well, no, 10 times, I guess, higher. Yeah, a little bit more than 10 times higher, bigger than the time it takes this. And then it's also almost 10 times the size it's taken. So why is it that one algorithm would, like applying only one algorithm would take more space and more time than applying two algorithms? Well, the key to it is let's go back to, let's go back up to, whoop, that's not what I wanted, I'm touching too many things at once. Yeah, let's go back up to here. So I'm going to execute this again. So look what's happening here with A. And remember A is this, it's this string. What's happening here is I'm breaking A into sections, but I am creating an array. So for every section in A, I'm going to have a three item row or three digit row. And I'm going to have five of those. So essentially I'm creating 15 spaces that then I'm going to work on after that. I'm going to find the shape of it. I'm going to get the second number in the shape. It's going to give me three. But the point is to get there. I've created an array with 15 positions in it, three rows of five. Now, if I go back and it's just as easy as changing this self thing to the tally. Now I've got five digits. I've got five digits. And when I apply that next algorithm, which is the creator of inserted between these digits, it's actually a very quick thing to do. I'm just comparing two digits at a time. I can roar through this really fast. It takes a lot more time and space to create this so that I can even find it's shape. So even though I'm only doing one algorithm here, I'm creating more work in doing that one algorithm than this case where I end up with a very reduced set of things to work with. I mean, I'm working with five instead of 15. And when you get up to 10,000, like in data here, and now we know that there are 12 deep. Well, let's do the, um, how could I do this? Let's take a look. I'm actually going to try and find the shape. So there's T one. Okay. So it's going to go rid of this. I'm not going to use T one, not going to assign it to anything. I just want to use this as a practice. I'm going to run data through it, but instead of taking the end of the everything, the last digit in the shape, I'm just going to find the shape of this. So I have 4,934 by 12 for that shape. That's the shape of what's coming out of self. So it's really big. If I change this, and I think you probably know, if I change this already to tally, all I'm going to end up with is 4,934. So right there, there's my factor of 12 in terms of the change in the size. And it takes longer to work with things that are larger. And so as a result, my whole algorithm, my whole solution slows down, even though it's only using one algorithm, it's working harder to create that one algorithm. Uh, whereas when I use two algorithms, I end up with a reduced, um, uh, set of numbers that I have to work with and the second algorithm just roars through.