可以在Fortran,C或Java代码中重新定义像sin()
这样的数学函数,同时保留其他数学函数(如cos()
)的默认行为吗?或者除了内置的sin()
之外,还可以定义另一个名为sin()
但接受不同参数类型的函数吗?我对这些语言的一般特性很感兴趣(我正在考虑像非通常数字代数的实现这样的应用程序)。
我尝试在Fortran 95程序中定义sin()
函数,但是调用了内部sin()
函数...有没有办法解决这个问题? C和Java怎么样?
PS :这是一个应用示例:您已经编写了一些计算代码;你意识到如果所有计算都能产生具有不确定性的数字(如3.14±0.01),那将是有用的。即使z = sin(x+2*y)
和x
是具有不确定性的数字(而不是浮点数),在代码中保持所有数学表达式(例如y
)都不会被修改是很方便的。这是为什么使用sin()
函数来概括通常的正弦函数是有用的。
为什么我还在问题中包含某些功能未被修改的条件的原因是,当sin(x)
具有不确定性时,计算x
上的不确定性的自定义库可能无法实现所有标准函数,所以仍然可以访问一些标准函数。
这些功能存在于Python中,这使得使用uncertainties Python模块(我写的)非常方便。我很想知道有多少这种便利是由于Python是它的语言,以及为C,Fortran或Java编写的类似不确定性计算库的方便程度。
答案 0 :(得分:7)
重新定义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发出警告:“在'(1)中声明的'sin'可能会影响同名的内在。为了调用内在的,可能需要明确的INTRINSIC声明。” ifort没有发出警告..
答案 1 :(得分:5)
在C中你可以使用预处理器:
#define sin mysin
然后,当你做
sin(x);
你实际上会打电话给
mysin(x);
您也可以绕过定义sin函数的#include
,然后您可以定义自己的函数。
在Java中,我相信只需在您自己的软件包中编写您自己的实现并通过导入您的软件包或限定调用来使用它:
mypackage.MyClass.sin(x);
答案 2 :(得分:2)
在C中,如果您不包含<math.h>
,则sin
可用作程序使用的符号 - 作为函数或您喜欢的任何其他内容。如果你确实包含它,那么sin
即使作为一个宏也被保留,虽然在实践中你可能会发现定义它来调用你自己的函数会起作用。
在Java中没有自由函数,因此没有函数sin
。 java.lang.Math.sin(double)
是标准库的一部分,java.lang.StrictMath.sin(double)
也是如此。无法将方法添加到现有类中,因此无法替换它。但是你可以给任何其他类编写一个名为sin
的方法。
我根本不认识Fortran。
通常的方法(没有更详细的说明你在这些语言中的标准实现的实际问题):调用你的函数。
在所有语言中,您的实现可以提供一种链接用户定义版本的部分或全部标准库的方法(如果您想要完成工作,可以用户定义整个实现)。如果您想重新实施sin
,这可能就是您的选择。在C中,您将编译要链接的库,并指定链接器选项以使用它:libm
与任何其他库非常相似。但您必须记住,如果选择,编译器可以在具有sin
d #include
的程序中专门处理<math.h>
。如果感觉像编译器允许编译器实现任何标准函数,那么对sin
的调用不能保证链接到库函数sin
,它可能改为使用内置的编译器。如果CPU有sin
指令,您甚至可能认为避免函数调用是个好主意。
在Java中,您可以在某些情况下指定一个类加载器来提供您的类的版本,但我不确定这是否适用于java.lang
中的类,因为这是一个神奇的命名空间。你总是可以在你的实现中徘徊(例如,安装了JRE),并牢记(1)sin
实现可以(应该?)使用本机方法实现; (2)你很容易破坏某些东西。
通常无法保证标准功能是否相互呼叫,如果这样做,则无法保证它们是否会调用标准版本或您的标准版本。在实践中,我认为其他标准函数不太可能调用sin
- 可能有sin
和cos
调用的内部辅助函数(在获取模数并应用相位角之后) ,tan
不会使用其中任何一个,因为tan(x) = sin(x) / cos(x)
不如您通过其他方式那样准确或快速。
答案 3 :(得分:2)
您无法替换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.
答案 4 :(得分:1)
您可能想要考虑更适合该任务的语言
如前面的答案中所述,您可以在某种程度上避免使用内置的sin
函数,但它不会覆盖。对于某些非常规数字代数(例如church numeral system),您可能需要考虑使用动态语言,例如LISP
或Scheme
。
答案 5 :(得分:1)
在C 重新定义中,从另一个编译单元链接的函数难以实现。这一切都归结为以下内容:编译程序时,每个导入或导出的符号都列在符号表中。您可以在GPL系统上查看此表,因此Linux objdump -T $EXECUTABLE_OR_BINARYOBJECT
,在Unix和Linux上nm $EXECUTABLE_OR_BINARYOBJECT
然后,链接器使用此表来标识要“粘合在一起”的部分。首先,它读取每个二进制文件和库提供的符号表,填写符号表,找到它们的位置,然后将它们与符号导入的空插槽链接(我强烈建议每个程序员读取链接器源代码,或者更好的是,写下自己的 - 这是高度教学的。)
标准库或 libm 特别提供符号sin
等。现在想象一个程序,其中一些编译单元也导出一个名为sin
的符号。链接器只有符号名称可供使用;如果一个符号恰好碰撞,那么发生的事情很大程度上取决于链接器,但可以肯定的是,它不太可能是你想要的。
C ++通过 mangling 符号来避免命名空间冲突。受损的名称包含有关类型和命名空间的信息。但是,如果在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编码器,所以我不能告诉你这个。
在Lisp和Scheme中,你可以做更酷的事情,而不仅仅是重新定义,但你所做的一切只有局部效果而没有副作用。在Haskell中它是相似的。