Несоответствие интерфейса в фиктивной процедуре '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

Если вы хотите обеспечить безопасность, как указано в вашем комментарии к решению MSB, вам следует использовать -fcheck=bounds, и компилятор сгенерирует проверки во время выполнения для предполагаемых и отложенных массивов форм. См. справочную страницу 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 устарел, устарел и подвержен ошибкам. Использование external для функции из того же модуля для сохранения нескольких строк кода — это действительно плохой стиль. Подробнее см. мой предыдущий комментарий к stackoverflow.com/a/42356839/721644. - person Vladimir F; 21.02.2017
comment
Хорошая точка зрения. Но, в таком случае, не могли бы вы привести ПРИМЕР, почему external подвержен ошибкам и что делать, если вам нужно вызвать большое количество подпрограмм? Придется ли вам писать много-много 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