Example uses of 'Proc'

A Proc (short for procedure) is basically a method that is stored in a variable; it may have formal parameters enclosed by pipes ("|"). The Proc is called by sending the message call to the variable; any parameters given to the call method are passed along to the Proc.

Procs are closures:The context in which the Proc is written is "sealed up" and included as part of the Proc.

A trivial Proc
p = Proc.new { puts "hello" } p.call
hello
A Proc with parameters
p = Proc.new { |x, y, z| puts 100 * x + 10 * y + z } p.call 14, 9, 2
1492
Same Proc with parameters, using 'do'
p = Proc.new do
  |x, y, z| puts 100 * x + 10 * y + z
end
p.call 122, 12, 25
12345
A Proc that demonstrates how "closure" works
def scoper p
  x = 3
  p.call
end
x = 77
p = Proc.new { puts x }
x = 19
scoper p
19
A Proc with a meaningful return value
max = Proc.new do |a, b|
  if a > b then a
  else b
  end
end
puts max.call(0.8, 0.12)
0.8
A block passed as a parameter to a function becomes a Proc
def foo &b
  b.call
end
foo { puts "Hi!" }
Hi!
Procs can be used to create variations of a method
def sort array, &test
  for i in 0...array.size
    for j in 0...array.size
      if test.call(array[i], array[j])
        array[i], array[j] = array[j], array[i]
      end
    end
  end
end
a = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 6]
sort(a) { |x, y| x < y }
p a
sort(a) { |x, y| x > y }
p a
[1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 9]
[9, 6, 6, 5, 5, 4, 3, 3, 2, 1, 1]
The 'lambda' method explicitly creates a Proc from a block
def foo b  # Notice: No ampersand
  b.call
end
p = lambda { puts "Hello to you, too!" }
foo p
Hello to you, too!
A block parameter must be the last formal parameter
# Three formal parameters
def foo(x, y, &b)
  x + b.call(y)
end
# Two actual parameters and a block:
puts foo(1000, 7) { |v| v * v }
1049
A block can be optional
def cost(base_cost, &tax)
  if block_given?
    base_cost + tax.call(base_cost)
  else
    base_cost
  end
end
puts cost(4.00) { |t| 0.07 * t }
puts cost(4.00)
4.28
4.0
Another example of using a Proc as a test
def choose!(array, &test)
  i = 0
  while i < array.length
    if not test.call(array[i])
      array.delete_at i
      next
    end
    i += 1
  end
  array
end
array = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 6]
p choose!(array) { |v| v % 2 == 1 }
p choose!(array) { |v| v % 3 != 0 }
p choose!(array) { |v| v > 2 }
[3, 1, 1, 5, 9, 5, 3]
[1, 1, 5, 5]
[5, 5]
Here's a function with varargs and a block
def select *value, &better
  best = value[0]
  value.each do |v|
    best = v if better.call(v, best)
  end
  best
end
puts select(3, 1, 4, 1, 5, 9) { |x, y| x > y }
puts select(3, 1, 4, 1, 5, 9) { |x, y| x == y + 1 }
9
5
Another example of closure carrying context
def scope &b
  x = 3
  b.call
  puts "but locally, x = #{x}"
end

def context
  x = 7
  scope { puts "In the closure, x = #{x}" }
end
context
In the closure, x = 7
but locally, x = 3
Loop from Range#min to Range#max by steps of n
class Range
  def my_step(n)
    puts "---------- (#{self}).my_step #{n}"
    count = -1
    self.each do |this|
      count += 1
      yield this if count % n == 0
    end
  end
end
(7..20).my_step(3) { |n| puts n }
---------- (7..20).my_step 3
7
10
13
16
19