Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

if condition problem

Blane_J_
Nouveau contributeur I
2 549 Visites

Since I didn't find any part of the standard stating out sequence of judging parallel conditions, I would like to ask if anyone has even met the situation like:

function Test(str)
    implicit none

    character(*), intent(in) :: str

    if(LEN(str) >=2 .AND. str(2:2) == 's')then
        ! do something...
    end if

end function Test

In the if statement above, there are two conditions. If the dummy argument str we passed in was just a single character assume 'a', there is going to be an error with ivf compiler as condition str(2:2)=='s' is referring to index number 2 of 'a' which simply doesn't exist. So in Fortran, is it true that conditions within a judging statement are all be judged every time of execution ?  In other words no left-to-right judging sequence ? About this case I See difference in C# while since condition LEN(str)>=2 is false, the if block will be skipped and, no matter true or false the second condition is, it is not ever judged so it's legal to write these conditions together, but not in Fortran. Appreciate any explanation.

0 Compliments
15 Réponses
Steve_Lionel
Contributeur émérite III
2 549 Visites

Fortran and C/C++/C# have different rules for expression evaluation. Fortran allows the compiler to evaluate any logically equivalent expression to any degree of completeness. There is no strict left-to-right nor short-circuiting. It's a classic error Fortran programmers have made for decades - sometimes it works the way they want and they think that's what the language says, and then they change compilers or even versions and the order changes.

The correct solution is to use nested IF-THEN.

0 Compliments
Kevin_D_Intel
Employé
2 549 Visites

This goes to the often discussed topic of "short-circuit evaluation". There are a number of posts in our Fortran forums discussing what compilers are permitted to do within the Standard.

To your questions, no not all conditions are judged at execution and no left-to-right evaluation is guaranteed. Refer to Dr. Fortran and "The Dog That Did Not Bark"

Here's a couple of other posts that offer related discussion and details.

https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/499227
https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/282534

(Posting although I see the Doctor himself has replied - Thank you Steve!)

0 Compliments
jimdempseyatthecove
Contributeur émérite III
2 549 Visites

If you do not like (want) nested IF-THEN blocks, then write an Fortran acceptable IF statement

if(LEN(str) >=2 .AND. str(MIN(LEN(str),2):MIN(LEN(str),2)) == 's') then

But then I think you will agree that nested IF-THEN blocks will be clearer (and faster).

Jim Dempsey
 

0 Compliments
Blane_J_
Nouveau contributeur I
2 549 Visites
Thanks for all your explanations. To my opition large amount of nested IF-THENs are really boring than the combined ones. But for reliability, I should always take the advice anyway.
0 Compliments
GVautier
Nouveau contributeur III
2 549 Visites

A solution is to create a specific function that will make the code clearer. For example something like that:

logical*4 function test_2nd_char(str,char)
    character(*), intent(in) :: str,char

    test_2nd_char=.false.
    if (len(str).le.2) return
    if (str(2:2).ne.char(1:1)) return
    test_2nd_char=.true.
end function

if (test_2nd_char(str,"s")) .....

Or more generic :

logical*4 function match(str,pattern)
    character(*), intent(in) :: str,pattern

    match=.false.
    if (len(str).lt.len(pattern)) return
    do i=1,len(pattern)
         if (pattern(i:i).ne."*".and.str(i:i).ne.pattern(i:i)) return
    enddo
    match=.true.
end function

if (match(str,"*s")) .....

 

0 Compliments
Steve_Lionel
Contributeur émérite III
2 549 Visites

Please don't use nonstandard syntax such as "logical*4". Just "logical" would work fine here.

0 Compliments
FortranFan
Contributeur émérite III
2 549 Visites

Steve Lionel (Ret.) wrote:

Please don't use nonstandard syntax such as "logical*4". Just "logical" would work fine here.

Indeed; I would also suggest anyone considering any such "special" functions should first review what the Fortran standard offers as intrinsic procedures; INDEX, SCAN, VERIFY in this instance.

0 Compliments
GVautier
Nouveau contributeur III
2 549 Visites

Effectively,

if (index(str,"s").eq.2) ...

will do the job.

0 Compliments
Blane_J_
Nouveau contributeur I
2 549 Visites

Vautier, Gilles wrote:

Effectively,

if (index(str,"s").eq.2) ...

will do the job.

That's a very good solution on this case, thanks a lot.

0 Compliments
FortranFan
Contributeur émérite III
2 549 Visites

Vautier, Gilles wrote:

Effectively,

if (index(str,"s").eq.2) ...

will do the job.

@Vautier, Gilles,

"will do the job" - how?  What if str is "ss"?

0 Compliments
FortranFan
Contributeur émérite III
2 549 Visites

Blane J. wrote:

Quote:

Vautier, Gilles wrote:

 

Effectively,

if (index(str,"s").eq.2) ...

will do the job.

 

 

That's a very good solution on this case, thanks a lot.

@Blane J.,

Did you check how your expected IF construct would function if you followed Steve's correct advice in Message #2 versus what you would get if you follow this latest one with the INDEX intrinsic?  What should happen if str equals "ss"?

0 Compliments
GVautier
Nouveau contributeur III
2 549 Visites

FortranFan wrote:

  What should happen if str equals "ss"?

So the best solution is to write a user function.as I show in a precedent post.

0 Compliments
andrew_4619
Contributeur émérite III
2 549 Visites

LOL. Like many things in programming there are many possible solutions, in this case the 'best' solution probably depends if this test is done in lots of places or just one, in the former case my personal preference would be a user function (less code easier to maintain), in the latter case a nest IF as it is more immediately apparent what the program logic is without having to find a and look at a function.

0 Compliments
jimdempseyatthecove
Contributeur émérite III
2 549 Visites

or...

module mod_test
    contains
    logical function Test(str)
        implicit none
        character(*), intent(in) :: str
        Test = .false. ; if(LEN(str) >=2) Test = (str(2:2) == 's')
    end function Test
end module mod_test
    
program ifif
    use mod_test
    implicit none
    print *, Test('x')
    print *, Test('xx')
    print *, Test('xs')
end program ifif

Jim Dempsey

0 Compliments
Blane_J_
Nouveau contributeur I
2 549 Visites

FortranFan wrote:

@Blane J.,

Did you check how your expected IF construct would function if you followed Steve's correct advice in Message #2 versus what you would get if you follow this latest one with the INDEX intrinsic?  What should happen if str equals "ss"?

Oops, that's a problem. so as suggested, a user function to handle the evaluation process should be the best anyway, thanks all again above.

0 Compliments
Répondre