Ruby Blocks, Procs, and Lambdas 🦁🐅🐻

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up in this episode i wanted to talk about ruby blocks procs and lambdas uh and just go through a bunch of things about bloxbox and lambdas that i think might be useful as you're writing some ruby codes this will be for a beginner rubyist and yeah so let's just jump in so a block is the that sort of bit of code that usually starts with a curly brace or starts with a do end if you've ever used an array and the each method on an array then you have used a block before so let's just let's just get a little array here going so i'm going to say maybe we have a list of students grayson logan steve and tony or something like this right and then if we wanted to like loop over those students and print out their names we could say students dot each and then we could use the curly brace and we could say puts student like this uh and so the part of this this line here that is the block is the part that starts with the curly blaze the curly brace and ends with the curly brace here now if we were to run this code we're gonna see that it prints out grayson logan steve and tony now the each method so the each method is a method on array and that each method accepts a block as an argument the same way that we would call maybe like students dot or maybe you have you maybe you have some method that's like do something and you wanted to pass it uh you know the numbers one two and three as arguments one two and three here are arguments to the do something method a block itself is also an argument so you could actually write do something in ruby without the parens and it would pass these arguments to the do something method the same way we are passing a block now we can only pass one block using the block syntax to a method technically you can pass multiple different little bits of code that can run inside of a method if you wanted to but you would need to pass at least one of them as a or only at most one of them could be a block passed as a block just like this so what i want to do now is i can rewrite this block that we're passing to each as a do end statement and we want the body of the block to be inside and usually indented it should be indented now in practice if if you have a multi-line block so if we're going to say like puts student length or something like that maybe like the length of the student's name we could do this now the the variable name here is kind of like an argument that you would pass to a function so when we say do end or those curly braces it's almost like we're defining a function in that we are defining a little bit of code that we want to run for every single instance of a student in this case when we're when we're talking about each now this little block of code is almost like a function it's not a function and it works differently than a function but it's kind of like a bit of code that you want to run and this part here is similar to a function's arguments so between these we call these pipes between the um these like vertical bars here this is the the list of arguments now there's some blocks that can accept multiple arguments so for instance each with index accepts a block that takes multiple arguments so we can say student and we can say i which will give us like which number student that is so if we say like puts i that will give us the index in the students array each with index oh i'm missing an x here each with index okay so this is giving us the index into the array as we iterate over it so this is these are going to be sort of the arguments to the block now let's talk about how this is actually implemented under the hood right how can we write our own method that accepts a block well if we wanted to we could say like def each here we can make our own method called each and maybe it takes as the first argument the array and as the second argument it's going to take the block that we want to execute on the array now there's a couple of helpful things that we can do inside of here but let's just look at what we might do so the each method inside of students i'm going to implement it without using the underlying arrays each method just so that we can look at how it might be implemented we're going to use a while loop because that's like a more primitive way of iterating over a list of things so let's say i is equal to zero and then we're going to say while i is less than array.count we're going to say i plus equals 1. so we're going to increment i and we want to do something inside of this right so if we wanted to we could just say puts like i'm in the array or like i'm iterating right and if we call each here and we pass in the first argument being students and for now let's just pass let's just pass in students and we'll say that our each method just takes an array as an argument now if we were to uh to run this block we see i'm iterating i'm iterating i'm rendering so it's going to run this while loop is going to run four times that's correct because this array of students has four elements inside the array and it's going to start off with zero so i is going to be 0 at the top of this while loop and then it will be incremented to 1 then it'll be 2 then it'll be 3 then it'll be 4. so let's actually print out what i is also so i is equal to and then we'll say i just can see what i is as we're iterating okay so we've got i 0 1 2 3. so we can actually use i to index into the array and get out the element so we'll say the element is array at index i and that will give us the the element also so we can say l is equal to and then we'll just print out l okay so this is going to give us what the element is at that at that index in the array so here we see like um at index 0 the element is equal to grayson so now we have l is equal to grayson then at index one it's equal to logan so on and so forth okay so now we have a method that at least can sort of like loop over an array but at this point our each method does not accept a block argument so the first thing that we can do is explicitly pass in a block argument and it's going to start with this ampersand that ampersand is going to do a special thing it's going to convert a block into a proc so when we pass in a block down here we're going to say each students do student and then we'll say puts student end so this block here is going to be this bit of code so this bit of code between the do and end is a block we're passing that as a block and we put this uh this ampersand or pretzel looking thing in front of it so that it converts it into a proc and a proc is something that has methods that we can call so what we want to do now is say block.call and we're going to pass in the element itself so i'm going to remove that put statement and so this block.call is going to execute and pass the element so recall that this element here is going to be the uh the student right so it's going to call the block so block.call means like execute this thing right an element here is going to be the student so for the very first time we go through this while loop element is going to be grayson and that element is going to be passed as an argument into this block and we'll be able to reference whoops we'll be able to reference the element by using the student name here because this is the uh the parameter or the block parameter sort of like the name of the parameter that we're accepting and we're going to print that out so let's give that a whirl so we'll say ruby blocks.rb and now we see grayson logan steve and tony so that is executing our block that we passed as an argument to the each method okay so this is one way that you can do it i like doing it this way um using this explicit block argument however there is an alternative and that is to use the yield keyword yield uh why okay so instead of passing the block argument explicitly i'm going to say yield and then i'm going to pass the element like so now this will also work it's just an alternative um to the block argument i i think yield is probably a little faster if i recall correctly but i like using the block argument because it's explicit you can see like when it when it's written with yield you have to read through the entire method body in order to identify that this method accepts a block if you didn't know that this method accepted a block or that you didn't if you didn't see the yield method then or the yield called here the yield keyword then you might not actually know that this method accepts a block so if there's also another helpful sort of underlying method that you can use and that is block given so you can say um if lock given then we want to like run all this stuff so maybe if like no block was given we can puts like no block given and then we can return early okay so if we were to just call each without any block down here we're just going to call each with students then this block given question mark method is like a built-in thing that will check to see if there was a block passed or not and so here we can see that no block was given this allows you to say like um you can you can add like default functionality that will say if there was a block then do like execute the block and get the default value if there was no block then don't do or you know you can you can basically build in defaults um that can fork based on whether or not a block was actually passed in okay so um all right so we've got a block we've got our block arguments and we're we've seen yield let's also go back to um passing in the block here we'll remove our checks at the top and we'll go back to block.call um okay this is all this is all great now let's take a look at procs so a proc proc.new is another way to just create a block of code that will accept some arguments and can run and do stuff so if we were to say like proc.new student and we could say put student right like this is a very simple proc and here we might say like print like print block or something is equal to this or print proc and then here what we could do is say students and then i believe we can say ampersand print proc and that will convert it from a proc to a block pass it as the block argument and then it'll convert it back to a proc before it runs so we can run this and we get back grayson logan steve so the the there's not very many differences between the two um yeah so uh let's see so there is so this is one way you can you can create a block independent of where the method is called so you could create a proc like much high at some higher level and store that off into a constant and that's going to be some little block of code that you want to pass around and use frequently you might use a proc for that an alternative is to use a lambda so you can use a lambda which is a special version of a proc and we can use we can use the the arrow keyword here and student this looks a little differently and i believe this should also work yep so this is the same same sort of deal but we use the arrow keyword instead or we can use lambda lambda the lambda keyword and i believe this will also work so lambda and the arrow the arrow format are another way the difference between a lambda and a proc is in uh there's a couple there's a couple different um there's a couple differences number one a lambda is stricter about the number of arguments that are passed so right now our block is only being called with one or our proc or whatever as being only called with one element so if our lambda expected two arguments here so if we said like student and i then this is going to fail because it's getting it's receiving the wrong number of arguments a lambda is more like an anonymous function that's being defined than it is um a a proc or a block of code so a lambda's first difference from a block is that it's more strict about the number of arguments the second the second difference with a lambda is how it handles the return keyword so if we are let's see we're back to normal this is working again you can actually say like uh let's make this do and and then we'll return student so we can return a student from a lambda and that's what will be actually returned uh when this thing is called here so let's say that like we have like our um yeah anyways let's say we have our value is equal to this and then we can say puts val down here so we're going to we're going to print out the students and we're also going to print out the values so right now we're getting duplicates of all of them if we don't have a return statement there then we're just getting back nil because we're not actually like returning anything from the from the lambda um but if we made this into a proc then it's going to implicitly return the last statement so if we go back to proc proc.new do and then this is going to say student then we should get something different here um in that if we say return inside of the proc or inside of a block it's going to return out of the context in which the block was defined so if we say return student this is going to try to return from main it's going to like try to return out of this out of out of this scope here so this is going to fail because you can't return out of main i thought it's going to reach oh right okay so this is this is um right okay so this is returning the student which is putting the value and it's also returning out of the execution because it does not want to continue the execution of maine so we're we're calling each dot students we're calling each students um and it's returning out of that function after seeing the first student so this is like a way to like break out of a loop right so if you were saying um if we go back to our example above right so one thing that you could do is as you're iterating over students you could say like if student equals steve like return right or return if student is equal to steve this return statement would break out of the enclosing function um hopefully that makes sense uh okay so the last i think we've gone over blocks procs and lambdas at like the basic level there's a couple other things that i wanted to show though um yep so let's let's just uh remove this all right so the next thing i wanted to demonstrate is that you can write a method that will execute a block multiple times so i'm going to say def do thrice and what we're going to do is we're going to accept a block here and inside of our do thrice method we will just execute block.call block.call and block.call and so it will do something three times when we call when we pass it a block so we can now say do thrice and if we say do i don't know maybe it doesn't take any arguments and we'll just say puts like say hello now when we execute this function we're passing the block and it can actually execute the block several times so now we can say like say hello and it will run that block three different times similarly you can do the same thing with yield but this yeah this can be interesting to encounter the first time that you see it is it's actually like yielding several different times another thing that's interesting is that you can um in the same way that you can sort of do argument destructuring in the um in the parameter list for a function so if we had a method here called pair and we wanted to pass it in a tuple or some sort of like two element array here maybe we have like x and y um and we're just going to say puts x and puts y this is going to be a pair of items maybe which one like print out the pair of items and we can say pair and we can pass it an array of one and two right and now when we pass one and two those will be destructured because we're using these parentheses here like a double set of parentheses this inner set of parentheses in using x and y is going to look at this array and it's going to destructure the the array into elements x and elements y and so 1 will be assigned to x and 2 will be assigned to y so let's take a look at how that works so now we have one and two so it's printing out one and two and just to kind of like drive this issue home let's make z another thing over here and maybe we'll pass like three and four as arguments to our pair function and now we'll see let's actually also print out z so we can see what this looks like so we can see that like x and y were still attached to one and two and it just discarded or didn't care about three so three wasn't uh we didn't have a third um a third parameter that we were destructuring into uh and then it it knew that passing four was actually like the second argument because the first argument is this array that's being destructured into this first parameter and the second argument was a 4 that's being destructured into or not destructure but assigned to z so now we see 1 2 and 4 as our x y and z similarly we can do sort of the same thing with blocks okay so if you have a if you have an array maybe we have our students here and they're they're actually tuples where it's like their name and also like their score on a test okay so now we have a 2d array where we have their name and we have their score on a test and what we're going to do is say like 72 and tony stark is pretty smart all right so our students now as we iterate over our students there's going to be two two arguments there okay so we can we can do like uh students.each do and now if we wanted to each student is going to be in an array right like the elements of students each of the elements of students is an array so if we wanted to we could say like student array right and then if we p student array then we're going to get back that array object because that was like an element of it right did i miss a comma i did okay let's see all right try this again all right so we've got the elements of the array fantastic but we could also destructure as part of our arguments to this block here so we can say like student comma grade and that will give us those those elements destructured into the student and grade so we can say print the student and then also print the grade and that will give us those separate values so this is how we can sort of like break it out into individual arguments that can be handy as you're like iterating over different weird combinations of things yeah so another way that blocks are commonly used is when you're initializing instances of things so um for like lazily instantiating something so for instance like uh if you wanted to initialize an array or let's let's talk about the the hash the hash version first so if we wanted to have like a histogram of um where we're like counting up the instances of something let's say these are going to be responses to a questionnaire where you could enter in like between one and five so let's say answers is equal to this list and it's just going to be a list of people's responses so we've got some ones we've got some twos we've got some fives and other threes and what we want to do is count up the instances of how many answers how many people answered one how many people answered two how many people answered five how many people answered three and so we might say answers dot each do uh answer and then we can say count or like if counter at answer or like that has key answer then we can say counter at answer plus equals one else counter at answer is equal to one right so this will give us like a way to sort of um check to see if so the counter is an empty hash right now and when we encounter the first element of the first answer right number one we wanna see if counter has the key of one and if it does then we're going to increment the count if it does not then we want to create a brand new key inside of the hash with the value of one and then we'll print out maybe we'll say like puts counter at the end here all right so let's take a look so we've got one one uh four twos one five and three twos so that is that's correct right that's exactly what we want but another common example for using a block or passing a block as an argument is to use it as a way to lazily instantiate something and so hash there is a way to initialize a brand new hash and pass a block to the new function so hash.new where we can pass in some arguments here the the underlying hash itself and a key that we've never seen before and we can say that the hash at that key value is equal to zero okay so this is going to say that like if i index into counter with a key that i've never seen before then it's going to create that key and set it equal to zero so if we were to like actually just pull up pry here and say maybe counter is equal to new and we pass it this block that accepts two arguments it accepts as the first argument the hash and as the second argument the key that was never found before and then we can use that key to index into the hash and set that equal to zero then we get back a counter object and we can say like counter at maybe like a or something and that will now say counter if we look at counter now it has like added a as a key into counter this is really handy if we're if we're trying to solve the problem we did before with this uh with this um sort of histogram thing that we wanted right and so we can actually now just say counter at answer is plus equal one right and we don't actually need all five lines here because as soon as we lazily access a key that we've never seen before it's going to run this block and when it runs that block it will set the value at k in the hash to zero and then we can increment by one because we're adding and or we have encountered that key now and so we're going to like actually see that oops all right let's see the demo all right so we get back the same exact result as we did before but now in like much fewer lines of code or like i don't know three fewer lines of code so uh but this can be this can be handy when you're trying to initialize uh hashes and arrays arrays is same deal right you can say like uh i don't know it's common to have like a 2d grid for a board so you might have your board as array.new and then you want to pass or like every time or new also accepts an argument and that'll be like how many elements in the array and then you can have a block that runs that will execute anytime a new element is created uh when you're initializing the array and so you can say like array.new9 or something like that so that'll give you like a nine by nine board uh that's just empty so let's just take a look at that real quick and so that's like an interesting way that's hard to read let's make it uh like two by two or something like that all right um cool so now we have like this two by two array now if we wanted something to be initialized in each of these elements then we could again pass a block here that was just like i don't know like empty or something right now when this when the when the two elements of the inner array are initialized it will execute this block and use the implicit return for whatever whatever is actually in here so if we run this again now we will see the string empty in all of these so um yep so that's kind of like some of the common use cases for blocks is lazily loading things for iteration for passing around stuff so hopefully hopefully this is useful and if not maybe a little interesting and yeah otherwise we'll see you next time
Info
Channel: CJ Avilla
Views: 1,201
Rating: undefined out of 5
Keywords: Ruby blocks procs and lambdas, blocks procs and lambdas, ruby blocks, ruby procs, ruby lambdas, How to write a method that accepts a block, block_given?, ruby block_given, How do ruby blocks work, ruby, blocks, procs, lambdas, ruby tutorial, ruby for beginners
Id: SADF5diqAJk
Channel Id: undefined
Length: 25min 42sec (1542 seconds)
Published: Thu Jul 01 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.