#!/usr/bin/env ruby
=begin
This file contains the notes from the second lesson (20180226).
Topics:
* String interpolation
* Mathematical operators (and int vs float)
* Gentle introduction to the math library
* First function (with hints on scopes)
* Variables (with hints on scopes)
* Bash input - output
More in detail, it contains a brief overview of math operators
and of useful functions/constants that are in the math library.
MB.
=end
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# String interpolation
puts "== String interpolation"
# To include a variable simply wrap its name in '#{}' like as follows
x = 42
puts "The answer to the meaning of life, the universe, and everything is #{x}"
# When printing rational numbers one may want to display only a limited number
# of decimals. C like string formatting to rescue: "%.2f". "%f" means
# "here place a float variable", .2 mean with 2 decimals.
# Please note no variable name has been specified, they must be supplied
# as argument like follows.
#
# P.S one may also mix the two methods.
x = 0
puts "%.2f is #{x}" % x
# More than variable may be specified via %f like this
puts "%.2f + %.2f = %.2f" % [x, x, x]
# Alternative (crazy) float formatting with string interpolation
puts "#{'%.2f' % x} + #{'%.2f' % x} = #{'%.2f' % x}"
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Mathematical operators (and int vs float)
# Common operators behave like in real world math. Same precedence rules apply.
puts "\n\n== Math"
a = 1
b = 3
puts "For next math examples: a is #{a}, b is #{b}"
# Please note that simple operations, like the ones that follow,
# can be directly inserted into the #{}.
y = a + b
puts "Sum: a + b = #{y}"
puts "Subtraction: a - b = #{a - b}"
puts "Multiplication: a * b = #{a * b}"
puts "Power: a ** b = #{a ** b}"
puts ""
# If both dividend and divisor are integer variables -- assigned without
# decimals `a = 3`, so converted `a.to_i`, or result of non float division
# operations -- the integer division is performed, float one otherwise.
puts "Division (int): a / b = #{a % b}"
puts "Module: a % b = #{a % b}"
puts "Division (float): a /b.to_f = #{a % b}"
puts "Division (float): a /(b * 1.0) = #{a % b}"
puts "Be aware of operator precedence!!"
puts "Int division -> int, int * float -> float: a /b * 1.0 = #{a % b}"
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Math library
# `Math` is the library that offers some functions like `tan` and can be called
# as Math.function_name(arguments). Furthermore, it provides some constants
# like PI that can be accessed like `Math::PI`.
#
# Some follows some examples.
# Please refer to the documentation for the full list:
# https://ruby-doc.org/core-2.5.0/Math.html
puts "\n\n== Math library (intro)"
puts "Tangent of 5 is #{Math.tan 5}"
puts "Sine of 5 is #{Math.sin 5}"
puts "Cosine of 5 is #{Math.cos 5}"
puts "Square root of 5 is #{Math.sqrt 5}"
# The library offers also some constants like:
puts "PI is #{Math::PI}"
puts "E is #{Math::E}"
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# First function
#
# Let's compute the fix numbers for a given regular polygon
# fix_num(n) := 1/(2*tan(pi/n))
# n: number of sides
#
# Ref: https://it.wikipedia.org/wiki/Apotema_(geometria)#Numeri_fissi
puts "\n\n== Functions"
# Use keyword `def` to begin the definition of a function,
# follows the `function_name` and the list of arguments `(a, b, c)`.
def fix_num(n)
# Body of the function.
# Note that a body may contain more than one statement (e.g. assignment,
# expression, function call, ...)
# Note we have indented the code, prefixing 2 spaces.
y = 1/( 2*Math.tan(Math::PI/n) )
# Since the function is not composed by a single expressions like in math,
# but by (possibly) many statement (assignment, loops, func call, ...)
# the `return` keyword should be used to identify what is the result of
# this function.
return y
end
# Note: the function has only been defined. We say how it should be computed,
# we never actually computed it (i.e. called it). Let's do it
hexa_fix_num = fix_num(6)
puts "Fix number for a hexagon is #{hexa_fix_num}"
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Variables
puts "\n\n== Variables and intro to scope"
# For now, please consider the following kind of variables
# and pay attention on how they are written.
#
# var_name -> local variable
# scope: inside the scope where it is defined (function, block, file)
# VAR_NAME -> constant variable (re-assigning raises a warning)
# scope: all file (in all scopes)
# $var_name -> global variable
# scope: everywhere
# $VAR_NAME -> global constant
# scope: everywhere
#
# Follows some examples to show the difference between those.
# Global variable. (Shall be avoided whenever possible)
$g = 99
puts "[script] $g: #{$g}"
# Constant
WORLD="Hello World"
print "[script] "
puts WORLD
# local variable defined in script body
l = 4
puts "[script] l: #{l}"
# A function has its own scope.
def hello()
# local variables defined outside a function body do not leak inside
# the function. Uncomment to see yourself.
# puts "\t[hello] l: #{l}"
# local variable defined in function body (name already exists in body).
l = 3
puts "\t[hello] l: #{l}"
# It is possible to access a global variable from within a function.
puts "\t[hello] $g: #{$g}"
$g = 50
# and modifying it.$
puts "\t[hello] $g: #{$g}"
# local variable defined in function body (new name)
k = 5
puts "\t[hello] k: #{k}"
# Constants are visible in the whole file, including inside a function.
print "\t[hello] "
puts WORLD
end
puts "[script] call hello()"
hello()
# Let us print again to check if the assignment in the hello func changed it.
puts "[script] l after: #{l}"
# Variable is *REDEFINED* inside the scope of the function,
# the `l` variable inside hello() is another variable (different memory location)
# than the one of the script body. Thus, modification to the first does not
# impact the latter.
# Being `$g` a global variable, effects of the `hello` function are visible
# globally, thus also from the body script.
puts "[script] g: #{$g}"
# Uncomment following row to check persistence of `k`, that is defined and used
# in hello(). It will produce a `NameError`
# math.rb:156:in `': undefined local variable or method `k' \
# for main:Object (NameError)
# puts "[script] k: #{k}"
# Variable is defined inside the scope of the function, once the function call
# terminates, the scope ceases to exist, and all local variables are lost.
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Bash input - output
puts "\n\n== Input from bash, redirect output in bash"
=begin
It is always possible to provide arguments to a bash command (ls ./my_dir).
This means that also the ruby scripts can take arguments from the common line.
It is sufficient to add the arguments at the end of the command separated by
a space, like this:
username@host:path $ command arg1 arg2 arg2
The syntax should be familiar... Remember "$ ruby my_script.rb" ?
Those arguments are packed together in an array and provided by ruby via
the ARGV predefined constant.
In ruby it is possible to access to an array item via its index
(counting from 0).
puts ARGV[0] --> "arg1"
my_array[12] = 6
Try to call this script with one or more argument and it will compute fix_num
for each of them.
=end
# Ignore the following line
puts "Try call this script with some numbers as argument like:" unless ARGV.length > 0
puts "./lesson2_20180226.rb 6 12 23" unless ARGV.length > 0
# We iterate with a for loop (we will cover it in detail in the next lessons)
for arg in ARGV do
# Ruby manages all arguments from command line as strings, we must clean
# them from spaces and control charters (\n\t ..) with `chomp` and then
# cast the string into an integer via `to_i`
n = arg.chomp.to_i
puts "[ARGV exmaple] Fix number for a #{n}-sided polygon is #{fix_num(n)}"
end
=begin
To save *all* the output of this script in a file use `>` as":
$ ./lesson2_20180226.rb 6 12 23 > output.txt
Note that every time the previous line is execute the whole content of
`output.txt` is deleted, if this is not the intended behaviour an alternative
is to use `>>`. This will append the output to the existing content.
$ ./lesson2_20180226.rb 6 12 23 >> output.txt
=end