Я пытаюсь написать простой модуль для обработки физических единиц в арифметических операциях.Моя цель - создать производные единицы из первичных.
Как вы можете видеть в следующем коде, у меня есть производный тип, а именно unit_t
, в котором хранится строка, представляющая саму единицу, степеньединица измерения, коэффициент преобразования (для преобразования в SI), логическая переменная, показывающая, клонируется ли единица, и указатели next
и prev
, которые указывают на следующую или предыдущую единицу (в случае, если у нас есть комбинация единиц,например kg * m / s**2
, так что в основном это связанный список, соединяющий различные устройства друг с другом).
У меня есть функция с именем unit_clone
для клонирования основного устройства.Функция unit_int_pow
перегружает оператор возведения в степень (**) и просто клонирует данную первичную единицу и обновляет ее экспоненту.Функция units_mul
перегружает оператор умножения (*).Эта функция сначала проверяет, клонированы ли два заданных объекта (если нет, то клонирует их), а затем просто соединяет их, используя указатели next
и `` `` prev```.
Вот мой код (вы должны скомпилировать его с помощью gfortran
)
module units
implicit none
type unit_t
character(len=16) :: symb
integer :: pow
real :: conv
logical :: cloned
type(unit_t), pointer :: next => null(), prev => null()
end type unit_t
! definitions
type(unit_t), target :: m = unit_t("m", 1, 1.d0, .false.)
type(unit_t), target :: km = unit_t("km", 1, 1.d3, .false.)
type(unit_t), target :: kg = unit_t("kg", 1, 1.d0, .false.)
type(unit_t), target :: s = unit_t("s", 1, 1.d0, .false.)
interface operator (**)
procedure unit_int_pow
end interface operator (**)
interface operator (*)
procedure units_mul
end interface operator (*)
contains
!> Cloning a given node (unit)
function unit_clone(u) result (clone)
implicit none
type(unit_t), intent(in) :: u
type(unit_t), allocatable, target :: clone
allocate(clone)
clone%symb = u%symb
clone%conv = u%conv
clone%pow = u%pow
clone%cloned = .true.
clone%next => u%next
clone%prev => u%prev
end function unit_clone
!> integer powers
function unit_int_pow(u1, p) result(u)
implicit none
type(unit_t), intent(in) :: u1
integer, intent(in) :: p
type(unit_t), allocatable, target :: u
u = unit_clone(u1)
u%pow = u%pow * p
end function unit_int_pow
!> multiplication
function units_mul (u1, u2) result (u1c)
implicit none
type(unit_t), intent(in) :: u1, u2
type(unit_t), allocatable, target :: u1c, u2c
if ( u1%cloned ) then
u1c = u1
else
u1c = unit_clone(u1)
end if
if ( u2%cloned ) then
u2c = u2
else
u2c = unit_clone(u2)
end if
u2c%prev => u1c
u1c%next => u2c
end function units_mul
end module units
program test
use units
implicit none
type(unit_t) :: u
u = kg**2 * m
print *, u%symb, "^", u%pow, " [expected: kg^2]"
print *, u%next%symb, "^", u%next%pow, " [expected: m^1]"
print *, u%next%prev%symb, "^", u%next%prev%pow, " [expected: kg^2]"
end program test
Проблема в том, что я получаю следующий вывод:
kg ^ 2 [expected: kg^2]
�ȷ2�U ^ 1 [expected: m^1]
�ȷ2�U ^ 2 [expected: kg^2]
По-видимому, после доступа к модулю next
или next%prev
(который является в основном заголовком этого короткого связанного списка), код выводит случайный символ вместо symb
s.Если я изменю порядок переменных в производном типе, unit_t
, например, если я поставлю symb
в конце производного типа, я получу правильные symb
с, но на этот раз неправильные pow
с.
Есть идеи, что является причиной этого довольно странного поведения?
Используя комментарий Рудриго, приведенный ниже, я переписал код, и теперь он работает нормально.Просто для справки, рабочий код выглядит следующим образом (если у вас есть дальнейшие предложения или изменения, пожалуйста, дайте мне знать, Nombre respository )
module units
implicit none
type unit_t
character(len=16) :: symb
real :: conv
real :: pow = 1.d0
logical :: cloned = .false.
type(unit_t), pointer :: next => null(), prev => null()
end type unit_t
! units definitions
type(unit_t), target :: m = unit_t("m", 1.d0)
type(unit_t), target :: km = unit_t("km", 1.d3)
type(unit_t), target :: kg = unit_t("kg", 1.d0)
type(unit_t), target :: s = unit_t("s", 1.d0)
interface operator (**)
procedure unit_int_pow
end interface operator (**)
interface operator (*)
procedure units_mul
end interface operator (*)
contains
!> Cloning a given node (unit)
function unit_clone(u) result (clone)
implicit none
type(unit_t), intent(in) :: u
type(unit_t), pointer :: clone
allocate(clone)
clone%symb = trim(u%symb)
clone%conv = u%conv
clone%pow = u%pow
clone%cloned = .true.
clone%next => u%next
clone%prev => u%prev
end function unit_clone
!> integer powers
function unit_int_pow(u1, p) result(u)
implicit none
type(unit_t), intent(in) :: u1
integer, intent(in) :: p
type(unit_t), pointer :: u
if ( u1%cloned ) then
! TODO: should be able to handle complex cases like: a * (b * c)**3
! most likly, only updating the power of the linked list chain
! would do the job
else
u => unit_clone(u1)
end if
u%pow = u%pow * p
end function unit_int_pow
!> multiplication
function units_mul (u1, u2) result (u2c)
implicit none
type(unit_t), intent(in), target :: u1, u2
type(unit_t), pointer :: u2c
if ( u2%cloned ) then
if ( associated(u2%prev) ) then
u2c => u2%prev%next
else
u2c => u2
end if
else
u2c => unit_clone(u2)
end if
if ( u1%cloned ) then
if ( associated(u2%prev) ) then
u2c%prev => u1%prev%next
else
u2c%prev => u1
end if
else
u2c%prev => unit_clone(u1)
end if
u2c%prev%next => u2c
end function units_mul
end module units