-1

我正在阅读化学符号列表。由于有 118 个元素,一个select case构造将有 119 个案例。有一个更好的方法吗?有些元素以相同的字母开头,例如C, Ca, Cd, Co,因此也许读取三个A1变量而不是一个A3变量可以在一定程度上简化该过程。

program case_test
implicit none

character(len=3)   :: input
integer            :: i
real               :: mass

write(*,*) "Give me a symbol"

read(*,"(A3)") input

select case (input)
  case("H")
    mass = 1.008
  case("He")
    mass = 4.003
! 116 other checks
  case default
    write(*,*) "Unknown element ", input
    stop
end select

write(*,*) "atom mass = ", mass
end program case_test
4

2 回答 2

2

有一小部分有限的元素符号(我相信最后计数为 118 个)可以放入不太长的文本字符串中。Fortran 不是文本处理的最佳语言(我会让 Perl 和 SNOBOL 为之争辩……)但现代 Fortran 已经稍微改善了一些问题。

以下代码中有一些假设。首先,我们希望用户使用大小写混合输入元素符号。您可以更正大小写,但对于此示例,如果用户输入中的第一个字符不是大写并且第二个字符不是空格或小写,我决定只抛出错误。这是使用verify在 Fortran 95 中添加到语言中的内在函数来完成的(这实际上是我第一次使用它)。

index()内在函数将为您提供从 1 开始计数的较大字符串中子字符串的第一个匹配项的起始位置,如果未找到匹配项,则返回 0 。

elblob字符串包含由下划线分隔的每个元素符号。单个字母元素保留尾随空格以匹配character(len=2)变量。前面有两个星号,elblob因此每个元素都以可被三整除的字符开头。这是一个愚蠢的魔法,它利用了我们对原子序数的了解——它们是唯一的、连续的整数,完全填充了从 1 到 118 的范围(或者这些天的顶级元素)。

另一个可能解决非问题的偷偷摸摸的位是使用adjustl()确保第一个字符elseek不是空格。这可能不仅仅是因为 Fortran 的read()工作方式,但我很偏执,所以我把它放在那里。它会做的最糟糕的事情是烧几个周期什么都不做。把它拿出来看看会发生什么。

通过对用户输入进行完整性检查以排除杂散的 '_' 和 '*',我们可以确保元素符号将正确匹配,并且我们可以通过将返回的匹配位置除以index()3 来获得真正的原子序数。Carbon 不会意外匹配钙,因为 carbon 的搜索字符串是“C”,而不是“C”,这是 Fortran 固定长度字符串的效果。如果elseek定义为character(len=:), allocatable,我们可能会遇到问题,但通过使用愚蠢的旧固定长度空格填充字符串,我们可以利用它们愚蠢的旧行为来发挥我们的优势。

!> Return an element's atomic number based on its symbol.
program elements
    use iso_fortran_env, only: input_unit, output_unit
    implicit none

    character(len=*), parameter :: alpha_u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    character(len=*), parameter :: alpha_l = 'abcdefghijklmnopqrstuvwxyz'

    character(len=*), parameter :: elblob =                             &
    '**H _He_Li_Be_B _C _N _O _F _Ne_Na_Mg_Al_Si_P _S _Cl_Ar_K _Ca_Sc_' &
 // 'Ti_V _Cr_Mn_Fe_Co_Ni_Cu_Zn_Ga_Ge_As_Se_Br_Kr_Rb_Sr_Y _Zr_Nb_Mo_'   &
 // 'Tc_Ru_Rh_Pd_Ag_Cd_In_Sn_Sb_Te_I _Xe_Cs_Ba_La_Ce_Pr_Nd_Pm_Sm_Eu_'   &
 // 'Gd_Tb_Dy_Ho_Er_Tm_Yb_Lu_Hf_Ta_W _Re_Os_Ir_Pt_Au_Hg_Tl_Pb_Bi_Po_'   &
 // 'At_Rn_Fr_Ra_Ac_Th_Pa_U _Np_Pu_Am_Cm_Bk_Cf_Es_Fm_Md_No_Lr_Rf_Db_'   &
 // 'Sg_Bh_Hs_Mt_Ds_Rg_Cn_Nh_Fl_Mc_Lv_Ts_Og'

    character(len=2) :: elseek
    character(len=1) :: c
    integer :: atomic_number

404 format("Sorry, I couldn't find ", '"', A, '"')
200 format("The element ", A, " has an atomic number of ", I0)
500 format('"', A, '" must be ', A, ' case letter')

    continue

    write(output_unit, '(A)') "Give me an element's symbol "            &
        // "(like H or Na)"

    read(input_unit, '(A2)') elseek

    ! Left-justify; eliminates any leading space
    ! (Q: is leading space even possible?)
    elseek = adjustl(elseek)

    c = elseek(1:1)
    if (verify(c, alpha_u) > 0) then
        write(output_unit, 500) c, 'an upper'
        stop(1)
    end if

    c = elseek(2:2)
    if (verify(c, alpha_l // ' ') > 0) then
        write(output_unit, 500) c, 'a lower'
        stop(2)
    end if

    atomic_number = index(elblob, elseek)
    if (atomic_number < 1) then
        write(output_unit, 404) elseek
    else
        atomic_number = atomic_number / 3
        write(output_unit, 200) elseek, atomic_number
    end if

end program elements

无论如何,我测试了 30 秒。不要将它用于任何对安全至关重要的事情。如果这是为了家庭作业,请至少阅读并理解代码并将其重写为您自己的代码,这样您就不会被 Turnitin 标记。

这不是最强大的解决方案,但它简短而简单,并且符合所写的要求。它不需要哈希表或搜索树或任何比字符串更复杂的数据结构。在 FORTRAN77 下工作并不需要太多,但那样就是疯狂......

于 2017-07-21T03:46:22.567 回答
2

一般来说,我喜欢 SELECTED CASE,但这似乎更直接......

MODULE Element_Definintions
IMPLICIT NONE

PUBLIC

INTEGER, PARAMETER, PUBLIC :: Max_Elements = 118

TYPE Elements_Type
  character(len=3)   :: Name
  integer            :: i
  real               :: mass
END TYPE Elements_Type

TYPE(Elements_Type), DIMENSION(Max_Elements), PUBLIC :: Elements

CONTAINS

SUBROUTINE Init_Elements
IMPLICIT NONE

Element(1).Name = "H"
Element(1).Num  = 1
Element(1).Mass = 1.008

Element(2).Name = "He"
Element(2).Num  = 2
Element(2).Mass = 4.008

!...
Element(118).Name = ""

RETURN
END SUBROUTINE Init_Elements

END MODULE Element_Definintions

那么程序...

program case_test
USE Element_Definintions
implicit none
character(len=3) :: input
LOGICAL          :: Found = .FALSE.

CALL Init_Elements()

write(*,*) "Give me a symbol"

read(*,"(A3)") input

DO I = 1, Max_Elements
  IF(Input(1:LEN_TRIM(Input)) == Element(I).Name(1:LEN_TRIM(Element(I).Name)) THEN
    FOUND= .TRUE.
    EXIT
  ELSE
    CYCLE
  ENDIF
ENDDO

IF(Found) THEN
  write(*,*) 'atom mass of "',Element(I).Name,'" = ', mass
ELSE
  write(*,*) 'Unknown element "', input,'"'
ENDIF

END program case_test
于 2017-07-21T00:24:48.150 回答