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_
New Contributor I
658 Views

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 Kudos
15 Replies
Steve_Lionel
Honored Contributor III
658 Views

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 Kudos
Kevin_D_Intel
Employee
658 Views

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 Kudos
jimdempseyatthecove
Honored Contributor III
658 Views

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 Kudos
Blane_J_
New Contributor I
658 Views
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 Kudos
GVautier
New Contributor II
658 Views

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 Kudos
Steve_Lionel
Honored Contributor III
658 Views

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

0 Kudos
FortranFan
Honored Contributor II
658 Views

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 Kudos
GVautier
New Contributor II
658 Views

Effectively,

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

will do the job.

0 Kudos
Blane_J_
New Contributor I
658 Views

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 Kudos
FortranFan
Honored Contributor II
658 Views

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 Kudos
FortranFan
Honored Contributor II
658 Views

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 Kudos
GVautier
New Contributor II
658 Views

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 Kudos
andrew_4619
Honored Contributor II
658 Views

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 Kudos
jimdempseyatthecove
Honored Contributor III
658 Views

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 Kudos
Blane_J_
New Contributor I
658 Views

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 Kudos
Reply