This library provides a very minimal (1 module, less than 400 lines of code, no dependencies) and lightweight implementation of a dictionary (key--value pairs) and set (only keys) in Modern Fortran. Some features, such as interface for popular data types (INTEGER, CHARACTER etc) are intentionally omitted to keep the size down to the minimum. This means that you likely need many SELECT TYPE statements or create your wrappers to help you interpret retrieved items. Since the tree is built solely using ALLOCATABLE, it does not need to be explicitly deallocated. Be careful on assignments and avoid returning it from a function, or you might face a lot of copies. Best use dict_t and set_t as arguments for subroutines, as that will prevent unneccesary copy.
The implementation uses PURE procedures wherever possible. Since this meant that using pointers was out of the picture, traversing the tree uses recursion. This should be fine up to hundreds thousands of items, but if you want to process really large sets, you might want to try another implementation. Pushing and retrieving values from a hashmap currently involves copies, so consider that if you store large objects (consider using POINTER wrapped in a derived type).
Critical features that have not yet been implemented:
- deletion of items,
- copy-less insertion of
dict_tvalues (byMOVE_ALLOC), - copy-less retrieving of
dict_tvalue (byPOINTER), - iterating over items of a tree (likely will be very inefficient due to pure implementation).
Check out example/demo.f90 by running fpm run --example.
program tree_demo
use yaftree_m
implicit none (type, external)
type(dict_t) :: dict
type(set_t) :: set
print *, 'call insert(set, "abc")'
call insert(set, "abc")
print *, 'call insert(set, 321)'
! any type without pointers or allocatable components is valid
call insert(set, 321)
! size overloaded for consistency with arrays
print *, "size(set)", "=", size(set)
! .in. operator -- like Python
print *, '"abc" .in. set', "=", "abc" .in. set
print *, 'call insert(dict, "my", "value")'
call insert(dict, "my", "value")
print *, 'call insert(dict, 15, 35)'
call insert(dict, 15, 35)
print *, '"my" .in. dict', "=", "my" .in. dict
! typically you will retrieve stored values like this
select type (val => get(dict, "my"))
type is (character(len=*))
print *, 'get(dict, "my")', "=", val
end select
! when key is not found, special object is returned
select type (val => get(dict, "ohno"))
type is (key_not_found_t)
print *, "key not found"
end select
! no explicit cleanup call is needed, since all data structure
! is build on allocatables
end programTested successfully on:
- ifx (IFX) 2025.0.0 20241008
- GNU Fortran 14.2.1 20241007