Numerical expression in I/O

2019-09-11 14:15发布

问题:

I am looking for a way to read numerical expressions in Fortran.

With numerical expression I mean dsqrt(0.5d0)/3.d0+1.d0 or something rather then the translated 1.235... real version.

With reading I mean

open(unit=UnitNumber,file="FileName") read(UnitNumber, *) ....

I try to define in the reading statement the format, for instanceread(unitNumber,"(15F24.17)") but it does not help. I am

I am wondering if I can do it only internally, defining real(8), parameter :: dsqrt(0.5d0)/3.d0+1.d0 .

Maybe the use of FORMAT syntax could help?

回答1:

As suggested by @agentp, interpreted languages like Python and Julia can parse a string directly as a piece of code, so utilizing such a feature may be convenient for your purpose. But if you definitely need to achieve the same goal in Fortran, another approach (with least effort!) may be simply to call eval() in such languages, for example:

module util
    use iso_fortran_env, only: dp => real64
    implicit none
contains

subroutine eval( expr, ans, x, y )
    character(*), intent(in)       :: expr
    real(dp), intent(out)          :: ans
    real(dp), intent(in), optional :: x, y
    character(len(expr)+200) cmd, sx, sy
    integer u

    sx = "" ; sy = ""
    if ( present(x) ) write( sx, "(' x = ', es25.15, ' ; ')" ) x
    if ( present(y) ) write( sy, "(' y = ', es25.15, ' ; ')" ) y

    write( cmd, "(a)" ) &
            "python -c ""from __future__ import print_function, division ; " // &
            "from math import * ; " // trim(sx) // trim(sy) // &
            "print( eval( '" // trim(expr) // "' ))"" > tmp.dat"

    call system( trim( cmd ) )

    open( newunit=u, file="tmp.dat", status="old" )
    read( u, * ) ans
    close( u )

    call system( "rm -f tmp.dat" )
end subroutine

end module

program main
    use util, only: dp, eval
    implicit none
    character(200) str
    real(dp) ans

    str = "sqrt( 2.0 ) + 1000.0"
    call eval( str, ans )
    print *, "ans = ", ans

    str = "acos( x ) + 2000.0"
    call eval( str, ans, x= -1.0_dp )
    print *, "ans = ", ans

    str = "10 * x + y"
    call eval( str, ans, x= 1.0_dp, y= 2.0_dp )
    print *, "ans = ", ans
end program

Results:

$ gfortran test.f90    # gfortran >=5 is recommended
$ ./a.out
 ans =    1001.4142135623731
 ans =    2003.1415926535899
 ans =    12.000000000000000

More specifically, the above code simply invokes the built-in eval() function in Python via system(). But it is not very efficient because the resulting value is once written to an external file (and also the overhead to call Python itself). So if efficiency matters, it may be better to use more specific 3rd-party libraries, or for handiness, work with interpreted languages directly. (I suggest the latter approach if the calculation is not too demanding, because it saves much time for coding...)

Python:

from __future__ import print_function, division
from math import *
str = input( "Input an expression: " )
x = 1.0
y = 2.0
print( eval( str ) )   # if you type "x + 10 * y" in the prompt, you get 21

Julia:

println( "Input an expression: " )
str = readline()
x = 1.0
y = 2.0
println( eval( parse( str ) ) )

[ EDIT ]

If it is OK to use system() and write external files, another option may be to simply write a small Fortran code that contains the expression to be evaluated, compile and run it via system(), get the result via an external file. For example, if we replace the two lines in the above code (write( cmd, "(a)" ) ... and system( trim( cmd ) )) by the following, it gives the same result. This might be useful if we want to keep the code entirely written in Fortran, with minimal effort for modification.

    open( newunit=u, file="tmp.f90" )
    write( u, "(a)" ) "implicit none"
    write( u, "(a)" ) "real :: x, y"
    write( u, "(a)" ) trim(sx)
    write( u, "(a)" ) trim(sy)
    write( u, "(a)" ) "write(*,'(e30.20)') " // trim(expr)
    write( u, "(a)" ) "end"
    close( u )

    call system( "gfortran -fdefault-real-8 -ffree-line-length-none tmp.f90 && ./a.out > tmp.dat" )
         ! Assuming bash on Linux or Mac (x86_64).
         ! -fdefault-real-8 is attached to promote 1.0 etc to 8-byte floating-point values.

    call system( "rm -f tmp.f90" )


回答2:

For the record, the library fparser allows you to do precisely what you asked for: evaluate a string as a mathematical expression within Fortran, without requiring any other programming languages.



回答3:

No, there is nothing like this built in Fortran or any related programming languages. There are specific libraries for similar purposes (not necessarily too many in Fortran).

It is not clear at all to me why do you want that and how do you intend to use such an expression. It would have to be some specific type and you would need specific subroutines to evaluate such an expression.



标签: io fortran