Динамически печатать массив в Matlab для Fortran - PullRequest
0 голосов
/ 28 января 2019

Matlab имеет возможность иметь динамически типизированный массив.Например, код Matlab:

function testing1
clc;
close all;
    chrom_len = 35;


function out = newChromosome(len)
genes_pool = struct(...
    'gene',       {'#', 'U-', 'Rs', '+', '-', '*', '/', '^'}, ...
    'stackDepth', {0,   0,    1,    2,   2,   2,  2,  2},...
    'deltaStack', {1,   1,    0,   -1,  -1,   -1, -1, -1});

function gene = newGene(stackDepth)
    % Select an appropriate gene
    done = false;    
    while ~done
        ind = randi([1 numel(genes_pool)], 1, 1);

        if genes_pool(ind).stackDepth <= stackDepth
            done = true;
        end
    end
    % Generate output

    gene = genes_pool(ind);
        disp('start');
        disp('gene.gene is ');
        disp(gene.gene);
         disp('stop');
    if gene.gene == '#'
        gene.gene = round(randn(1,1) * 5);
        disp('new gene.gene is ');
        disp(gene.gene);
        disp('the gene is ');
        disp(gene);
    end
   end

 genes = {}; stack = [];
 stackDepth = 0;    
 i = 1;
 while i <= len || (i > len && stackDepth ~= 1)
     gene = newGene(stackDepth);
 %    disp('outside gene');
 %    disp(gene);
    class(gene.gene) 
    stackDepth = stackDepth + gene.deltaStack;
 %    disp('stackDepth');
 %    disp(stackDepth);
    genes = [genes, gene.gene];
 %   disp('genes');
 %    disp(genes);
    stack = [stack, stackDepth];
 %    disp('stack')
 %   disp(stack);
    i = i + 1;
    if strcmp(gene.gene, 'X') && rand(1,1) < 0.5
        genes = [genes, randi([2 4], 1)]; stackDepth = stackDepth + 1;
        stack = [stack, stackDepth];
        genes = [genes, '^']; stackDepth = stackDepth - 1;
        stack = [stack, stackDepth];
    end
    return;
 end
 out = struct('genes', {genes}, 'stack', {stack});
 return; 
end

newChromosome(2) 
end

Выход Matlab при первом запуске (я использую GNU Octave, что то же самое):

start
gene.gene is
U-
stop
ans = char 

Выход Matlab при втором запуске:

start
gene.gene is
#
stop
new gene.gene is
-11
the gene is

scalar structure containing the fields:

gene = -11
stackDepth = 0
deltaStack =  1
ans = double

Когда я переводю Matlab в Fortran, я хочу продублировать вывод, чтобы вывод Fortran был того же типа, что и в Matlab.Результаты gen.gene могут быть символьными или двойными.Это означает, что он может иметь два типа.Можно ли сделать то же самое в Фортране?Как мне динамически ввести переменную в Fortran?Я считаю, что это как-то связано с приведением типов.Пока что мое решение на Фортране таково:

function newchromosome(clen) result(out1)
implicit none

 type garray
 character*2 :: gene; 
 integer     :: stackdepth; 
 integer     :: deltastack;
 end type 

 type(garray), dimension(8) :: genespool
 type(garray), dimension(1) :: gene2

  integer,intent(in) :: clen;
  integer :: out1;
  integer :: inttest;

  genespool = [garray('#',0,1),garray('X',0,1),garray('U-',1,0), &
         garray('+',2,-1),garray('-',2,-1),garray('*',2,-1), &
         garray('/',2,-1),garray('^',2,-1) ]

  gene2 = [garray('s',0,0) ]  

  out1 = clen;
  inttest = newgene(2); 

  contains

  function newgene(stackdepth) result(out2)
  integer, intent(in) :: stackdepth;
  integer :: out2;
  logical :: done;  
  integer :: ind;
  real    :: rnd1;
  character*2, dimension(:), allocatable:: answer2;
  character*2 :: answer;

 answer = 'ye'
 out2=0;
 allocate(answer2(1)); 
!  gene=0;
!  gene = stackdepth;

  done = .FALSE.

  do while (done .EQV. .FALSE.)
    call random_number(rnd1);
    ind = nint(rnd1*size(genespool));  
    if (genespool(ind)%stackdepth <= stackdepth) then
          done = .True.
    end if         
  end do

  ! Generate output
  print*, "genespool(ind)  ", genespool(ind);    
  print*, "gene 2          ", gene2;
 gene2=genespool(ind)
  print*, "new gene 2      ",gene2;
  print*, "new gene2.gene     ",gene2%gene;
  answer2=gene2%gene;

  print*, "test 2 ", answer;
  if ( gene2(1)%gene == '#' ) then
    call random_number(rnd1);
  !  gene2%gene = nint(rnd1*5);   !<------ problem (convert integer to string integer)
  endif  
  return;      
  end function newgene 
end function newchromosome

program testing1
! ------ unit declaration ----------------------------------------------
use iso_fortran_env
implicit none
! ----------------------------------------------------------------------
! ------ variable declaration ------------------------------------------
integer                 :: chromlen;
integer                 :: newchromosome;
integer                 :: test;

  chromlen = 35; 
  test = 0;
  test=int(newChromosome(chromlen));
  print*, "newChromosome is ", test;
 flush( output_unit )

 end program testing1

Я добавил несколько фиктивных переменных, таких как 'answer2', только для целей отладки.Я пометил стрелкой, где мой код в FORTRAN вызывает проблемы из-за объявления динамического типа Matlab.В Фортране может быть невозможно иметь переменную, которая является символьной и двойной (вещественной).В FORTRAN у меня не может быть U- и -11, как в Matlab, потому что в FORTRAN есть объявление типа gene2 как символа.Matlab гибок, FORTRAN строг.В FORTRAN, как я могу заставить gene2.gene принимать 'real type' -11, не вызывая ошибки?Есть ли лучший способ сделать объявление типа, например,% ptr или class (*) для объявления гетерогенного массива?

1 Ответ

0 голосов
/ 01 февраля 2019

Ну, я думаю, что наконец понял ваш код.И, если я это сделаю, вам нужно, чтобы компонент gene%gene имел возможность отображать символ или число при необходимости.

Вам определенно не нужно / не нужно имитировать динамический тип иливсе, что трудно, хотя.Вы можете просто сохранить компонент как символьный тип, и если вам нужно сохранить целое число, вы преобразуете его в текст.В Фортране преобразование других типов в символ выполняется с помощью тех же операторов ввода-вывода, к которым вы привыкли, но вместо номера единицы вы передаете символьную переменную.Например:

write(gene2%gene, '(i0)') int(rnd1 * 5);

Этот оператор запишет целочисленное значение переданного выражения в символьный компонент.В этом случае формат (i0) будет использовать минимальное пространство, необходимое для номера.Если вам нужно хранить вещественную переменную вместо целого, используйте тот же принцип;но вам может потребоваться увеличить длину символьного компонента и использовать другой формат, например (g0).


Даже если это технически решает ваш вопрос, я написал порт вашей программы Matlab в:как я считаю, более "Фортран".Вы можете использовать его как упражнение для изучения некоторых языковых концепций и конструкций, которые вам могут быть не знакомы.

Оберните функциональность в модуль:

module genetics
  implicit none

  ! use a constant, so it will be easier to refactor when needed
  integer, parameter :: g_charlen = 2

  type gene
    character(g_charlen) :: gene
    integer :: depth
    integer :: delta
  end type

  ! initializing in a data statement looks nicer in this case
  type(gene) :: pool(8)
  data pool%gene  / '#', 'U-', 'Rs',  '+',  '-',  '*',  '/',  '^'/
  data pool%depth /   0,    0,    1,    2,    2,    2,    2,    2/
  data pool%delta /   1,    1,    0,   -1,   -1,   -1,   -1,   -1/

  ! or whatever implementation you may have
  type chromosome
    character(g_charlen) :: gene
    integer :: stack
  end type

contains

  ! there is no need for nested procedures in this case
  function new_gene(depth) result(out)
    integer, intent(in) :: depth
    type(gene) :: out
    real :: rnd

    associate(filtered => pack(pool, pool%depth <= depth))
      if(size(filtered) < 1) error stop 'there is no suitable gene in the pool'
      call random_number(rnd)
      out = filtered(int(rnd * size(filtered)) + 1)
    end associate
    if(out%gene == '#') then
      call random_number(rnd)
      write(out%gene, '(i0)') int(rnd * 5) ! <- here is the line
    end if
  end

  ! or whatever implementation you may have
  function new_chromosome(clen) result(out)
    integer, intent(in) :: clen
    type(chromosome), allocatable :: out(:)
    type(gene) :: g
    integer :: i, depth
    real :: rnd
    character(g_charlen) :: gch

    allocate(out(0))
    depth = 0
    i = 1
    do while(i <= clen .or. depth /= 1)
      g = new_gene(depth)
      depth = depth + g%delta
      out = [out, chromosome(g%gene, depth)]
      i = i + 1
      if(g%gene == 'X') then
        call random_number(rnd)
        if(rnd >= 0.5) cycle
        call random_number(rnd)
        write(gch, '(i0)') int(rnd * 3) + 2;
        out = [out, chromosome(gch, depth + 1), chromosome('^', depth - 1)]
      end if
    end do
  end
end

И вы можете протестировать код с помощью:

program test
  use :: genetics
  implicit none

  type(chromosome), allocatable :: c1(:)
  integer :: i

  call random_seed
  c1 = new_chromosome(10)
  do i = 1, size(c1)
    print *, c1(i)
  end do
end

Один возможный вывод:

 1            1
 U-           2
 *            1
 U-           2
 1            3
 2            4
 -            3
 U-           4
 U-           5
 ^            4
 /            3
 Rs           3
 -            2
 0            3
 -            2
 ^            1
...