Можно ли переопределить функции типа sin () в Fortran, C или Java? - PullRequest
4 голосов
/ 14 ноября 2011

Можно ли переопределить математическую функцию, такую ​​как sin(), в коде Fortran, C или Java, сохранив поведение по умолчанию других математических функций, таких как cos()?Или может быть определена другая функция с именем sin(), но принимающая различные типы аргументов в дополнение к встроенной sin()?Меня интересуют общие черты этих языков (я имею в виду такие приложения, как реализация необычных алгебр чисел).

Я пытался определить функцию sin() в программе на Фортране 95, но внутреннююsin() функция была вызвана вместо ... Есть ли способ обойти это?как насчет C и Java?

PS : Вот пример приложения: у вас уже есть какой-то код расчета;Вы понимаете, что было бы полезно, если бы все вычисления могли привести к числам с неопределенностью (например, 3,14 ± 0,01).Было бы удобно сохранить все математические выражения (например, z = sin(x+2*y)) неизмененными в коде, даже если x и y являются числами с неопределенностью (вместо числа с плавающей запятой).Это пример того, почему полезно иметь sin() функцию, которая обобщает обычную функцию синуса.

Причина, по которой я также включил в вопрос условие, что определенные функции не должны быть изменены, заключается в том, что пользовательская библиотека, котораявычисляет неопределенность на sin(x), когда x имеет неопределенность, может не реализовывать все стандартные функции, поэтому было бы неплохо иметь возможность иметь доступ к некоторым стандартным функциям.

Эти функции присутствуют вPython, и это делает использование неопределенности модуля Python (который я написал) весьма удобным.Мне было бы интересно узнать, насколько это удобство объясняется тем, что Python является языком, и насколько удобной может быть подобная библиотека для вычисления неопределенности, написанная для C, Fortran или Java.

Ответы [ 6 ]

7 голосов
/ 14 ноября 2011

Нет проблем с переопределением встроенного в Fortran. Это не повлияет на другие свойства, но, конечно, у вас не будет доступа к тому, что вы «затеняли». Пример кода:

module my_subs

contains

function sin (x)
real :: sin
real, intent (in) :: x

sin = x**2

return
end function sin

end module my_subs

program test_sin

use my_subs

implicit none

write (*, *) sin (2.0)

stop

end program test_sin

Выход составляет 4,0

gfortran выдает предупреждение: «sin», объявленный в (1), может скрывать внутреннюю часть того же имени. Чтобы вызвать внутреннюю, могут потребоваться явные декларации INTRINSIC. ifort не выдает предупреждение ..

5 голосов
/ 14 ноября 2011

В C вы можете использовать препроцессор:

#define sin mysin

Затем, когда вы выполните

sin(x);

, вы на самом деле позвоните

mysin(x);

Вы также можетеобойти #include, где определена функция sin, и тогда вы можете определить свою собственную.

В Java, я считаю, достаточно написать свою собственную реализацию в вашем собственном пакете и использовать ее либо путем импорта пакетаили уточняющий звонок:

mypackage.MyClass.sin(x);
2 голосов
/ 14 ноября 2011

Вы не можете заменить Math.sin(double), но вы можете сделать MyMath.sin(double), который вы можете использовать вместо.

import static mypackage.MyMath.sin;
import static java.lang.Math.cos;

// later
double s = sin(d); // uses MyMath.
double c = cos(d); // uses Math.
2 голосов
/ 14 ноября 2011

В C, если вы не включите <math.h>, тогда sin доступно как символ для использования программой - как функция или что-либо еще, что вам нравится.Если вы включите его, то sin зарезервировано даже как макрос, хотя на практике вы, вероятно, обнаружите, что определение его для вызова вашей собственной функции будет работать.

В Java нет свободных функций,следовательно, нет функции sin.java.lang.Math.sin(double) является частью стандартных библиотек, как и java.lang.StrictMath.sin(double).Нет способа добавить метод в существующий класс, поэтому вы не можете его заменить.Но вы можете дать любому другому классу написать метод с именем sin.

. Я совсем не знаю Фортран.

Как обычно (без каких-либо подробностейнастоящая проблема связана со стандартными реализациями на этих языках): вызывайте свою функцию как-нибудь иначе.

На всех языках ваша реализация может обеспечить способ связывания с определенной пользователем версией части или всех стандартных библиотек.(черт возьми, вы можете определить пользователя для всей реализации, если вы хотите сделать работу).Скорее всего, так будет, если вы захотите повторно внедрить sin.В C вы должны скомпилировать библиотеку для ссылки и указать параметры компоновщика, чтобы использовать ее: libm очень похоже на любую другую библиотеку.Но вы должны иметь в виду, что компилятору разрешено, если он решит, обрабатывать sin, особенно в программах, которые имеют #include d <math.h>.Компилятору разрешается реализовывать любую стандартную функцию самостоятельно, если она на это похожа, поэтому вызов sin не гарантирует связывания с библиотечной функцией sin, вместо этого он может использовать встроенную компилятор.Если у процессора есть инструкция sin, вы можете даже подумать, что это хорошая идея - избегать вызова функции.

В Java вы можете в некоторых случаях указать загрузчик классов для предоставления вашей версии класса, но я не уверен, будет ли это работать с классами в java.lang, поскольку это волшебное пространство имен.Вы всегда можете покопаться в своей реализации (например, в установленной JRE) и иметь в виду (1), что реализация sin может (должна?) Быть реализована с использованием нативных методов;(2) вы можете легко что-то сломать.

Как правило, нет никаких гарантий относительно того, будут ли стандартные функции вызывать друг друга или нет, и если они это сделают, то нет никаких гарантий, будут ли они вызывать стандартную версию или вашу.На практике я думаю, что маловероятно, что другая стандартная функция вызовет sin - вероятно, есть внутренняя вспомогательная функция, вызываемая как sin, так и cos (после взятия модуля и применения фазового угла), и tan будетне используйте ни один из них, потому что tan(x) = sin(x) / cos(x) не так точен или не так быстр, как вы можете сделать с помощью других средств.

1 голос
/ 16 ноября 2011

В C переопределение функция, связанная с другим модулем компиляции, между трудной или невозможной. Все сводится к следующему: при компиляции программы каждый импортированный или экспортированный символ указывается в таблице символов. Вы можете посмотреть на эту таблицу, используя системы GPL, поэтому Linux objdump -T $EXECUTABLE_OR_BINARYOBJECT, Unix и Linux nm $EXECUTABLE_OR_BINARYOBJECT

Затем компоновщик использует эту таблицу для идентификации частей, которые должны быть "склеены". Сначала он читает в таблице символов каждого предоставленного двоичного файла и библиотеки, заполняет таблицу символов тем, где их найти, а затем связывает их с пустыми ячейками импорта символов (поэтому я настоятельно рекомендую каждому программисту прочитать исходный код компоновщика, или еще лучше, напиши свое - это очень поучительно).

Стандартная библиотека, или libm специально предоставляет символ sin, среди прочего. Теперь представьте программу, в которой какой-то модуль компиляции также экспортирует символ с именем sin. У компоновщика есть только имена символов для работы; если символ сталкивается, то, что происходит, сильно зависит от компоновщика, но в чем можно быть уверенным, так это в том, что он вряд ли будет тем, что требуется.

C ++ позволяет избежать коллизий пространства имен путем искажения символов. Искаженное имя содержит информацию о типе и пространстве имен. Однако если в программе на C ++ два символа с одним и тем же именем и типом, без или внутри одного и того же пространства имен, определены в нескольких единицах компиляции, существует одна и та же проблема.

Fortran даже старше, чем C. Как и в C, нет пространств имен, и опять же, для ссылок используются только имена символов. Так что проблема та же, по крайней мере для Fortran77.

Objective-C - это технически C с системой объектов, основанной на сообщениях; одни и те же правила связи. Objective-C ++ относится к Objective-C так же, как C ++ - к C.

В Java каждый символ существует в своем классе и, таким образом, окружен пространством имен. Поскольку классы связаны с модулями компиляции, это также предотвращает конфликты пространства имен, естественно. Но это также делает практически невозможным переопределение вещей в Java простым способом (хотя можно делать сумасшедшие вещи на уровне байт-кода).

В Python внешние функции доступны с помощью модулей. Если вы пишете import spamneggs, то на самом деле происходит то, что вызывается специальная функция __import__, а результат сохраняется как переменная с именем модуля. Тип этой переменной module, но технически это просто ссылка на прославленный словарь, похожий на классы. Если вы напишите

import math

origsin = math.sin()
def mysin(v):
    return origsin(v)

math.sin = mysin

в результате вы берете копию оригинала math.sin и перезаписываете math.sin переопределением. Поскольку экземпляры модуля являются общими для интерпретатора, если только не используется песочница, это переопределяет sin прозрачным, неразрывным способом для всей программы. Это позволяет выбрать: либо переопределить всю программу, либо использовать «песочницу» для сохранения локальности.

Я не такой опытный кодировщик Ruby, поэтому не могу рассказать вам об этом.

В Лиспе и Схеме вы можете делать намного круче, чем просто переопределение, но все, что вы делаете, имеет только локальные эффекты без побочных эффектов. В Хаскеле это похоже.

1 голос
/ 14 ноября 2011

Возможно, вы захотите рассмотреть язык, который лучше подходит для этой задачи.
Как указывалось в предыдущих ответах, вы можете несколько избежать использования встроенной функции sin, но она не переопределяет.Для некоторой необычной алгебры чисел (например, церковная система счисления ) вы можете подумать об использовании динамического языка, такого как LISP или Scheme.

...