Quantcast
Channel: Hacker News
Viewing all articles
Browse latest Browse all 25817

Show HN: Functional programming for modern Fortran

$
0
0

README.md

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 - returns a regularly spaced array
  • complement - returns a set complement of two arrays
  • filter - filters an array using a logical input function
  • foldl - recursively left-folds an array using an input function
  • foldr - recursively right-folds an array using an input function
  • foldt - recursively tree-folds an array using an input function
  • head - returns the first element of an array
  • init - returns everything but the last element
  • insert - inserts an element into an array, out-of-bound safe
  • intersection - returns a set intersection of two arrays
  • iterfold - iteratively reduces an array using an input function
  • last - returns the last element of an array
  • limit - limits a scalar or array by given lower and upper bounds
  • map - maps an array with an input function
  • set - returns a set given input array
  • reverse - returns array in reverse order
  • sort - recursive quicksort using binary tree pivot
  • split - returns first or second half of an array
  • subscript - out-of-bound safe implementation of vector subscript
  • tail - returns everything but the first element
  • unfold - unfolds an array with an input function
  • union - returns a set union of two arrays

All of the above functions are compatible with the standard Fortran 2008 kinds:int8, int16, int32, int64, real32, real64, real128.

System requirements

functional-fortran builds and passes all tests with:

  • GNU Fortran compiler v4.8.3 and higher
  • Intel Fortran compiler v15.0.0 and higher

Example usage

Array functions

arange is used to generate evenly spaced arrays, given start and end values as input arguments:

write(*,*)arange(1,5)12345

arange works with real numbers as well:

write(*,*)arange(1.,5.)1.000000002.000000003.000000004.000000005.00000000

Third argument to arange (optional) is the increment, which defaults to 1 if not given:

write(*,*)arange(1,15,3)1471013

Negative increments work as expected:

write(*,*)arange(3,1,-1)321

We can use floating-point increments:

write(*,*)arange(1.,1.5,0.1)1.000000001.100000021.200000051.299999951.399999981.50000000

Be mindful of floating-point arithmetic:

write(*,*)arange(1.,1.4,0.1)1.000000001.100000021.200000051.29999995

If start is greater than end and increment is positive,arange returns an empty array:

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])23

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])12

Subscript an array at specific indices:

write(*,*)subscript([1,2,3,4,5],[3,4])34

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])5123! insert a 5 at position 4 to append:write(*,*)insert(5,4,[1,2,3])1235! insert a 2 at position 2:write(*,*)insert(2,2,[1,3,4])1234

split can be used to return first or second half of an array:

! return first half of the arraywrite(*,*)split(arange(1,5),1)12! return second half of the arraywrite(*,*)split(arange(1,5),2)345

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) :: xcallrandom_number(x)write(*,*)x0.9975595470.5668246750.9659153220.7479276660.367390871write(*,*)sort(x)0.3673908710.5668246750.7479276660.9659153220.997559547

Use reverse to sort in descending order:

write(*,*)reverse(sort(x))0.9975595470.9659153220.7479276660.5668246750.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 4write(*,*)limit(5,1,4)4! flipping the bounds works just as wellwrite(*,*)limit(5,1,4)4

limit also works on arrays:

write(*,*)limit(arange(0,4),1,3):11233

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 recursiveinteger function fibonacci(n) result(fib)integer,intent(in) :: nif(n == 0)then
    fib =0elseif(n == 1)then
    fib =1else
    fib = fibonacci(n-1)+fibonacci(n-2)endif
endfunction fibonacciwrite(*,*)map(fibonacci,[17,5,13,22])1597523317711

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 evenwrite(*,*)filter(even,[1,2,3,4,5])24

Functions can be chained together into pretty one-liners:

write(*,*)filter(even,map(fibonacci,arange(1,10)))2834

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 sumwrite(*,*)foldl(add,0.,arange(1.,5.))15.0000000! left-fold an array using mult to compute array productwrite(*,*)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 multpt1write(*,*)unfold(multpt1,[1.],5)1.000000001.100000021.210000041.331000091.46410012

Set functions: set, union, intersection, complement

Function set returns all unique elements of an input array:

write(*,*)set([1,1,2,2,3])123

Common functions that operate on sets, union, intersection, and complement, are also available:

! unique elements that are found in either arraywrite(*,*)union([1,2,2],[2,3,3,4])1234! unique elements that are found in both arrayswrite(*,*)intersection([1,2,2],[2,3,3,4])2! unique elements that are found first but not in second arraywrite(*,*)complement([1,2,2],[2,3,3,4])1

Contributing

Please submit a bug report or a request for new feature here.


Viewing all articles
Browse latest Browse all 25817

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>