M_list Module

NAME

M_list(3f) - [M_list::INTRO] maintain simple lists
(LICENSE:PD)

SYNOPSIS

use M_list, only : insert, replace, remove, locate
use M_list, only : dictionary

DESCRIPTION

The M_list(3fm) module allows for maintaining an allocatable array of
intrinsic type (REAL, INTEGER, CHARACTER) as a sorted list. An example
is given that creates a keyword-value dictionary using the lists.

The lists are maintained as simple allocatable arrays. Each time an
entry is added or deleted the array is re-allocated. Because of the
expense of reallocating the data these routines are best suited for
maintaining small lists that do not change size frequently.

The advantage of this simplistic approach is that the dictionary
components are simple arrays of intrinsic types which can be easily
accessed with standard routines. It is easy to understand, as it
works with simple arrays. For more demanding applications this would
be implemented as a linked list, which there are a number of freely
available examples of; several are listed on the Fortran Wiki.

BASIC LIST

subroutine locate(list,value,place,ier,errmsg)  finds the index where a
                                                value is found or should
                                                be in a sorted array and
                                                flag if the value exists
                                                already
subroutine insert(list,value,place)     insert entry into an allocatable
                                        array at specified position
subroutine replace(list,value,place)    replace entry in an allocatable
                                        array at specified position
subroutine remove(list,place)           remove entry from an allocatable
                                        array at specified position

EXAMPLES

Sample program

program demo_M_list
use M_list, only : insert, locate, replace, remove
! create a dictionary with character keywords, values, and value lengths
! using the routines for maintaining a list

 use M_list, only : locate, insert, replace
 implicit none
 character(len=:),allocatable   :: keywords(:)
 character(len=:),allocatable   :: values(:)
 integer,allocatable            :: counts(:)
 integer                        :: i
 ! insert and replace entries
 call update('b','value of b')
 call update('a','value of a')
 call update('c','value of c')
 call update('c','value of c again')
 call update('d','value of d')
 call update('a','value of a again')
 ! show array
 write(*,'(*(a,"==>","[",a,"]",/))')&
  & (trim(keywords(i)),values(i)(:counts(i)),i=1,size(keywords))
 ! remove some entries
 call update('a')
 call update('c')
 write(*,'(*(a,"==>","[",a,"]",/))')&
  & (trim(keywords(i)),values(i)(:counts(i)),i=1,size(keywords))
 ! get some values
 write(*,*)'get b=>',get('b')
 write(*,*)'get d=>',get('d')
 write(*,*)'get notthere=>',get('notthere')
 !
 contains
 subroutine update(key,valin)
 character(len=*),intent(in)           :: key
 character(len=*),intent(in),optional  :: valin
 integer                               :: place
 integer                               :: ilen
 character(len=:),allocatable          :: val
 if(present(valin))then
    val=valin
    ilen=len_trim(val)
    ! find where string is or should be
    call locate(keywords,key,place)
    ! if string was not found insert it
    if(place.lt.1)then
       call insert(keywords,key,iabs(place))
       call insert(values,val,iabs(place))
       call insert(counts,ilen,iabs(place))
    else
       call replace(values,val,place)
       call replace(counts,ilen,place)
    endif
 else
    call locate(keywords,key,place)
    if(place.gt.0)then
       call remove(keywords,place)
       call remove(values,place)
       call remove(counts,place)
    endif
 endif
 end subroutine update
 function get(key) result(valout)
 character(len=*),intent(in)   :: key
 character(len=:),allocatable  :: valout
 integer                       :: place
    ! find where string is or should be
    call locate(keywords,key,place)
    if(place.lt.1)then
       valout=''
    else
       valout=values(place)(:counts(place))
    endif
 end function get
 end program demo_M_list

Results

   >  d==>[value of d]
   >  c==>[value of c again]
   >  b==>[value of b]
   >  a==>[value of a again]
   >
   > d==>[value of d]
   > b==>[value of b]
   >
   >  get b=>value of b
   >  get d=>value of d
   >  get notthere=>


BASIC DICTIONARY

A basic dictionary that uses the basic M_list functions.

Consider using generic linked-list based dictionaries when heavy
usage is required, now that that is available in more recent versions
of Fortran.

Note: this does not work with gfortran(1) up to at least 7.4.0 but
works from at least 10.3.0 and onward.

Dictionary type definition:

   type dictionary
      character(len=:),allocatable :: key(:)
      character(len=:),allocatable :: value(:)
      integer,allocatable          :: count(:)
      contains
         procedure,public :: get => dict_get
         procedure,public :: set => dict_add
         procedure,public :: del => dict_delete
         procedure,public :: clr => dict_clear
   end type dictionary

   %get      get value from type(dictionary) given an existing key
   %set      set or replace value for type(dictionary) given a key
   %del      delete an existing key from type(dictionary)
   %clr      empty a type(dictionary)
   %ifdef    test if name is defined

EXAMPLES

Sample program

   program test_dictionary
   use M_list, only : dictionary
   implicit none
   type(dictionary)             :: table
     !
     ! create a character string dictionary
     !
     call table%set('A','aye')
     call table%set('B','bee')
     call table%set('C','see')
     call table%set('D','dee')
     !
     write(*,*)'A=',table%get('A')
     write(*,*)'C=',table%get('C')
     write(*,*)'notthere=',table%get('notthere')
     !
     call print_dict()
     !
     ! delete dictionary entries
     !
     call  table%del('A')
     call  table%del('C')
     call  table%del('z') ! a noop as there is no key of 'z'
     !
     call print_dict()
     !
     ! clear dictionary
     !
     call  table%clr()
     !
     call print_dict()
   !
   contains
   !
   subroutine print_dict()
   integer :: i
      ! the dictionary is just three arrays
      write(*,'("DICTIONARY:")')
      write(*,'(*(a,"==>","[",a,"]",/))') &
      & (trim(table%key(i)),               &
      & table%value(i)(:table%count(i)),    &
      & i=1,size(table%key))
      !
   end subroutine print_dict
   !
   end program test_dictionary

AUTHOR

John S. Urban

LICENSE

Public Domain


Contents


Interfaces

public interface insert

  • private subroutine insert_c(list, value, place)

    NAME

    insert(3f) - [M_list] insert entry into a string array at specified position
    (LICENSE:PD)
    

    SYNOPSIS

    subroutine insert(list,value,place)

    character(len=*)|doubleprecision|real|integer,intent(in) :: value
    character(len=:)|doubleprecision|real|integer,intent(in) :: list(:)
    integer,intent(in)    :: place
    

    DESCRIPTION

    Insert a value into an allocatable array at the specified index.
    The list and value must be of the same type (CHARACTER, DOUBLEPRECISION,
    REAL, or INTEGER)
    

    OPTIONS

    list    is the list array. Must be sorted in descending order.
    value   the value to place in the array
    PLACE   is the subscript that the entry should be placed at
    

    EXAMPLES

    Find if a string is in a sorted array, and insert the string into the list if it is not present …

     program demo_insert
     use M_sort, only : sort_shell
     use M_list, only : locate, insert
     implicit none
     character(len=:),allocatable :: arr(:)
     integer                       :: i
    
     arr=[character(len=20) :: '', 'ZZZ', 'aaa', 'b', 'xxx' ]
     ! make sure sorted in descending order
     call sort_shell(arr,order='d')
     ! add or replace values
     call update(arr,'b')
     call update(arr,'[')
     call update(arr,'c')
     call update(arr,'ZZ')
     call update(arr,'ZZZ')
     call update(arr,'ZZZZ')
     call update(arr,'')
     call update(arr,'z')
    
     contains
     subroutine update(arr,string)
     character(len=:),allocatable :: arr(:)
     character(len=*)             :: string
     integer                      :: place, end
    
     end=size(arr)
     ! find where string is or should be
     call locate(arr,string,place)
     ! if string was not found insert it
     if(place.lt.1)then
        call insert(arr,string,abs(place))
     endif
     ! show array
     end=size(arr)
     write(*,'("array is now SIZE=",i0,1x,*(a,","))')end,(trim(arr(i)),i=1,end)
    
     end subroutine update
     end program demo_insert
    

    Results

        > array is now SIZE=5 xxx,b,aaa,ZZZ,,
        > array is now SIZE=6 xxx,b,aaa,[,ZZZ,,
        > array is now SIZE=7 xxx,c,b,aaa,[,ZZZ,,
        > array is now SIZE=8 xxx,c,b,aaa,[,ZZZ,ZZ,,
        > array is now SIZE=9 xxx,c,b,aaa,[,ZZZZ,ZZZ,ZZ,,
        > array is now SIZE=10 z,xxx,c,b,aaa,[,ZZZZ,ZZZ,ZZ,,
    

    AUTHOR

    1989,2017 John S. Urban
    

    LICENSE

    Public Domain
    

    Arguments

    Type IntentOptional Attributes Name
    character(len=:), allocatable :: list(:)
    character(len=*), intent(in) :: value
    integer, intent(in) :: place
  • private subroutine insert_d(list, value, place)

    Arguments

    Type IntentOptional Attributes Name
    doubleprecision, allocatable :: list(:)
    doubleprecision, intent(in) :: value
    integer, intent(in) :: place
  • private subroutine insert_r(list, value, place)

    Arguments

    Type IntentOptional Attributes Name
    real, allocatable :: list(:)
    real, intent(in) :: value
    integer, intent(in) :: place
  • private subroutine insert_i(list, value, place)

    Arguments

    Type IntentOptional Attributes Name
    integer, allocatable :: list(:)
    integer, intent(in) :: value
    integer, intent(in) :: place
  • private subroutine insert_l(list, value, place)

    Arguments

    Type IntentOptional Attributes Name
    logical, allocatable :: list(:)
    logical, intent(in) :: value
    integer, intent(in) :: place

public interface locate

  • private subroutine locate_c(list, value, place, ier, errmsg)

    NAME

    locate(3f) - [M_list] finds the index where a string is found or
                 should be in a sorted array
    (LICENSE:PD)
    

    SYNOPSIS

    subroutine locate(list,value,place,ier,errmsg)

    character(len=:)|doubleprecision|real|integer,allocatable :: list(:)
    character(len=*)|doubleprecision|real|integer,intent(in)  :: value
    integer, intent(out)                  :: PLACE
    
    integer, intent(out),optional         :: IER
    character(len=*),intent(out),optional :: ERRMSG
    

    DESCRIPTION

    LOCATE(3f) finds the index where the VALUE is found or should
    be found in an array. The array must be sorted in descending
    order (highest at top). If VALUE is not found it returns the index
    where the name should be placed at with a negative sign.
    
    The array and list must be of the same type (CHARACTER, DOUBLEPRECISION,
    REAL,INTEGER)
    

    OPTIONS

    VALUE         the value to locate in the list.
    LIST          is the list array.
    

    RETURNS

    PLACE         is the subscript that the entry was found at if it is
                  greater than zero(0).
    
                  If PLACE is negative, the absolute value of
                  PLACE indicates the subscript value where the
                  new entry should be placed in order to keep the
                  list alphabetized.
    
    IER           is zero(0) if no error occurs.
                  If an error occurs and IER is not
                  present, the program is stopped.
    
    ERRMSG        description of any error
    

    EXAMPLES

    Find if a string is in a sorted array, and insert the string into the list if it is not present …

     program demo_locate
     use M_sort, only : sort_shell
     use M_list, only : locate
     implicit none
     character(len=:),allocatable  :: arr(:)
     integer                       :: i
    
     arr=[character(len=20) :: '', 'ZZZ', 'aaa', 'b', 'xxx' ]
     ! make sure sorted in descending order
     call sort_shell(arr,order='d')
    
     call update(arr,'b')
     call update(arr,'[')
     call update(arr,'c')
     call update(arr,'ZZ')
     call update(arr,'ZZZZ')
     call update(arr,'z')
    
     contains
     subroutine update(arr,string)
     character(len=:),allocatable :: arr(:)
     character(len=*)             :: string
     integer                      :: place, plus, ii, end
     ! find where string is or should be
     call locate(arr,string,place)
     write(*,*)'for "'//string//'" index is ',place, size(arr)
     ! if string was not found insert it
     if(place.lt.1)then
        plus=abs(place)
        ii=len(arr)
        end=size(arr)
        ! empty array
        if(end.eq.0)then
           arr=[character(len=ii) :: string ]
        ! put in front of array
        elseif(plus.eq.1)then
           arr=[character(len=ii) :: string, arr]
        ! put at end of array
        elseif(plus.eq.end)then
           arr=[character(len=ii) :: arr, string ]
        ! put in middle of array
        else
           arr=[character(len=ii) :: arr(:plus-1), string,arr(plus:) ]
        endif
        ! show array
        write(*,'("SIZE=",i0,1x,*(a,","))')end,(trim(arr(i)),i=1,end)
     endif
     end subroutine update
     end program demo_locate
    

    Results

       >  for "b" index is            2           5
       >  for "[" index is           -4           5
       > SIZE=5 xxx,b,aaa,[,ZZZ,
       >  for "c" index is           -2           6
       > SIZE=6 xxx,c,b,aaa,[,ZZZ,
       >  for "ZZ" index is           -7           7
       > SIZE=7 xxx,c,b,aaa,[,ZZZ,,
       >  for "ZZZZ" index is           -6           8
       > SIZE=8 xxx,c,b,aaa,[,ZZZZ,ZZZ,,
       >  for "z" index is           -1           9
       > SIZE=9 z,xxx,c,b,aaa,[,ZZZZ,ZZZ,,
    

    AUTHOR

    1989,2017 John S. Urban
    

    LICENSE

    Public Domain
    

    Arguments

    Type IntentOptional Attributes Name
    character(len=:), allocatable :: list(:)
    character(len=*), intent(in) :: value
    integer, intent(out) :: place
    integer, intent(out), optional :: ier
    character(len=*), intent(out), optional :: errmsg
  • private subroutine locate_d(list, value, place, ier, errmsg)

    Arguments

    Type IntentOptional Attributes Name
    doubleprecision, allocatable :: list(:)
    doubleprecision, intent(in) :: value
    integer, intent(out) :: place
    integer, intent(out), optional :: ier
    character(len=*), intent(out), optional :: errmsg
  • private subroutine locate_r(list, value, place, ier, errmsg)

    Arguments

    Type IntentOptional Attributes Name
    real, allocatable :: list(:)
    real, intent(in) :: value
    integer, intent(out) :: place
    integer, intent(out), optional :: ier
    character(len=*), intent(out), optional :: errmsg
  • private subroutine locate_i(list, value, place, ier, errmsg)

    Arguments

    Type IntentOptional Attributes Name
    integer, allocatable :: list(:)
    integer, intent(in) :: value
    integer, intent(out) :: place
    integer, intent(out), optional :: ier
    character(len=*), intent(out), optional :: errmsg

public interface remove

  • private subroutine remove_c(list, place)

    NAME

    remove(3f) - [M_list] remove entry from an allocatable array at
                 specified position
    (LICENSE:PD)
    

    SYNOPSIS

    subroutine remove(list,place)

    character(len=:)|doubleprecision|real|integer,intent(inout) :: list(:)
    integer, intent(out) :: PLACE
    

    DESCRIPTION

    Remove a value from an allocatable array at the specified index.
    The array is assumed to be sorted in descending order. It may be of
    type CHARACTER, DOUBLEPRECISION, REAL, or INTEGER.
    

    OPTIONS

    list    is the list array.
    PLACE   is the subscript for the entry that should be removed
    

    EXAMPLES

    Sample program

     program demo_remove
     use M_sort, only : sort_shell
     use M_list, only : locate, remove
     implicit none
     character(len=:),allocatable :: arr(:)
     integer                       :: i
     integer                       :: end
    
     arr=[character(len=20) :: '','ZZZ','Z','aaa','b','b','ab','bb','xxx' ]
     ! make sure sorted in descending order
     call sort_shell(arr,order='d')
    
     end=size(arr)
     write(*,'("SIZE=",i0,1x,*(a,","))')end,(trim(arr(i)),i=1,end)
     call remove(arr,1)
     end=size(arr)
     write(*,'("SIZE=",i0,1x,*(a,","))')end,(trim(arr(i)),i=1,end)
     call remove(arr,4)
     end=size(arr)
     write(*,'("SIZE=",i0,1x,*(a,","))')end,(trim(arr(i)),i=1,end)
    
     end program demo_remove
    

    Results

       > SIZE=9 xxx,bb,b,b,ab,aaa,ZZZ,Z,,
       > SIZE=8 bb,b,b,ab,aaa,ZZZ,Z,,
       > SIZE=7 bb,b,b,aaa,ZZZ,Z,,
    

    AUTHOR

    1989,2017 John S. Urban
    

    LICENSE

    Public Domain
    

    Arguments

    Type IntentOptional Attributes Name
    character(len=:), allocatable :: list(:)
    integer, intent(in) :: place
  • private subroutine remove_d(list, place)

    Arguments

    Type IntentOptional Attributes Name
    doubleprecision, allocatable :: list(:)
    integer, intent(in) :: place
  • private subroutine remove_r(list, place)

    Arguments

    Type IntentOptional Attributes Name
    real, allocatable :: list(:)
    integer, intent(in) :: place
  • private subroutine remove_i(list, place)

    Arguments

    Type IntentOptional Attributes Name
    integer, allocatable :: list(:)
    integer, intent(in) :: place
  • private subroutine remove_l(list, place)

    Arguments

    Type IntentOptional Attributes Name
    logical, allocatable :: list(:)
    integer, intent(in) :: place

public interface replace

  • private subroutine replace_c(list, value, place)

    NAME

    replace(3f) - [M_list] replace entry in a string array at specified position
    (LICENSE:PD)
    

    SYNOPSIS

    subroutine replace(list,value,place)

    character(len=*)|doubleprecision|real|integer,intent(in) :: value
    character(len=:)|doubleprecision|real|integer,intent(in) :: list(:)
    integer, intent(out)          :: PLACE
    

    DESCRIPTION

    replace a value in an allocatable array at the specified index. Unless
    the array needs the string length to increase this is merely an assign
    of a value to an array element.
    
    The array may be of type CHARACTER, DOUBLEPRECISION, REAL, or INTEGER.
    It is assumed to be sorted in descending order without duplicate
    values.
    
    The value and list must be of the same type.
    

    OPTIONS

    VALUE         the value to place in the array
    LIST          is the array.
    PLACE         is the subscript that the entry should be placed at
    

    EXAMPLES

    Replace key-value pairs in a dictionary

     program demo_replace
     use M_list, only  : insert, locate, replace
     ! Find if a key is in a list and insert it
     ! into the key list and value list if it is not present
     ! or replace the associated value if the key existed
     implicit none
     character(len=20)            :: key
     character(len=100)           :: val
     character(len=:),allocatable :: keywords(:)
     character(len=:),allocatable :: values(:)
     integer                      :: i
     integer                      :: place
     call update('b','value of b')
     call update('a','value of a')
     call update('c','value of c')
     call update('c','value of c again')
     call update('d','value of d')
     call update('a','value of a again')
     ! show array
     write(*,'(*(a,"==>",a,/))')&
            &(trim(keywords(i)),trim(values(i)),i=1,size(keywords))
    
     call locate(keywords,'a',place)
     if(place.gt.0)then
        write(*,*)'The value of "a" is ',trim(values(place))
     else
        write(*,*)'"a" not found'
     endif
    
     contains
     subroutine update(key,val)
     character(len=*),intent(in)  :: key
     character(len=*),intent(in)  :: val
     integer                      :: place
    
     ! find where string is or should be
     call locate(keywords,key,place)
     ! if string was not found insert it
     if(place.lt.1)then
        call insert(keywords,key,abs(place))
        call insert(values,val,abs(place))
     else ! replace
        call replace(values,val,place)
     endif
    
     end subroutine update
    end program demo_replace
    

    Results

    > d==>value of d
    > c==>value of c again
    > b==>value of b
    > a==>value of a again
    

    AUTHOR

    1989,2017 John S. Urban
    

    LICENSE

    Public Domain
    

    Arguments

    Type IntentOptional Attributes Name
    character(len=:), allocatable :: list(:)
    character(len=*), intent(in) :: value
    integer, intent(in) :: place
  • private subroutine replace_d(list, value, place)

    Arguments

    Type IntentOptional Attributes Name
    doubleprecision, allocatable :: list(:)
    doubleprecision, intent(in) :: value
    integer, intent(in) :: place
  • private subroutine replace_r(list, value, place)

    Arguments

    Type IntentOptional Attributes Name
    real, allocatable :: list(:)
    real, intent(in) :: value
    integer, intent(in) :: place
  • private subroutine replace_i(list, value, place)

    Arguments

    Type IntentOptional Attributes Name
    integer, allocatable :: list(:)
    integer, intent(in) :: value
    integer, intent(in) :: place
  • private subroutine replace_l(list, value, place)

    Arguments

    Type IntentOptional Attributes Name
    logical, allocatable :: list(:)
    logical, intent(in) :: value
    integer, intent(in) :: place

Derived Types

type, public ::  dictionary

Components

Type Visibility Attributes Name Initial
integer, public, allocatable :: count(:)
character(len=:), public, allocatable :: key(:)
character(len=:), public, allocatable :: value(:)

Type-Bound Procedures

procedure , public :: clr => dict_clear Subroutine
procedure , public :: del => dict_delete Subroutine
procedure , public :: get => dict_get Function
procedure , public :: ifdef => dict_ifdef Function
procedure , public :: set => dict_add Subroutine