当我运行以下代码时:
program foo
implicit none
logical :: a(2)
a = [.true., .true.]
print *, 'a = ', a
call evaluate(a)
a = [.true., .false.]
print *, 'a = ', a
call evaluate(a)
a = [.false., .false.]
print *, 'a = ', a
call evaluate(a)
contains
subroutine evaluate(a)
logical, intent(in) :: a(2)
if (a(1) .eqv. .true. .and. a(2) .eqv. .true.) then
print *, 'TT'
elseif (a(1) .eqv. .true. .and. a(2) .eqv. .false.) then
print *, 'TF'
elseif (a(1) .eqv. .false. .and. a(2) .eqv. .false.) then
print *, 'FF'
endif
end subroutine evaluate
end program
我得到以下输出:
a = T T
TT
a = T F
TF
a = F F
TT
为什么子程序evaluate的最后一次调用给出了错误的输出(即,如果条件不是第三次,则匹配第一次(?该代码是使用命令gfortran -Wall -fcheck=all foo.f90
编译的。
您发现Fortran中逻辑运算符的优先级可能有点混乱。让我们稍微扩展一下你的程序,看看更奇怪的地方:
ijb@ijb-Latitude-5410:~/work/stack$ cat eqv_2.f90
program foo
implicit none
logical :: a(2)
a = [.true., .true.]
print *, 'a = ', a
call evaluate(a)
a = [.true., .false.]
print *, 'a = ', a
call evaluate(a)
a = [.false., .true.]
print *, 'a = ', a
call evaluate(a)
a = [.false., .false.]
print *, 'a = ', a
call evaluate(a)
contains
subroutine evaluate(a)
logical, intent(in) :: a(2)
if (a(1) .eqv. .true. .and. a(2) .eqv. .true.) then
print *, 'TT'
elseif (a(1) .eqv. .true. .and. a(2) .eqv. .false.) then
print *, 'TF'
elseif (a(1) .eqv. .false. .and. a(2) .eqv. .true.) then
print *, 'FT'
elseif (a(1) .eqv. .false. .and. a(2) .eqv. .false.) then
print *, 'FF'
endif
end subroutine evaluate
end program
ijb@ijb-Latitude-5410:~/work/stack$ gfortran -std=f2008 -Wall -Wextra -fcheck=all -g -O eqv_2.f90
ijb@ijb-Latitude-5410:~/work/stack$ ./a.out
a = T T
TT
a = T F
TF
a = F T
TF
a = F F
TT
嗯,所以不仅[假,假]是[真,真],[假,真]也是[真,假]!这怎么会发生在政治之外?
问题是.eqv.
算子的优先级低于.and.
算子的优先级,因此首先对.and.
进行求值。事实上,.eqv.
和.neqv.
的优先级是Fortran中所有非用户定义运算符中最低的,因此在任何只使用语言定义运算符的逻辑表达式中,它们将最后求值。这与我们将3 + 4 * 5 + 6
评估为3 + (4*5) + 6 = 29
和而不是(3+4) * (5+6) = 77
是一样的,因为*
的优先级高于+
。
所以您将.false. .eqv. .true. .and. .false. .eqv. .true.
评估为
.false. .eqv. (.true. .and. .false.) .eqv. .true. =
( .false. .eqv. .false. ) .eqv. .true. =
.true. .eqv. .true. =
.true.
这就是你看到的结果。正是出于这个原因,我强烈建议学生在长逻辑表达式中使用括号——如果我们在这里这样做,我们就会得到你所期望的:
ijb@ijb-Latitude-5410:~/work/stack$ cat eqv.f90
program foo
implicit none
logical :: a(2)
a = [.true., .true.]
print *, 'a = ', a
call evaluate(a)
a = [.true., .false.]
print *, 'a = ', a
call evaluate(a)
a = [.false., .false.]
print *, 'a = ', a
call evaluate(a)
contains
subroutine evaluate(a)
logical, intent(in) :: a(2)
if ( (a(1) .eqv. .true.) .and. (a(2) .eqv. .true.)) then
print *, 'TT'
elseif ((a(1) .eqv. .true.) .and. (a(2) .eqv. .false.)) then
print *, 'TF'
elseif ( (a(1) .eqv. .false.) .and. (a(2) .eqv. .false.)) then
print *, 'FF'
endif
end subroutine evaluate
end program
ijb@ijb-Latitude-5410:~/work/stack$ gfortran -std=f2008 -Wall -Wextra -fcheck=all -g -O eqv.f90
ijb@ijb-Latitude-5410:~/work/stack$ ./a.out
a = T T
TT
a = T F
TF
a = F F
FF
正如Martin在另一个答案中所解释的那样,很多这都是多余的。事实上,我认为像a .eqv. .true.
这样的表达式不是很好的风格,而且事实上我不记得上次在代码中使用.eqv.
或.neqv.
是什么时候了。
我不清楚为什么要检查a(1)
和a(2)
是true
或false
。根据定义,它们的值为true或false,因此可以直接使用,而无需将它们与true
或false
:进行比较
if (a(1) .and. a(2)) then
print *, 'TT'
elseif (a(1) .neqv. a(2)) then
print *, 'TF'
elseif (a(1) .eqv. a(2)) then
print *, 'FF'
endif
案例包括:
- 两个值均为
true
=a(1).and.a(2)
=TT
- 值不相等=
a(1).neqv.a(2)
=TF
- 值等于=
a(1).eqv.a(2)
=FF
在第三种情况下,它们必须是FF
,因为实际上已经涵盖了所有其他情况,所以else
而不是elseif
就足够了。但是,为了清楚起见,我保留了原来的代码。
输出:
a = T T
TT
a = T F
TF
a = F F
FF