Defines a class that can enable objects that behave like lambda functions.
To use this class, define a type of either lambda_obj or lambda_obj_err, or extend the lambda_obj class as necessary, such
that an interface that matches the function you wish to lambdafy.
Once defined, the lambda object can evaluate itself by calling the type-bound procedure eval. e.g. f%eval(x)
(or f%eval(x, lerr), f%eval(x, [argument list], etc))
Example - Defining a lambda function f(x,rval,ival) where rval and ival are a real and integer argument, respectively.
This implementation uses an abstract interface, though this is not
strictly necessary unless you want to bind more than one function with the same interface.
module lambda_new
use swiftest ! This will bring in the lambda_function module
! Define types in a module
type, extends(lambda_obj) :: lambda_obj_ri_args
procedure(abstract_lambda_ri_args), pointer, nopass :: lambdaptr_ri_args => null()
real(DP) :: rval !! Real parameter
integer(I4B) :: ival !! Integer paramete
contains
generic :: init => lambda_ri_args_init
procedure :: eval => lambda_ri_args_eval
procedure, nopass :: lambda_ri_args_init
final :: lambda_ri_args_destroy
end type
interface lambda_obj
module procedure lambda_ri_args_init
end interface
abstract interface
function abstract_lambda_ri_args(x, rval, ival) result(y)
!Template for the lambda function
import DP, I4B
real(DP), dimension(:), intent(in) :: x !! Dependent variable
real(DP), intent(in) :: rval !! Real parameter
integer(I4B), intent(in) :: ival !! Integer parameter
real(DP) :: y !! Real result
end function
end interface
contains
type(lambda_obj_ri_args) function lambda_ri_args_init(lambda, rval, ival)
!! Initializes the lambda function parameters (can be used as a structure constructor)
implicit none
procedure(abstract_lambda_ri_args) :: lambda !! The lambda function that will be passed
real(DP), intent(in) :: rval !! Real parameter
integer(I4B), intent(in) :: ival !! Integer parameter
! Assign the procedure passed to this function to the procedure pointer
lambda_ri_args_init%lambdaptr_ri_args => lambda
! Assign the argument values
lambda_ri_args_init%rval = rval
lambda_ri_args_init%ival = ival
return
end function lambda_ri_args_init
function lambda_ri_args_eval(self, x) result(y)
!! Defines the evaluation method, allowing the lambda function to be called with a single argument
implicit none
class(lambda_obj_ri_args), intent(inout) :: self
real(DP), dimension(:), intent(in) :: x
real(DP) :: y
if (associated(self%lambdaptr_ri_args)) then
y = self%lambdaptr_ri_args(x, self%rval, self%ival)
self%lastval = y
if (allocated(self%lastarg)) deallocate(self%lastarg)
allocate(self%lastarg, source=x)
else
stop "Lambda function was not initialized"
end if
end function lambda_ri_args_eval
subroutine lambda_ri_args_destroy(self)
!! Finalizer method. Use this as a template for cleaning up the object upon destruction, such as nullifying pointers
implicit none
type(lambda_obj_ri_args) :: self
if (associated(self%lambdaptr_ri_args)) nullify(self%lambdaptr_ri_args)
end subroutine lambda_ri_args_destroy
function example_function(x, rval, ival) result(y)
! This is the actual function you are going to use as the lambda function. Its interface must match the abstract
! interface previously defined
implicit none
! Arguments
real(DP), dimension(:), intent(in) :: x
real(DP), intent(in) :: rval
integer(I4B), intent(in) :: ival
! Result
real(DP) :: y
! Internals
integer(I4B) :: i, n
n = size(x)
y = 42._DP * ival
do i = 1, n
y = y + x(i)**2
end do
return
end function example_function
end module lambda_new
program usage
use swiftest
use lambda_new
implicit none
type(lambda_obj_ri_args) :: f
real(DP) :: sigma_par
integer(I4B) :: iwonky, i,j
real(DP), dimension(12) :: xarr
sigma_par = 3.14_DP
iwonky = 13
f = lambda_obj(example_function, sigma_par, iwonky)
do i = 1, 10
xarr(:) = [(j * 0.25_DP / i, j=1, 12)]
write(,) i,f%eval(xarr)
end do
end program usage
Nodes of different colours represent the following:
Solid arrows point from a submodule to the (sub)module which it is
descended from. Dashed arrows point from a module or program unit to
modules which it uses.
Nodes of different colours represent the following:
Solid arrows point from a submodule to the (sub)module which it is
descended from. Dashed arrows point from a module or program unit to
modules which it uses.