Несъответствие на интерфейса във фиктивна процедура 'f' при предаване на функция към подпрограма

Опитвам се да напиша подпрограма (за минимизиране), която има два аргумента:

  • масив x с произволна дължина
  • функция f, която взема масив с тази дължина и връща скалар

примерен модул:

module foo

contains

  subroutine solve(x, f)
    real, dimension(:), intent(inout) :: x
    interface
      real pure function f(y)
        import x
        real, dimension(size(x)), intent(in) :: y
      end function
    end interface

    print *, x
    print *, f(x)
  end subroutine

end module

и тестова програма:

use foo

real, dimension(2) :: x = [1.0, 2.0]

call solve(x, g)

contains

  real pure function g(y)
    real, dimension(2), intent(in) :: y

    g = sum(y)
  end function

end

gfortran се проваля на:

call solve(x, g)
              1
Error: Interface mismatch in dummy procedure 'f' at (1): Shape mismatch in dimension 1 of argument 'y'

Ако променя size(x) => 2, той се компилира (и работи) добре. Също така работи добре, ако сменя : => 2. Но нито едно от тези решения не ми дава това, което искам.

Някакви идеи как мога да постигна това?


person js947    schedule 10.04.2013    source източник


Отговори (4)


Какво ще кажеш:

interface
  real pure function f(y)
    real, dimension(:), intent(in) :: y
  end function
end interface

Когато подадете аргумента на solve към функцията, размерът на масива автоматично ще бъде предаден. Не е необходимо да правите тази част от интерфейса.

person M. S. B.    schedule 10.04.2013
comment
Освен това не се компилира, страхувам се, с подобна грешка: Error: Interface mismatch in dummy procedure 'f' at (1): Shape mismatch in argument 'y' - person js947; 10.04.2013
comment
всички ваши размери трябва да са :, също във функцията g(y) (в противен случай смесвате масиви с явна и предполагаема форма) - person steabert; 10.04.2013
comment
Проблемът е решен предполагам! Надявах се да спечеля безопасността, като бъда изричен с размера на масива в g(y), но може би това наистина не е възможно. - person js947; 10.04.2013

Ако искате да постигнете безопасност, както е посочено във вашия коментар за решението на M.S.B, трябва да използвате -fcheck=bounds и компилаторът ще генерира проверки по време на изпълнение за предполагаеми и отложени масиви с форми. Вижте man страницата на gfortran за повече информация относно -fcheck. Въпреки това ще загубите малко скорост.

person bijancn    schedule 11.04.2013

Имате решението, но за какво си струва обяснение... ако фиктивният аргумент има изричен интерфейс (което има тук), тогава има изискване характеристиките на процедура, предадена като действителният аргумент трябва да съвпада с тези на фиктивния аргумент, с някои изключения около чистотата и елементарните присъщи характеристики. Характеристиките на една процедура включват, наред с други неща, характеристиките на нейните фиктивни аргументи.

Характеристиките на фиктивен аргумент включват неговата форма, наред с други неща. Ако тази форма не е постоянен израз - характеристиките включват "точната зависимост [на формата] от обектите в израза".

Интерфейсният блок за фиктивния аргумент f декларира, че масивът е с размер SIZE(x). x е свързана с хост променлива с предполагаема форма - нейният размер може да варира по време на изпълнение, така че SIZE(x) не е константа. Следователно този израз и същностите в него стават характеристика на фиктивния аргумент.

Модулната процедура g декларира, че масивът е с размер 2. Това очевидно е константа.

Независимо от стойността на неконстантния израз за размера на фиктивния аргумент на f, тези характеристики на размера на масива (някакъв израз срещу константа) не съвпадат - оттук и грешката.

Когато замените SIZE(x) с константата 2, характеристиките очевидно съвпадат. Когато промените предполагаемата форма x да бъде постоянен размер 2 - тогава SIZE(x) става постоянен израз на стойност 2 - тъй като това е постоянен израз, всичко, което е от значение, е неговата стойност - следователно характеристиките на двата аргумента тогава съвпадат. Когато промените както фиктивния аргумент на f, така и фиктивния аргумент на g, за да бъде приета форма (:), характеристиките съвпадат.

person IanH    schedule 10.04.2013

Ето демонстрация, която показва как се предава разпределяем масив.

Някои съвети:

  • Използвайте модули, за да избегнете тромавия интерфейс.
  • Добавете допълнителна информация за размера на матрицата, когато предавате масив към действителната функция. Например f(y, sizeinfo), така че във вашата действителна функция можете да декларирате правилно размера на входната матрица. Разпределяемият масив може да бъде предаден на подпрограма solve, така че размерът може да бъде получен чрез size(mat) във вашата подпрограма solve.

Така коригираната версия изглежда така:

module foo

contains

  subroutine solve(x, f)
    real, dimension(:), intent(inout) :: x
    real,external::f
        integer::sizeinfo

    print *,'x=', x
    sizeinfo = size(x)
    print *, 'f(x)=',f(x,sizeinfo)
  end subroutine

    real function g(y,sizeinfo)
    integer::sizeinfo
    real, dimension(sizeinfo) :: y

    g = sum(y)
  end function
end module

Ето основната програма:

program main
use foo

real, dimension(2) :: x = (/1.0, 2.0/)

call solve(x, g)

end program

И резултатът е:

x=   1.000000       2.000000    
f(x)=   3.000000 
person Kiyomine    schedule 21.02.2017
comment
Това има същия проблем като другия отговор. Смисълът на предаването на подпрограма е, че можете да подадете всяка подпрограма. Външният е стар, остарял и податлив на грешки. Използването на external за функция от същия модул за запазване на няколко реда код е наистина лош стил. За повече вижте предишния ми коментар към stackoverflow.com/a/42356839/721644 - person Vladimir F; 21.02.2017
comment
Добра точка. Но в такъв случай, бихте ли могли да дадете ПРИМЕР защо външният е податлив на грешки и какво ще стане, ако имате голямо количество подпрограми, които трябва да бъдат извикани? Ще трябва ли да напишете много и много interface? - person Kiyomine; 21.02.2017
comment
Защо би? Вие пишете един интерфейс и всички тези подпрограми съответстват на този интерфейс. Това е целият смисъл! Вие пишете библиотека и потребителят на библиотеката пише онези подпрограми, които се предават. Следователно те не могат да бъдат в един модул, защото някой друг ги пише (дори и двамата да са вие в крайна сметка). - person Vladimir F; 21.02.2017
comment
Прав си, ако има едно или няколко callsubs(sub, arg1,arg2). Съжалявам, че не бях ясен, защото това, което наистина исках да попитам, е какво ще стане, ако има голямо количество callsubs извикване на други sub s. Освен това все още се чудя защо използването на модул е ​​податливо на грешки. Бих искал да науча дали има пример, показващ, че използването на интерфейс наистина е добра практика. - person Kiyomine; 21.02.2017
comment
Не, използването на модул не е склонно към грешки, използването на external е!!! и както ви казах stackoverflow.com/a/42356839/721644 можете лесно да замените external с procedure(sub). И след като го направите, ще разберете, че sub действа като интерфейс, че можете просто да имате abstract interface в модула вместо sub и сте там, където сте били преди. - person Vladimir F; 21.02.2017
comment
Но това не е добро място за обсъждане тук, защото този въпрос е за обяснение на някаква грешка и вие не обяснявате грешка, а показвате различен подход, който според мен не отговаря на зададения въпрос. По-скоро е по темата, когато ти отговори първи. - person Vladimir F; 21.02.2017