All articles

Block vs proc vs lambda

Category:

Block

A block is code inside do and end or { and }. It’s not an object.

A block alone is a syntax error, it can only exist as a part of method calls.

{ puts "lol" }.call
# BAD! Syntax error! Block is not a proc!

{ puts "lol" }.to_proc.call
# BAD! Syntax error! Block is not even an object!

A method call can have zero or one block after its regular arguments. Every method can take a block, even if it doesn’t make sense because the method doesn’t use block.

puts "lol" do
  puts "this block has no sense, but it will not throw any error"
end

# the result is:
# lol
# => nil

Block is just a special syntax for a piece of code just for passing that piece of code as an argument of a method.

Blocks are made to inject code (in anonymous and literally form) into methods to make them more versatile.

def universal_but_useless_method
  yield 
end

universal_but_useless_method do
  puts "I am a block"
end

# the result is:
# I am a block
# => nil
def print_block_result_in_a_fancy_way(a, b)
  result = yield
  puts "The result of doing something with #{a} and #{b} is... #{result}!!!!!!"
end

a = 2
b = 7

print_block_result_in_a_fancy_way(a, b) do
  a + b
end

# the result is:
# The result of doing something with 2 and 7 is... 9!!!!!!
# => nil

print_block_result_in_a_fancy_way(a, b) do
  a * b
end

# the result is:
# The result of doing something with 2 and 7 is... 14!!!!!!
# => nil

There are not such things as “named blocks”.

def foo(&block)
 word = "Ho"
 block.call(word)
 block.call()
 p block
end

foo do | name |
  puts "Hey! #{name}!"
end

# it prints:
# Hey! Ho!
# Hey! !
# #<Proc:0x007f702ab4a608@main.rb:11>

block is an instance of Proc.

This method definition

def foo(&block)
  block.call("John")
end

is the same as

def foo
  block = Proc.new   
  block.call("John")
end

Proc

Procs exist in Ruby because Ruby doesn’t have functions and properties. Ruby has only methods and Ruby allows you to skip parenthesis during the method call.

But functions are useful in many languages if they are “first-class citizens”. So in Ruby procs (instances of Proc class) are objects with just a #call method with a few aliases. They can be called and passed as argument and returned like every objects.

def print_proc_result_in_a_fancy_way(a, b, operation)
  result = operation.call(a, b)
  puts "The result of doing something with #{a} and #{b} is... #{result}!!!!!!"
end

adding = Proc.new { |a, b| a + b }
multiplying = Proc.new { |a, b| a * b }

print_proc_result_in_a_fancy_way(3, 5, adding)

# the result is:
# The result of doing something with 3 and 5 is... 8!!!!!!
# => nil

print_proc_result_in_a_fancy_way(3, 5, multiplying)

# the result is:
# The result of doing something with 3 and 5 is... 15!!!!!!
# => nil

Proc doesn’t care about number of arguments. It takes argument as nil if there was nothing passed and ignores any extra arguments.

proc = Proc.new do |x|
  if x
    result = x.to_s
  else 
    result = "this was nil"
  end
  result
end
puts proc.call
puts proc.call(1)
puts proc.call(2, 3, 1)
# it prints:
# this was nil
# 1
# 2

Proc can be invoked in many ways.

adding = Proc.new { |a, b| a + b }

puts adding.call(2, 3)
# 5
# => nil
puts adding.(2, 3)
# 5
# => nil
puts adding[2, 3]
# 5
# => nil
puts adding.yield(2, 3)
# 5
# => nil

Proc can be created in many ways too.

adding = Proc.new { |a, b| a + b }

adding = proc { |a, b| a + b }

But procs can’t be created without any block!

This code:

blockless_proc = Proc.new

blockless_proc.call

will raise an exception.

Unless there is some block passed to the current scope.

That’s why this code will work;

def foo
  block = Proc.new   
  block.call
end

foo do
  puts "this block will be passed automatically"
end

Lambda

Lambda, unlike proc, raise ArgumentError if number of arguments is incorrect.

Creating lambda:

adding = lambda { |a, b| a + b }

adding = ->(a, b) { a + b }

Calling lambda (same as calling proc):

p adding.call(8, 5)
p adding.(3, 9)
p adding[2, 2]
p adding.yield(2, 3)

Return in proc and lambda

In lambdas return means the same as next.

a = [4,6,3,2,5]
l = lambda do |n|
  if n % 2 == 0
    return n * 2 # or next n * 2
  else
    return 1 # or next 1
  end
end
p = proc do |n|
  if n % 2 == 0
    next n * 2
  else
    next 1
  end
end

p a.map(&l) # => [8, 12, 1, 4, 1]
p a.map(&p) # => [8, 12, 1, 4, 1]

Return in non-lambda procs (and blocks) raises LocalJumpError:

a = [4,6,3,2,5]
p = proc do |n|
  if n % 2 == 0
    return n * 2
  else
    return 1
  end
end

p a.map(&p) # => unexpected return (LocalJumpError)

Here the first return terminates the whole method because the proc is defined in a method definition:

def convert_array(a)
  p = proc do |n|
    if n % 2 == 0
      return n * 2
    else
      return 1
    end
  end
  return a.map(&p)
end

p convert_array([8, 12, 1, 4, 1]) # => 16

Here it raises LocalJumpError too, because the proc definition is not in a method definition:

p = proc do |n|
  if n % 2 == 0
    return n * 2
  else
    return 1
  end
end

def convert_array(a, p)
  return a.map(&p)
end

p convert_array([8, 12, 1, 4, 1], p) # => unexpected return (LocalJumpError)

include vs extend

Category:

include

Adds instance methods of the module to the class as instance methods of that class.

extend

Adds instance methods of the module to the class as class methods of that class.


So it is only about instance methods of the module.

public vs protected vs private

Category:

public

Can be called by any object. Default for methods.

protected

Can be called by objects of the defining class and its subclasses.

private

Can be called only by current object. The object sends message to itself. The receiver is always self and it can’t be set explicitly even with self keyword. initialize method is always private.

Advanced parameters

Category:

Parameters made of hash

Using fetch with default values.

def stats(args)
  health = args.fetch(:health, 10)
  mana = args.fetch(:mana, 5)
  attack = args.fetch(:attack, 5)
  defense = args.fetch(:defense, 5)
  puts "Health: #{health}, Mana: #{mana}, Attack: #{attack}, Defense: #{defense}."
end

stats(attack: 30)

It prints: Health: 10, Mana: 5, Attack: 30, Defense: 5.


Using default arguments and merge method.

def stats(args)
  defaults = {
    health: 10,
    mana: 5,
    attack: 5,
    defense: 5
  }
  args = defaults.merge args
  health = args[:health]
  mana = args[:mana]
  attack = args[:attack]
  defense = args[:defense]
  puts "Health: #{health}, Mana: #{mana}, Attack: #{attack}, Defense: #{defense}."
end

stats(health: 30)

It prints: Health: 30, Mana: 5, Attack: 5, Defense: 5.


Keyword arguments (since Ruby 2.0)

def stats(health: 10, mana: 5, attack: 5, defense: 5)
  puts "Health: #{health}, Mana: #{mana}, Attack: #{attack}, Defense: #{defense}."
end

stats(mana: 20)

It prints: Health: 10, Mana: 20, Attack: 5, Defense: 5.

Org mode: Outlines

Category:
key action
M-left/right Change the headline’s level.
M-up/down Move the entire section up or down. Only in the skope of the parent section.
S-left/right Cycle through the todo states.

Installation

Category:

This installation description is for Ubuntu.


Curl is needed to install RVM. In case the system doesn’t have it:

apt-get install curl

An installation of RVM doesn’t require root privileges. RVM will install itself in a hidden .rvm directory in user’s home directory.

gpg –keyserver hkp://keys.gnupg.net –recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
\curl -sSL https://get.rvm.io | bash -s stable

Now add this line to the ~/.bashrc file

source /.rvm/scripts/rvm

Then type that command (source ~/.rvm/scripts/rvm) or restart the terminal.


Install requirements. The system will ask for the password.

rvm requirements

You have installed RVM, but you don’t have any Ruby installed inside RVM. You can check this with rvm list command.


To remove RVM from your system just delete ~/.rvm directory. Then delete the lines source ~/.rvm/scripts/rvm and export PATH="$PATH:$HOME/.rvm/bin" (created by heroku?) from ~/.bashrc.

Finding files

Category:

List every files and directories (with relative paths) in the current directory and all its subdirectories.

find .

Or just

find

Same but just files.

find -type f

Just directiories.

find -type d


Search in the home directory (and all its subdirectories).

find ~

In the /mnt and /media directories.

find /mnt /media


Find something named “config”.

find -name "config"

Find all ruby files in the home directory.

find ~ -type f -name "*.rb"

Find all ruby files in the home directory, but not if they have letter “a” or “e” in their names.

find ~ -type f -name "*.rb" -not -name "*a*" -not -name "*e*"

Or

find ~ -type f -name "*.rb" ! -name "*a*" ! -name "*e*"

Using 7z

Category:

Split a file to 500 MB parts with no compression.

7z a -v500m -mx0 movie.7z movie.mp4

Tar

Category:

Creating an archive from a directory.

tar -czvf archive.tar.gz directory/

Or just

tar czf archive.tar.gz directory


Extracting.

tar -xzvf archive.tar.gz

Or even without the file format option.

tar xf archive.tar.gz


To use bzip2 instead of gzip use j option instead of z. For bzip2 the file extension convention is “.bz2”.


List (test) the content of the archive.

tar tf archive.tar.gz