Reflections

Blocks, Procs, Lambdas

DBC Phase 0 Week #6
December 7, 2014

    Block, Procs, and Lambdas? They're all ways to contain chunks of code that you want to run.
Here are some examples of each:

        
        numbers = [2,4,6]

        #----Blocks-------------------------------------------

        numbers.map! {|x| x}
        #curly braces above or the do/end below contain the block
        p numbers

        numbers.map! do|x|
          x / 2
        end
        p numbers

        #=>
        [2, 4, 6]
        [1, 2, 3]

        

Blocks are contained with curly braces "{ }" I'm sure you've seen these already if you've used .each or .map methods. They can also be contained within a "do / end"

We can do that by adding this line outside the class and running it:


        #-----Procs--------------------------------------------
        pro = Proc.new{|x| 3 * x}

        p numbers.map!(&pro) #converts pro into block before using it with .map!

        pops = Proc.new { |x| x + 5}

        puts "Calling pops Proc with an argument, returns: #{pops.call(3)}"

        #=>
        [3, 6, 9]
        Calling pops Proc with an argument, returns: 8

        

Procs must be created with a "Proc.new" before the block. Procs are blocks that can be saved and called later on. Here I defined a new Proc called "pro" that multiplies the variable by 3. On the next line I call the pro Proc on the map! method. The "&" symbol before the proc converts the proc into a block before using it with the map! method. You can call Procs with the ".call(arguments)". The arguments, if any, are placed in parentheses after the call.


        #--------Lambdas--------------------------------------
        delta = lambda{|x|p x * 2}
        numbers.each(&delta)

        p pro.inspect
        p delta.inspect

        #=>
        6
        12
        18
        "# Proc:0x0000000242e3a0@solution.rb:13 "
        "# Proc:0x0000000242de00@solution.rb:23 (lambda)>"

        

Lambdas are created almost like Procs except without the ".new"

If I inspect the proc and lambda I've created. It shows that both are Proc objects. The lambda has a special (lambda) annotation at end. Lambas are Procs but are a specialized form.

Here's an example of a way to implement Procs / Lambdas in to methods. :


        #------------method call ---------------

        def adding_procs(x, y, proc1, proc2)
          puts proc1.call(x,y) +  proc2.call(x,y)
        end

        adding = Proc.new{ |x,y| x + y }
        subtracting = lambda{ |a,b| a - b }

        adding_procs(10, 5, adding, subtracting)

        #=>
        20
        3

        

Here I created a method that adds the output of 2 blocks of code. It takes in a 2 variables and 2 blocks of code. Within the method, I call each block with 2 arguments that are the variables of the first two arguments of the method. I then add those blocks together. "adding" is a Proc that adds 2 numbers "subtracting" is a Lambda that adds 2 numbers Calling the "adding_procs" method with (10, 5, adding, subtracting) yields 20.

One difference between Procs and Lambdas is that Lambdas are stricter in the number of arguments called. Procs won't be as picky when extra arguments are given.


        #-------number of arguments - --------
        p adding.call(1,2,3) #no error
        p subtracting.call(1,2,3) #returns wrong number of arguments (3 for 2)

        #=>
        3
        ArgumentError: wrong number of arguments (3 for 2)
        from solution.rb:38:in `block in 
'

Here "subtracting.call(1,2,3)" returns ArgumentError: wrong number of arguments (3 for 2). This means that you gave 3 arguments when only 2 was expected. "adding.call(1,2,3)" doesn't give any errors even though it has 3 for 2 arguments. It will give an error if only 1 argument is given when it's expecting 2 arguments.

Another difference between Procs and Lambdas is the way they "return" when called in a method.


        #-----------return procs and lambdas---------

        def test_returnlam
          lam_ret = lambda do
          puts "in lambda then return"
          return
        end
          lam_ret.call
          puts "end of test_return method"
        end

        test_returnlam
        puts ""

        def test_returnproc
          proc_ret = Proc.new do
          puts "in Proc then return"
          return
        end
          proc_ret.call
          puts "end of test_return method"  #this line doesn't get printed
        end

        test_returnproc

        #=>
        in lambda then return
        end of test_return method

        in Proc then return

        

    In test_returnlam method, I create a lambda that prints text then "return" The lambda is then called and on the next line, it should print out some more text. When this method is called, it returns the text as expected. I created another method test_returnproc that is the exact same as the other method except a proc is used and called. When this new method is called, it only printed the text in the proc and not the text at the end of the method. What happened was when the "return" in the Proc was executed, it completely exited out of the block and the method in which it was called. Lambdas behave differently in that when the return, they only exit back to where the lambda was called and the next line of code runs.

    After learning about Lambdas, Procs, and blocks, it seems beneficial to use Lambdas over Procs due to the extra strictness of number of arguments and they way it returns. Rarely would you want to completely exit out of the method that contains it. Blocks are used when you don't need to repeat that block of code somewhere else in the program. Procs and especially lambdas are definitely useful in the DRY (Don't Repeat Yourself) format. It allows you to save a block for later use and can be called later on anywhere else in the program. Using these will help clean up your code and also let you call multiple blocks at one time. You can call a lambda/proc with an argument of another lambda / proc. You can create some intricate methods with procs and lambdas.