Functional Fortran
source link: https://www.tuicool.com/articles/MvABJfr
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
functional-fortran
Functional programming for modern Fortran.
Getting started
git clone https://github.com/wavebitscientific/functional-fortran cd functional-fortran mkdir build cd build cmake .. make ctest
Start using functional-fortran in your code by including the module:
use mod_functional
Why functional-fortran?
While not designed as a purely functional programming language, modern Fortran goes a long way by letting the programmer use pure
functions to encourage good functional discipline, express code in mathematical form, and minimize bug-prone mutable state. This library provides a set of commonly used tools in functional programming, with the purpose to help Fortran programmers be less imperative and more functional.
What’s included?
The following functions are provided:
arange complement empty filter foldl foldr foldt head init insert intersection iterfold last limit map set reverse sort split subscript tail unfold union
All of the above functions are compatible with the standard Fortran 2008 kinds: int8
, int16
, int32
, int64
, real32
, real64
, real128
, complex(real32)
, complex(real64)
, and complex(real128)
.
Further, these functions (and their corresponding operators) are compatible with character strings: complement
, empty
, head
, init
, intersection
, insert
, last
, reverse
, set
, sort
, split
, tail
, and union
.
Functions that operate on one or two arguments are also available as unary or binary operators, respectively. These are: .complement.
, .head.
, .init.
, .intersection.
, .last.
, .reverse.
, .set.
, .sort.
, .tail.
, and .union.
.
Example usage
Array functions
arange
is used to generate evenly spaced arrays, given start and end values as input arguments:
write(*,*)arange(1,5) 1 2 3 4 5
arange
works with real numbers as well:
write(*,*)arange(1.,5.) 1.00000000 2.00000000 3.00000000 4.00000000 5.00000000
Third argument to arange
(optional) is the increment, which defaults to 1
if not given:
write(*,*)arange(1,15,3) 1 4 7 10 13
Negative increments work as expected:
write(*,*)arange(3,1,-1) 3 2 1
We can use floating-point increments:
write(*,*)arange(1.,1.5,0.1) 1.00000000 1.10000002 1.20000005 1.29999995 1.39999998 1.50000000
If start
is greater than end
and increment is positive, arange
returns an empty array:
write(*,*)arange(5,1)
Use empty
to generate a zero-length array of any Fortran standard kind:
write(*,*)size(empty(1)) 0
which may be useful to initialize accumulators, for example see the implementation of set intersection
in this library.
head
returns the first element of the array:
write(*,*)head([1,2,3]) 1
tail
returns everything but the first element of the array:
write(*,*)tail([1,2,3]) 2 3
Similarly, last
returns the last element of the array:
write(*,*)last([1,2,3]) 3
init
returns everything but the last element of the array:
write(*,*)init([1,2,3]) 1 2
Subscript an array at specific indices:
write(*,*)subscript([1,2,3,4,5],[3,4]) 3 4
Unlike Fortran 2008 vector subscript, the subscript
function is out-of-bounds safe, i.e. subscripting out of bounds returns an empty array:
write(*,*)subscript([1,2,3],[10])
We can prepend, append, or insert an element into an array using insert
:
! insert a 5 at position 0 to prepend: write(*,*)insert(5,0,[1,2,3]) 5 1 2 3 ! insert a 5 at position 4 to append: write(*,*)insert(5,4,[1,2,3]) 1 2 3 5 ! insert a 2 at position 2: write(*,*)insert(2,2,[1,3,4]) 1 2 3 4
split
can be used to return first or second half of an array:
! return first half of the array write(*,*)split(arange(1,5),1) 1 2 ! return second half of the array write(*,*)split(arange(1,5),2) 3 4 5
The above is useful for recursive binary tree searching or sorting, for example, see the implementation of sort
in this library.
sort
returns a sorted array in ascending order:
real,dimension(5) :: x call random_number(x) write(*,*)x 0.997559547 0.566824675 0.965915322 0.747927666 0.367390871 write(*,*)sort(x) 0.367390871 0.566824675 0.747927666 0.965915322 0.997559547
Use reverse
to sort in descending order:
write(*,*)reverse(sort(x)) 0.997559547 0.965915322 0.747927666 0.566824675 0.367390871
The limit
function can be used to contrain a value of a scalar or an array within a lower and upper limit, for example:
! limit a scalar (5) within bounds 1 and 4 write(*,*)limit(5,1,4) 4 ! flipping the bounds works just as well write(*,*)limit(5,4,1) 4
limit
also works on arrays:
write(*,*)limit(arange(0,4),1,3): 1 1 2 3 3
More functional: map
, filter
, fold
, unfold
map
has the same functionality as pure elemental functions, but can be used to apply recursive functions to arrays, for example:
pure recursive integer function fibonacci(n) result(fib) integer,intent(in) :: n if(n == 0)then fib = 0 elseif(n == 1)then fib = 1 else fib = fibonacci(n-1)+fibonacci(n-2) endif endfunction fibonacci write(*,*)map(fibonacci,[17,5,13,22]) 1597 5 233 17711
filter
returns array elements that satisfy a logical filtering function. For example, we can define a function that returns .true. when input is an even number, and use this function to filter an array:
pure logical function even(x) integer,intent(in) :: x even = .false. if(mod(x,2) == 0)even = .true. endfunction even write(*,*)filter(even,[1,2,3,4,5]) 2 4
Functions can be chained together into pretty one-liners:
write(*,*)filter(even,map(fibonacci,arange(1,10))) 2 8 34
functional-fortran also provides left-, right-, and tree-fold functions, foldl
, foldr
, and foldt
, respectively. These functions recursively consume an array using a user-defined function, and return a resulting scalar. For simple examples of sum
and product
functions using folds, we can define the following addition and multiplication functions that operate on scalars:
pure real function add(x,y) real,intent(in) :: x,y add = x+y endfunction add pure real function mult(x,y) real,intent(in) :: x,y mult = x*y endfunction mult
We can then calculate the sum
and product
of an array by “folding” the input using the above-defined functions and a start value (second argument to fold*
):
! left-fold an array using add to compute array sum write(*,*)foldl(add,0.,arange(1.,5.)) 15.0000000 ! left-fold an array using mult to compute array product write(*,*)foldl(mult,1.,arange(1.,5.)) 120.000000
The above is a trivial example that re-invents Fortran intrinsics as a proof of concept. Intrinsic functions should of course be used whenever possible.
foldl
, foldr
, and foldt
return the same result if the user-defined function is associative. See the Wikipedia page on fold for more information. iterfold
is an iterative (non-recursive) implementation of foldl
that is provided for reference.
Opposite to fold*
, unfold
can be used to generate an array based on a start value x
, and a function f
, such that the resulting array equals [x, f(x), f(f(x)), f(f(f(x))), ... ]
. For example:
pure real function multpt1(x) real,intent(in) :: x multpt1 = 1.1*x endfunction multpt1 write(*,*)unfold(multpt1,[1.],5) 1.00000000 1.10000002 1.21000004 1.33100009 1.46410012
Set functions: set
, union
, intersection
, complement
Function set
returns all unique elements of an input array:
write(*,*)set([1,1,2,2,3]) 1 2 3
Common functions that operate on sets, union
, intersection
, and complement
, are also available:
! unique elements that are found in either array write(*,*)union([1,2,2],[2,3,3,4]) 1 2 3 4 ! unique elements that are found in both arrays write(*,*)intersection([1,2,2],[2,3,3,4]) 2 ! unique elements that are found first but not in second array write(*,*)complement([1,2,2],[2,3,3,4]) 1
Contributing
Please submit a bug report or a request for new feature here .
Further reading
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK