Hello World & Running
Hello, World
program hello
implicit none
print *, "Hello, World!"
end program hello puts "Hello, World!" Ruby has no
program/end program wrapper and nothing to compile: a script is a plain sequence of statements executed top to bottom. puts writes a line to standard output — the list-directed print *, of Ruby, but with no leading space.Comments
program comments
implicit none
! a full-line comment
print *, "done" ! a trailing comment
end program comments # a full-line comment
puts "done" # a trailing comment Ruby marks a comment with
# rather than Fortran’s !, and it runs to the end of the line the same way. There is no block-comment form in everyday use; Rubyists prefix each line with #.No IMPLICIT NONE
program typing
implicit none
integer :: count
count = 5
print *, count
end program typing # No 'implicit none', no declarations at all.
count = 5
puts count Ruby has no implicit-typing rule to defend against, so there is no
implicit none and no type-and-kind declaration line. A variable springs into existence on first assignment and simply refers to whatever object you gave it.Formatted output
program formatted
implicit none
real :: pi
pi = 3.14159
write(*, '(A, F6.3)') "pi = ", pi
end program formatted pi = 3.14159
printf("pi = %6.3f\n", pi)
puts format("pi = %6.3f", pi) Ruby keeps C-style format strings through
printf and format, so the F6.3 edit descriptor becomes %6.3f. The format lives inline rather than in a separate FORMAT statement or a parenthesized descriptor string.Variables & Types
Dynamic typing
program declare
implicit none
integer :: quantity
real :: price
quantity = 3
price = 9.99
print *, quantity, price
end program declare quantity = 3 # an Integer
price = 9.99 # a Float
p quantity, price
p quantity.class # Integer
p price.class # Float Ruby infers everything at run time — there is no
integer :: / real :: declaration section. The type belongs to the object, not the variable, so you can ask any value its class. A type mismatch surfaces only when an operation actually runs.Everything is an object
program intrinsic
implicit none
integer :: value
value = -5
! Intrinsics operate on the value from outside.
print *, abs(value)
print *, mod(value, 3)
end program intrinsic value = -5
# Operations are methods sent to the object.
p value.abs # 5
p value % 3 # remainder
p value.class # Integer
p 5.times.to_a # [0, 1, 2, 3, 4] In Ruby even an integer is an object, so operations are methods you send to it (
value.abs) rather than intrinsic functions applied from outside (abs(value)). There are no unboxed primitives and no distinction between an INTEGER and a "boxed" integer.Variables are untyped
program retype
implicit none
! A name is bound to one type for the whole scope.
integer :: item
item = 42
print *, item
end program retype item = 42 # holds an Integer
puts item
item = "forty-two" # now holds a String — legal
puts item A Ruby variable is just a label with no fixed type, so reassigning it to a value of a different class is ordinary and legal. In Fortran a name’s type is fixed by its declaration for the entire scope; there is no equivalent freedom.
Constants
program constants
implicit none
real, parameter :: gravity = 9.81
print *, gravity
end program constants GRAVITY = 9.81 # a constant: name begins with a capital
puts GRAVITY
# Reassigning only warns; freeze to make it truly fixed.
SPEED_LIMIT = 100
puts SPEED_LIMIT Ruby has no
parameter attribute; instead any name beginning with a capital letter is a constant. Reassigning one produces a warning rather than an error, so the convention is enforced socially — much weaker than Fortran’s compile-time parameter.Numbers
INTEGER/REAL → Integer/Float
program numeric
implicit none
integer :: whole
real :: fraction
whole = 7
fraction = 3.5
print *, whole, fraction
end program numeric whole = 7 # Integer
fraction = 3.5 # Float (a double)
p whole
p fraction
p whole.to_f # explicit conversion, like REAL(whole) Ruby’s
Integer and Float stand in for INTEGER and REAL, but a Float is always double precision — there is no single/double split and no KIND to specify. Convert explicitly with to_f/to_i rather than REAL()/INT().Arbitrary-precision integers
program overflow
implicit none
integer(kind=8) :: big
! Even a 64-bit INTEGER overflows past ~9.2e18.
big = 2_8 ** 62
print *, big
end program overflow big = 2 ** 100 # no overflow, ever
puts big
puts (10 ** 50) + 1 # exact, arbitrary precision A Ruby
Integer grows to whatever size the value needs, transparently — there is no fixed width, no KIND=8, and no overflow. This trades Fortran’s predictable machine-word performance for never having to reason about integer range.Integer division
program division
implicit none
print *, 7 / 2 ! 3 — integer division
print *, real(7) / 2 ! 3.5 — one REAL promotes
end program division p 7 / 2 # 3 — Integer / Integer truncates
p 7.0 / 2 # 3.5 — one Float promotes the result
p 7.fdiv(2) # 3.5 — explicit float division
p 7 % 2 # 1 — remainder, like mod(7, 2) The rule matches Fortran: integer divided by integer truncates, and a single
Float operand promotes the whole expression to Float. Ruby decides by the runtime classes of the operands, the way Fortran decides by declared types.Complex numbers
program complex_demo
implicit none
complex :: z
z = (1.0, 2.0)
print *, z
print *, abs(z) ! magnitude
end program complex_demo z = Complex(1, 2) # or 1 + 2i
p z
p z.abs # magnitude
p z.real # 1
p z.imaginary # 2 Ruby has a built-in
Complex class, so complex arithmetic works out of the box as it does in Fortran — but the imaginary literal is 2i and the parts are read with the methods real and imaginary rather than the intrinsics REAL()/AIMAG().Math functions
program math_demo
implicit none
print *, sqrt(2.0)
print *, sin(3.14159 / 2)
print *, max(3, 8)
end program math_demo p Math.sqrt(2.0)
p Math.sin(Math::PI / 2)
p [3, 8].max # or 3 and 8 compared
p 2 ** 10 # ** is exponentiation, like ** Transcendental functions live in the
Math module (Math.sqrt, Math::PI) rather than being global intrinsics. Note max is a method on an array of values or on the numbers themselves, not a free function taking a list.Strings
Dynamic-length strings
program strings
implicit none
character(len=20) :: name
name = "Ada"
! Fixed width: 'name' is padded to 20 chars.
print *, "[", trim(name), "]"
end program strings name = "Ada" # length is exactly 3, grows as needed
puts "[#{name}]"
p name.length # 3 — no padding, no fixed width
longer = name + " Lovelace"
puts longer A Ruby
String has no declared length — it is a dynamic, resizable object, so there is no fixed character(len=20) width and no trailing blanks to trim. Its length is simply however many characters it currently holds.Interpolation vs //
program concat
implicit none
character(len=:), allocatable :: greeting
character(len=20) :: name
name = "Ada"
greeting = "Hello, " // trim(name)
print *, greeting
end program concat name = "Ada"
greeting = "Hello, #{name}" # interpolation
puts greeting
also = "Hello, " + name # + concatenates, like //
puts also Ruby’s
#{expression} interpolates any value directly into a string literal, evaluating its to_s — there is no concatenation operator needed for the common case. When you do want to join strings, + plays the role of Fortran’s //.String operations
program strops
implicit none
character(len=20) :: text
text = " hello "
print *, len_trim(text)
print *, adjustl(text)
! No built-in uppercase intrinsic in standard Fortran.
end program strops text = " hello "
p text.strip # "hello" — like trim + adjustl
p text.strip.upcase # "HELLO"
p text.strip.length # 5
p "hello".reverse # "olleh" String manipulation is a rich set of methods on the object:
strip removes surrounding whitespace (both adjustl and trim in one), and upcase/reverse do what standard Fortran has no intrinsic for. They chain because each returns a new string.Substrings
program substr
implicit none
character(len=11) :: phrase
phrase = "hello world"
! 1-based, inclusive range.
print *, phrase(1:5)
print *, phrase(7:11)
end program substr phrase = "hello world"
p phrase[0..4] # "hello" — 0-based, inclusive range
p phrase[6..] # "world" — open-ended range
p phrase[0, 5] # "hello" — start + length
p phrase[-5..] # "world" — negative index from the end The big adjustment: Ruby strings are 0-based, so Fortran’s
phrase(1:5) becomes phrase[0..4]. Ruby also allows negative indices counting from the end and open-ended ranges — neither of which Fortran substring notation offers.Arrays
1-based → 0-based indexing
program indexing
implicit none
integer :: numbers(3)
numbers = [10, 20, 30]
! Fortran arrays start at index 1 by default.
print *, numbers(1) ! 10 — the first element
print *, numbers(3) ! 30 — the last element
end program indexing numbers = [10, 20, 30]
# Ruby arrays start at index 0.
p numbers[0] # 10 — the first element
p numbers[2] # 30 — the last element
p numbers[-1] # 30 — negative index from the end
p numbers.first # 10 — or just ask by name This is the single most important adjustment: Ruby arrays are 0-based, so the first element is
numbers[0], not numbers(1). There is no way to declare a custom lower bound as Fortran’s numbers(0:2) allows; 0 is always the first index.Array construction
program construct
implicit none
integer :: primes(4)
primes = [2, 3, 5, 7]
print *, primes
print *, size(primes)
end program construct primes = [2, 3, 5, 7]
p primes
p primes.size # 4 — like size(primes)
# Arrays are heterogeneous and can hold any object:
mixed = [1, "two", 3.0, :four]
p mixed The
[ ] literal looks like a Fortran array constructor, but a Ruby Array is a dynamic, heterogeneous object — it can hold values of different classes at once, which a Fortran array (uniform type and kind) cannot.Growing arrays
program allocate_demo
implicit none
integer, allocatable :: stack(:)
allocate(stack(0))
! Growing means reallocating the whole array.
stack = [stack, 1]
stack = [stack, 2]
print *, stack
end program allocate_demo stack = [] # no allocate, no fixed size
stack.push(1) # grows in place
stack << 2 # << is the same as push
p stack # [1, 2]
top = stack.pop # removes and returns 2
p top A Ruby array grows and shrinks on demand with
push/<</pop — no allocatable attribute, no allocate, and no reallocation dance. It is a stack, queue, and vector all in one object.Array slicing
program slicing
implicit none
integer :: values(5)
values = [10, 20, 30, 40, 50]
print *, values(2:4) ! elements 2,3,4
print *, values(1:5:2) ! stride of 2
end program slicing values = [10, 20, 30, 40, 50]
p values[1..3] # [20, 30, 40] — 0-based range
p values.values_at(0, 2, 4) # [10, 30, 50] — a stride by hand
p values.each_slice(2).to_a # [[10,20],[30,40],[50]] Ruby slices with a
Range (values[1..3]), 0-based and inclusive. There is no built-in strided slice like Fortran’s values(1:5:2); you express a stride with values_at, step, or each_slice.Array Operations
Elementwise operations
program elementwise
implicit none
integer :: a(3), b(3), c(3)
a = [1, 2, 3]
b = [10, 20, 30]
! Whole-array arithmetic is built in.
c = a + b
print *, c
end program elementwise a = [1, 2, 3]
b = [10, 20, 30]
# No whole-array arithmetic; pair up with zip, then map.
c = a.zip(b).map { |x, y| x + y }
p c # [11, 22, 33]
doubled = a.map { |n| n * 2 }
p doubled # [2, 4, 6] Ruby has no whole-array arithmetic —
a + b concatenates two arrays rather than adding elementwise. You express Fortran’s c = a + b by zip-ing the arrays into pairs and map-ing over them. (For heavy numerics, the numo-narray gem restores Fortran-style vectorized operations.)Reductions
program reductions
implicit none
integer :: data(5)
data = [3, 1, 4, 1, 5]
print *, sum(data)
print *, product(data)
print *, maxval(data)
print *, minval(data)
end program reductions data = [3, 1, 4, 1, 5]
p data.sum # like sum(data)
p data.reduce(:*) # like product(data)
p data.max # like maxval(data)
p data.min # like minval(data) The Fortran reduction intrinsics map to
Enumerable methods: sum, max, and min read the same, and product becomes reduce(:*), which folds the multiplication operator over the array.WHERE → select/map
program masking
implicit none
integer :: values(5), result(5)
values = [-2, 3, -4, 5, -6]
result = values
! Masked assignment: only where the condition holds.
where (values < 0) result = 0
print *, result
end program masking values = [-2, 3, -4, 5, -6]
# Transform conditionally with map:
result = values.map { |n| n < 0 ? 0 : n }
p result # [0, 3, 0, 5, 0]
# Or keep only the matching elements:
positives = values.select { |n| n > 0 }
p positives # [3, 5] Fortran’s
where masked assignment becomes a map with a conditional expression, and Fortran’s pack/masking becomes select (keep matching) or reject (drop matching). Each returns a new array rather than assigning into an existing one.Matrix multiply
program matrix
implicit none
integer :: a(2,2), b(2,2), c(2,2)
a = reshape([1, 2, 3, 4], [2, 2])
b = reshape([5, 6, 7, 8], [2, 2])
c = matmul(a, b)
print *, c
end program matrix require "matrix"
a = Matrix[[1, 2], [3, 4]]
b = Matrix[[5, 6], [7, 8]]
c = a * b # matrix multiply, like matmul
p c.to_a Ruby has no built-in multidimensional array; a nested array is just an array of arrays with no elementwise or
matmul semantics. The standard-library Matrix class supplies real linear algebra, where * is matrix multiplication — but there is nothing as fundamental as Fortran’s native rank-2 arrays and reshape.Control Flow
IF / ELSE
program branch
implicit none
integer :: score
score = 72
if (score >= 90) then
print *, "A"
else if (score >= 60) then
print *, "pass"
else
print *, "fail"
end if
end program branch score = 72
if score >= 90
puts "A"
elsif score >= 60
puts "pass"
else
puts "fail"
end The structure matches, with cosmetic differences: no
then keyword, no parentheses required around the condition, elsif (one word) for else if, and a single end instead of end if. An if in Ruby is also an expression that returns a value.SELECT CASE
program choose
implicit none
integer :: code
code = 2
select case (code)
case (1)
print *, "one"
case (2, 3)
print *, "two or three"
case default
print *, "other"
end select
end program choose code = 2
case code
when 1
puts "one"
when 2, 3
puts "two or three"
else
puts "other"
end Ruby’s
case/when mirrors select case, including multiple values per branch (when 2, 3). It is more powerful, though: a when can match a range (when 1..10), a class, or a regular expression, because it tests with the === operator.Logical operators
program logic
implicit none
logical :: ready, willing
ready = .true.
willing = .false.
if (ready .and. .not. willing) then
print *, "ready but not willing"
end if
end program logic ready = true
willing = false
if ready && !willing
puts "ready but not willing"
end
p (ready || willing) # true Ruby uses the C-style
&&, ||, and ! in place of .and., .or., and .not., and the literals are the bare words true/false without the surrounding dots. Only false and nil are falsy; every other object, including 0, is truthy.Loops & Iteration
DO loop → each/times
program counting
implicit none
integer :: index
do index = 1, 5
print *, index
end do
end program counting # Idiomatic Ruby iterates a range, not an index counter:
(1..5).each do |index|
puts index
end
# Or, when you just need N repetitions:
5.times { |index| puts index } # index runs 0..4 The counting
do loop becomes iteration over a Range with each, or 5.times when the counter itself does not matter. Ruby rarely manages a bare loop index — you ask a collection to hand you each element instead.Stepped & reverse loops
program stepping
implicit none
integer :: index
do index = 10, 1, -2
print *, index
end do
end program stepping 10.step(1, -2) do |index|
puts index
end
# ascending stride:
(0..10).step(2) { |index| puts index } Fortran’s three-part
do index = start, stop, step becomes start.step(stop, step) or range.step(size). Both directions work; a negative step counts down, exactly as in Fortran.DO WHILE
program repeat
implicit none
integer :: total
total = 0
do while (total < 10)
total = total + 3
end do
print *, total
end program repeat total = 0
while total < 10
total += 3 # += works; there is no ++ operator
end
puts total # 12 Ruby’s
while drops the do keyword and the parentheses. Note there is no ++; use total += 3. Ruby also offers until (loop while the condition is false), the inverse form Fortran lacks.Iterating an array
program iterate
implicit none
integer :: values(3), index
values = [10, 20, 30]
do index = 1, size(values)
print *, index, values(index)
end do
end program iterate values = [10, 20, 30]
# Iterate elements directly — no index needed:
values.each { |value| puts value }
# Need the index too? each_with_index gives both:
values.each_with_index do |value, index|
puts "#{index}: #{value}"
end Rather than loop an index from
1 to size and subscript the array, Ruby hands you each element directly with each. When you genuinely need the position, each_with_index yields both — and the index is 0-based.Procedures & Functions
SUBROUTINE → method
program run
implicit none
call greet("Ada")
contains
subroutine greet(name)
character(len=*), intent(in) :: name
print *, "Hello, ", trim(name)
end subroutine greet
end program run def greet(name)
puts "Hello, #{name}"
end
greet("Ada") # no 'call' keyword A
subroutine — a procedure with no return value — becomes an ordinary def, invoked without a call keyword. There is no contains section or separate declaration of the dummy argument’s type; the parameter is just a name.FUNCTION → return value
program compute
implicit none
print *, square(5)
contains
integer function square(x)
integer, intent(in) :: x
square = x * x
end function square
end program compute def square(x)
x * x # last expression is the return value
end
p square(5) # 25 A Ruby method returns its last evaluated expression implicitly — there is no result variable to assign (as Fortran assigns to the function name) and an explicit
return is rarely written. The return type is not declared; it is whatever object the body produces.INTENT & argument passing
program modify
implicit none
integer :: value
value = 10
call double_it(value) ! passed by reference
print *, value ! 20 — the caller's copy changed
contains
subroutine double_it(x)
integer, intent(inout) :: x
x = x * 2
end subroutine double_it
end program modify def double_it(number)
number * 2 # returns a new value; caller must capture it
end
value = 10
value = double_it(value) # reassign to "modify"
p value # 20 Ruby has no
intent(inout) and no pass-by-reference for a rebind: an integer argument cannot be changed in the caller’s scope. You return the new value and reassign. (Mutable objects like arrays can be changed in place, since the reference is shared — but the variable binding itself never is.)OPTIONAL → default arguments
program greeting
implicit none
call welcome()
call welcome("Ada")
contains
subroutine welcome(name)
character(len=*), intent(in), optional :: name
if (present(name)) then
print *, "Hello, ", trim(name)
else
print *, "Hello, guest"
end if
end subroutine welcome
end program greeting def welcome(name = "guest")
puts "Hello, #{name}"
end
welcome # "Hello, guest"
welcome("Ada") # "Hello, Ada" Ruby gives a parameter a default value directly in the signature, so there is no
optional attribute and no present() check — an omitted argument simply takes its default. This is terser than Fortran’s optional-plus-present idiom.Recursion
program fact
implicit none
print *, factorial(5)
contains
recursive integer function factorial(n) result(value)
integer, intent(in) :: n
if (n <= 1) then
value = 1
else
value = n * factorial(n - 1)
end if
end function factorial
end program fact def factorial(n)
n <= 1 ? 1 : n * factorial(n - 1)
end
p factorial(5) # 120 Ruby methods are recursive by default — there is no
recursive keyword to opt in, and no result variable. The ternary condition ? a : b keeps the whole body to one expression here.Derived Types → Classes
TYPE → class
program point_demo
implicit none
type :: point
real :: x, y
end type point
type(point) :: origin
origin%x = 3.0
origin%y = 4.0
print *, origin%x, origin%y
end program point_demo Point = Struct.new(:x, :y) # or: Data.define(:x, :y)
origin = Point.new(3.0, 4.0)
p origin.x # 3.0 — a method, not %-field access
p origin.y
origin.x = 5.0 # Struct fields are mutable
p origin.x A Fortran
type becomes a Ruby class. Struct.new generates one with accessor methods in a single line — fields are read as origin.x (a method call), not origin%x. Use Data.define instead when you want an immutable value object.Type-bound procedures
module geometry
implicit none
type :: circle
real :: radius
contains
procedure :: area
end type circle
contains
real function area(self)
class(circle), intent(in) :: self
area = 3.14159 * self%radius ** 2
end function area
end module geometry
program main
use geometry
implicit none
type(circle) :: c
c%radius = 2.0
print *, c%area()
end program main class Circle
def initialize(radius)
@radius = radius # an instance variable
end
def area
3.14159 * @radius ** 2
end
end
circle = Circle.new(2.0)
p circle.area Where Fortran attaches a type-bound
procedure and passes self explicitly, a Ruby class defines methods that reach instance state through @radius — no explicit self parameter and no separate contains block. The constructor is the special method initialize.Type extension → inheritance
module animals
implicit none
type :: animal
character(len=20) :: name
end type animal
type, extends(animal) :: dog
end type dog
end module animals
program main
use animals
implicit none
type(dog) :: rex
rex%name = "Rex"
print *, trim(rex%name)
end program main class Animal
attr_reader :name
def initialize(name)
@name = name
end
def describe = "a nameless animal named #{name}"
end
class Dog < Animal # < means "inherits from"
def describe = "a dog named #{name}"
end
puts Dog.new("Rex").describe Ruby inheritance uses
< where Fortran uses extends. Beyond inheriting fields, a Ruby subclass can override methods and call super — full polymorphism, versus Fortran’s type extension which mainly adds components and type-bound procedures.Modules & Namespacing
MODULE → module/class
module constants
implicit none
real, parameter :: pi = 3.14159
contains
real function circle_area(radius)
real, intent(in) :: radius
circle_area = pi * radius ** 2
end function circle_area
end module constants
program main
use constants
implicit none
print *, circle_area(2.0)
end program main module Geometry
PI = 3.14159
def self.circle_area(radius)
PI * radius ** 2
end
end
puts Geometry.circle_area(2.0) # namespaced call A Ruby
module groups constants and methods under a namespace, like a Fortran module. A def self.name method is called on the module itself (Geometry.circle_area) — the counterpart of a module procedure made available through use.USE → require & include
module utilities
implicit none
contains
function doubled(x) result(y)
integer, intent(in) :: x
integer :: y
y = x * 2
end function doubled
end module utilities
program main
use utilities ! bring names into scope
implicit none
print *, doubled(21)
end program main module Doubler
def doubled(x) = x * 2 # instance method for includers
end
class Calculator
include Doubler # mixes the module's methods in
end
p Calculator.new.doubled(21) # 42 Fortran’s
use brings a module’s public names into scope. Ruby splits this: require loads a file, while include mixes a module’s methods into a class — a form of code reuse (a mixin) that Fortran has no direct equivalent for, sitting between a module and inheritance.Hashes
A key–value store
program lookup
implicit none
! Fortran has no dictionary type. The usual workaround
! is parallel arrays searched by hand.
character(len=5) :: names(2)
integer :: ages(2)
names = ["Ada ", "Alan "]
ages = [36, 41]
print *, trim(names(1)), ages(1)
end program lookup ages = { "Ada" => 36, "Alan" => 41 }
p ages["Ada"] # 36 — O(1) lookup by key
p ages.size # 2
ages["Grace"] = 45 # insert
p ages.keys # ["Ada", "Alan", "Grace"] This is a capability Fortran simply lacks: the
Hash is a built-in, growable map from keys to values with O(1) lookup. Where Fortran forces parallel arrays and a linear search, Ruby indexes by any object as a key — no fixed size, no manual scanning.Iterating & counting
program tally
implicit none
! Counting occurrences in Fortran means a manual loop
! over parallel key/count arrays.
integer :: counts(3)
character :: letters(3)
letters = ["a", "b", "c"]
counts = [0, 0, 0]
counts(1) = counts(1) + 1
print *, letters(1), counts(1)
end program tally # A default of 0 makes frequency counting a one-liner:
tally = Hash.new(0)
"banana".each_char { |letter| tally[letter] += 1 }
p tally # {"b"=>1, "a"=>3, "n"=>2}
tally.each do |letter, count|
puts "#{letter}: #{count}"
end A
Hash.new(0) returns 0 for any absent key, turning frequency counting into a single line — no pre-sized arrays, no search. Iterating yields each key–value pair to the block, which destructures it into two parameters.Blocks & Closures
Blocks: map & select
program transform
implicit none
integer :: numbers(4), squares(4)
integer :: index
numbers = [1, 2, 3, 4]
! Transform with an explicit loop.
do index = 1, size(numbers)
squares(index) = numbers(index) ** 2
end do
print *, squares
end program transform numbers = [1, 2, 3, 4]
# A block passed to map transforms each element:
squares = numbers.map { |n| n ** 2 }
p squares # [1, 4, 9, 16]
evens = numbers.select { |n| n.even? }
p evens # [2, 4] A block — an anonymous chunk of code in
{ } — is Ruby’s central idiom and has no Fortran equivalent. map applies it to each element and collects the results, replacing the explicit index loop and temporary result array.Folding with reduce
program accumulate
implicit none
integer :: data(5), total, index
data = [1, 2, 3, 4, 5]
total = 0
do index = 1, size(data)
total = total + data(index)
end do
print *, total
end program accumulate data = [1, 2, 3, 4, 5]
# reduce folds the array down to a single value:
total = data.reduce(0) { |sum, n| sum + n }
p total # 15
# For a plain sum, the built-in is enough:
p data.sum # 15 A running accumulation over a loop is captured by
reduce (also called inject): it threads an accumulator through the block for each element and returns the final value — the general form behind sum, product, and the rest.Procedures as values
program proc_ptr
implicit none
procedure(op), pointer :: operation
operation => add
print *, operation(3, 4)
contains
integer function add(a, b)
integer, intent(in) :: a, b
add = a + b
end function add
integer function op(a, b)
integer, intent(in) :: a, b
op = 0
end function op
end program proc_ptr add = ->(a, b) { a + b } # a lambda, stored in a variable
p add.call(3, 4) # 7
p add.(3, 4) # 7 — shorthand
# Pass one straight into an iterator:
p [1, 2, 3].map { |n| add.call(n, 10) } # [11, 12, 13] A Ruby lambda (
->(a, b) { … }) is a first-class function value you store and pass around — much simpler than a Fortran procedure pointer with its matching abstract interface. You invoke it with call (or the .() shorthand).Error Handling
STOP → raise & exceptions
program guard
implicit none
integer :: denominator
denominator = 0
if (denominator == 0) then
! stop halts the whole program — there is no recovery.
stop "cannot divide by zero"
end if
print *, 10 / denominator
end program guard def safe_divide(a, b)
raise ZeroDivisionError, "cannot divide by zero" if b.zero?
a / b
end
begin
puts safe_divide(10, 0)
rescue ZeroDivisionError => error
puts "caught: #{error.message}"
end Ruby has true exceptions:
raise throws one and begin/rescue catches it, unwinding the stack until a handler is found. Fortran’s only comparable tool is stop/error stop, which halts the program outright — there is no catching or recovery.Status flags → begin/rescue
File I/O is unavailable in the in-browser runtime, so the Fortran example is shown for reference only.
program readfile
implicit none
integer :: status
! Fortran surfaces errors through an integer status code.
open(unit=10, file="missing.txt", status="old", iostat=status)
if (status /= 0) then
print *, "open failed, code", status
else
close(10)
end if
end program readfile def parse_number(text)
Integer(text) # raises on bad input
rescue ArgumentError => error
puts "parse failed: #{error.message}"
nil # a sentinel, if you want one
end
p parse_number("42") # 42
p parse_number("oops") # nil Where Fortran threads an
iostat integer through every fallible call and checks it by hand, Ruby lets the failure raise and handles it in one rescue. A method body is an implicit begin, so the rescue can sit at method level with no explicit block.Guaranteed cleanup
program cleanup
implicit none
! Fortran has no finally; you close resources on every
! path by hand, including before each error stop.
print *, "acquire"
print *, "use"
print *, "release"
end program cleanup def with_resource
puts "acquire"
yield
ensure
puts "release" # runs on normal exit AND on exception
end
with_resource { puts "use" } The
ensure clause runs whether the body returns normally or raises, guaranteeing cleanup in one place. Fortran has no such construct — you repeat the close/deallocate on every exit path, which is exactly the bookkeeping ensure removes.I/O & Files
print/write → puts/p
program output
implicit none
integer :: values(3)
values = [1, 2, 3]
print *, "list:", values
write(*, *) "again:", values
end program output values = [1, 2, 3]
puts "list: #{values.join(', ')}" # human-facing
p values # developer-facing: [1, 2, 3]
print "no newline" # print omits the newline
puts Ruby splits output by audience:
puts writes a value’s to_s plus a newline, p writes the inspect form (with brackets and quotes) for debugging, and print writes to_s with no newline. There is no * list-directed format to remember.Reading input
Standard input is unavailable in the in-browser runtime, so this example is shown for reference only.
program input
implicit none
character(len=100) :: name
print *, "What is your name?"
read(*, '(A)') name
print *, "Hello, ", trim(name)
end program input puts "What is your name?"
name = gets.chomp # read a line, strip the newline
puts "Hello, #{name}" gets reads one line from standard input as a string; chomp removes the trailing newline. It returns nil at end of input rather than raising, and there is no format descriptor or fixed-width buffer to declare.Reading a file
File I/O is unavailable in the in-browser runtime, so this example is shown for reference only.
program readlines
implicit none
integer :: unit, status
character(len=100) :: line
open(newunit=unit, file="notes.txt", status="old")
do
read(unit, '(A)', iostat=status) line
if (status /= 0) exit
print *, trim(line)
end do
close(unit)
end program readlines contents = File.read("notes.txt")
contents.each_line do |line|
puts line.chomp
end
# whole file as an array of lines:
lines = File.readlines("notes.txt", chomp: true) Ruby opens, reads, and closes a file in a single
File.read call — no unit number, no open/close pairing, and no read-loop terminated by an iostat code. each_line streams lines, and readlines returns them all as an array.