Function Call

The general form of a function call consists of: an optional receiver name, the function name, zero or more function arguments and an optional block of statements which can be given to the function. The block is executed inside the function by the yield statement. When the receiver name is omitted, the current instance is used as a default receiver:

receiver_name.function_name( argument, argument, argument) {statement block}

There are some conventions on method names:

Arguments are passed by reference to the functions and arrays and hashes passed to functions can be changed into the function.

Function Definition

Functions are defined with the statement "def" , followed by the function name, and arguments between round brackets; then there is the body of the function, which is a block of statements, terminated by the keyword: "end".

The name of the function begins with a lowercase letter.

Parentheses around arguments are optional both in method definition and invocations. As most of the Ruby statements, the "def" statement is an expression, and returns a value that is: "nil".

The function name can be prefixed by the name of the receiver, separated from the function name by a dot. Methods can be defined outside their instances or classes, after the instance or class creation.

Functions return the values given by the "return" statement or the last evaluated statement, if return is omitted. Multiple values can be returned and they are automatically placed into an array:

def func(a,b)
  c=1
  return a+b,c,"xx"
end

func(1,2)  => [3, 1, "xx"] # returns an array

Methods can be deleted with the "undef" statement: "undef func" deletes the method named: func.

Method names can have aliases:

"alias newname old_name"

A function can have exception handling blocks at the end, which catch the exceptions raised in the function:

def func(..)
      ...
      raise Exception_class
      ...
rescue Exception_class => local_name
   ....
rescue Exception_class => local_name
   ....
else
   ...
ensure
   ....
end

Function Arguments

There are many different ways to specify the arguments:

Blocks Given to Functions

A function can receive, in addition to the usual arguments, a block of code, between curly braces. This block is itself similar to a function and have arguments. Arguments are at the beginning of the block , between bars: "||",

Into the function the statement yield executes the block, giving arguments to the block. The block is executed in the environment of the calling program and sees the variables of the calling program.

Using the block a function can return many values during its execution and not only a final result.

Inside the function the yield statement returns the value of the block which is available to the function statements. In this way the use of a block can establish a sort of communication channel between the function and the calling environment.

In the function the statement: block_given? can test if the block has been given to the function; otherwise, if the block is missing, the statement yield raises a "LocalJumpError".

The call syntax is:

funcname(a,b,c,d) { |x,y,z| statements and function of x,y,z ..  }

The function definition syntax is:

def funcname(a,b=3,*e)
   ....
   ....
   if block_given?
         yield x1,y2,z1  # here the block is executed, with arguments x1,y1,z1
   end
   ...
   ...
end

If the yield function is inside a loop it returns a sequence of values, and the block can be executed many times, with the element of the sequence as arguments. In this way Ruby implements iterators: methods returning elements of a sequence of objects. Iterators are the preferred way to implement loops on sequences as arrays and hashes.

Examples:

def iterfun(k)
   a=[1,2,3,4]
   for ia in a
      yield ia*k if block_given?
   end
end

iterfun(1)      # returns => [1, 2, 3, 4] :
                # the last evaluated object (there is no return statement)

iterfun(1){ |k| print k } => prints 1234
iterfun(2){ |k| print k } => prints 2468

In the following example two argument are passed to the block only once:

def yfunc(a)
  b=0
  a=a+1
  yield a,b
end

yfunc(1){|k,j| print(k,j)} # => 20

In the following example the "block_given?" statement is used to obtain different results depending on the block presence:

def a_method
  return yield if block_given?       # testing if the block exists
  'block missing'
end

a_method                       # => "block missing"
a_method { "block found!" }    # => "block found!"

In the following example the value of the block, returned by the statement "yield" into the function, is used by the function computation.

def a_method(a, b)
   a + yield(a, b)
end
a_method(1, 2) {|x, y| (x + y) * 3 } # => 10

proc and lambda

Proc objects are instances of the Proc class and hold a block of code that is executable; the attribute "call" can be used to execute the proc:

myproc = Proc.new {|wine| puts "I love #{wine}!"}
myproc.call("sangiovese")

=>     I love sangiovese!

A "proc" can be passed as an argument to a function, it must be the last argument, with an ampersand :"&" before the proc name:

def func(arg,arg2,arg3=default,  &block)
  ..
  the name: block contains the proc


func(a,b,c,d,&procname)   # ampersand also in the call

A block can also be seen as a proc into a function, it can be executed, inside the function, as a proc object, with the call method, or with yield:

def stampa(&block)
  block.call                # here the block is called as it where a *proc*
  yield                     # here the block is called by Yield
end

stampa { print "--AAAA " }
=> --AAAA --AAAA            # the block is execute twice

Another way to create a Proc object is to use the lambda method; using this method is essentially equivalent to calling Proc.new.

myproc = lambda {|x| puts "Argument: #{x}"}

myproc.call("ABCDE!")

=>  Argument: ABCDE!

A difference between lambda and proc is that the lambda tests the number of arguments and raise an error if arguments are not given, proc doesn't test anything