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:
method names begin with a lowercase letter.
Parentheses around arguments are optional.
Methods ending with "?" returns true or false; methods ending with "!" changes the internal status of an object.
Methods ending with "=" mimic the setting of values; when these functions are called, if we omit the brackets, instead of "func=(x)", we can write: "func=x", which seems an assignment. A typical usage of these functions is to give a value to an instance variable. A function giving value to an instance variable is named: "setter function", and can be written as:
def b=(a) # '@b' is an instance variable; 'b=' an instance method @b=a end self.b=333 # seems an assignment, but calls a function @b # => 333
Arguments are passed by reference to the functions and arrays and hashes passed to functions can be changed into the function.
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
There are many different ways to specify the arguments:
Arguments can have default values:
def iniziostringa(s, caratteri=1) s[0,caratteri] # a substring of a given length end iniziostringa("Abcdef") => "A" iniziostringa("Abcdef",3) => "Abc"
Arguments can be expressions, and also default values can be expressions and methods of the preceding arguments:
def iniziostringa(s, caratteri=s.size-2) s[0,caratteri] end iniziostringa("Abcdef") => "Abcd"
The number of arguments can be variable, with some arguments going into an array:
def stampa(a,*b) print(a," ",b) end stampa("a") => a [] stampa("a","b") => a ["b"] stampa("a","b","c") => a ["b", "c"]
Array arguments can be expanded to multiple values into the function, by using the "splat" operator:
def stampa(a,b,c) print("a=#{a} ","b=#{b} ","c=#{c} ") end stampa(*[1,2,3]) => a=1 b=2 c=3 # there are 3 values into the function
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 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