从循环体重构多个 goto 语句
Refactor multiple goto statements from the loop body
我正在努力将以下 Fortran 77 子例程复制到 C# 方法中。主要问题是我试图摆脱 fortran 的 goto
语句。
fortran 到 C# 的转换看起来不错,但 goto
语句仍然存在。有没有一种方法可以将它们全部替换为条件语句或其他一些方法?谢谢。
这是 fortran 子例程:
C PROGRAM TEMP
subroutine TEMP (acl,adu,aeff,c,cair,cb,cbare,
+ cclo,count1,csum,di,ed,emcl,emsk,enbal,
+ enbal2,ere,erel,esw,eswdif,eswphy,eswpot,
+ evap,facl,fcl,fec,feff,food,h,hc,he,ht,htcl,icl,j,
+ mbody,p,po,r1,r2,rbare,rcl,
+ rclo,rclo2,rdcl,rdsk,rob,rsum,sex,sigm,sw,swf,swm,
+ ta,tbody,tcl,tcore,tmrt,tsk,v,vb,vb1,vb2,
+ vpa,vpts,wetsk,wd,wr,ws,wsum,xx)
real acl,adu,aeff,c(0:10),cair,cb,cbare,
+ cclo,csum,di,ed,emcl,emsk,enbal,
+ enbal2,ere,erel,esw,eswdif,eswphy,eswpot,
+ evap,facl,fcl,fec,feff,food,h,hc,he,ht,htcl,icl,
+ mbody,p,po,r1,r2,rbare,rcl,
+ rclo,rclo2,rdcl,rdsk,rob,rsum,sigm,sw,swf,swm,
+ ta,tbody,tcl,tcore(1:7),tmrt,tsk,v,vb,vb1,vb2,
+ vpa,vpts,wetsk,wd,wr,ws,wsum,xx
integer count1,count3,j,sex
wetsk = 0.
adu = 0.203 * mbody ** 0.425 * ht ** 0.725
hc = 2.67 + ( 6.5 * v ** 0.67)
hc = hc * (p /po) ** 0.55
feff = 0.725
C
facl = (- 2.36 + 173.51 * icl - 100.76 * icl * icl + 19.28
+ * (icl ** 3.)) / 100.
C
if (facl .gt.1.) facl = 1.
rcl = (icl/6.45)/facl
if (icl.ge.2.) y = 1.
if ((icl .gt. 0.6) .and. (icl .lt. 2.)) y = (ht - 0.2) / ht
if ((icl .le. 0.6) .and. (icl .gt. 0.3)) y = 0.5
if ((icl .le. 0.3) .and. (icl .gt. 0.)) y = 0.1
r2 = adu * (fcl - 1. + facl) / (2. * 3.14 * ht * y)
r1 = facl * adu / (2. * 3.14 * ht * y)
di = r2 - r1
C TEMPERATURE
do 90 j = 1,7
tsk = 34.
count1 = 0
tcl = (ta + tmrt + tsk) / 3.
count3 = 1
enbal2 = 0.
20 acl = adu * facl + adu * (fcl - 1.)
rclo2 = emcl*sigm *((tcl+273.2)** 4.-(tmrt+273.2)** 4.)*feff
htcl = 6.28 * ht * y * di / (rcl * alog(r2/r1) * acl)
tsk = 1. / htcl * (hc * (tcl - ta) + rclo2) + tcl
C RADIATION
aeff = adu * feff
rbare = aeff * (1.-facl) * emsk * sigm *
+ ((tmrt + 273.2) ** 4. - (tsk + 273.2) ** 4.)
rclo = feff * acl * emcl * sigm *
+ ((tmrt + 273.2) ** 4. - (tcl + 273.2) ** 4.)
rsum = rbare + rclo
C CONVECT
cbare = hc * (ta - tsk) * adu * (1. - facl)
cclo = hc * (ta - tcl ) * acl
csum = cbare + cclo
C CORE
c(0) = h + ere
c(1) = adu * rob * cb
c(2) = 18. - 0.5 * tsk
c(3) = 5.28 * adu * c(2)
c(4) = 0.0208 * c(1)
c(5) = 0.76075 * c(1)
c(6) = c(3) - c(5) - tsk * c(4)
c(7) = - c(0) * c(2) - tsk * c(3) + tsk * c(5)
c(8) = c(6) * c(6) - 4. * c(4) * c(7)
c(9) = 5.28 * adu - c(5) - c(4) * tsk
c(10) = c(9) * c(9) - 4. * c(4) *
+ (c(5) * tsk - c(0) - 5.28 * adu * tsk)
C
if (tsk.eq.36.) tsk=36.01
tcore(7) = c(0) / (5.28 * adu + c(1) * 6.3 / 3600.) + tsk
tcore(3) = c(0) / (5.28 * adu + (c(1) * 6.3 / 3600.) /
+ (1 + 0.5 * (34. -tsk))) + tsk
if (c(10) .lt. 0.) goto 22
tcore(6) = (- c(9) - c(10) ** 0.5) / (2. * c(4))
tcore(1) = (- c(9) + c(10) ** 0.5) / (2. * c(4))
22 if (c(8) .lt. 0.) goto 24
tcore(2) = (- c(6) + abs(c(8)) ** 0.5) / (2. * c(4))
tcore(5) = (- c(6) - abs(c(8)) ** 0.5) / (2. * c(4))
24 tcore(4) = c(0) / (5.28 * adu + c(1) * 1. / 40.) + tsk
C TRANSPARENCE
tbody = 0.1 * tsk + 0.9 * tcore (j)
swm = 304.94 * (tbody - 36.6) * adu / 3600000.
vpts = 6.11 * 10. ** (7.45 * tsk / (235. + tsk))
if (tbody .le. 36.6) swm = 0.
swf = 0.7 * swm
if(sex .eq. 1) sw = swm
if(sex .eq. 2) sw = swf
eswphy = - sw * evap
he = 0.633 * hc / (p * cair)
fec = 1. / (1. + 0.92 * hc * rcl)
eswpot = he * (vpa - vpts) * adu * evap * fec
wetsk = eswphy / eswpot
if (wetsk .gt. 1.) wetsk = 1.
eswdif = eswphy - eswpot
if (eswdif .le. 0.) esw = eswpot
if (eswdif .gt. 0.) esw = eswphy
if (esw .gt. 0.) esw = 0.
C DIFFERENCE
rdsk = 0.79 * 10. ** 7.
rdcl = 0.
ed = evap / (rdsk + rdcl) * adu * (1 - wetsk) * (vpa-vpts)
C VB
vb1 = 34. - tsk
vb2 = tcore(j) - 36.6
if (vb2 .lt.0.) vb2 = 0.
if (vb1 .lt.0.) vb1 = 0.
vb = (6.3 + 75. * (vb2)) / (1. + 0.5 * vb1)
C BALANCE
enbal = h + ed + ere + esw + csum + rsum + food
C COVER
if (count1 .eq.0) xx = 1.
if (count1 .eq.1) xx = 0.1
if (count1 .eq.2) xx = 0.01
if (count1 .eq.3) xx = 0.001
if (enbal .gt. 0.) tcl = tcl + xx
if (enbal .lt. 0.) tcl = tcl - xx
if ((enbal .le. 0.) .and. (enbal2 .gt. 0.)) goto 30
if ((enbal .ge. 0.) .and. (enbal2 .lt. 0.)) goto 30
enbal2 = enbal
count3 = count3 + 1
C
if (count3 .gt. 200) goto 30
goto 20
30 if ((count1 .eq.0.).or.(count1.eq.1.).or.(count1.eq.2.)) then
count1 = count1 + 1.
enbal2 = 0.
goto 20
end if
C
if (count1 .eq. 3.) then
C
if ((j .eq. 2) .or. (j .eq. 5)) goto 40
if ((j .eq. 6) .or. (j .eq. 1)) goto 50
if (j .eq. 3) goto 60
if (j .eq. 7) goto 70
if (j .eq. 4) goto 80
end if
40 if (c(8) .lt. 0.) goto 90
if ((tcore(j) .ge. 36.6) .and. (tsk .le. 34.050)) goto 80
goto 90
50 if (c(10) .lt. 0. ) goto 90
if ((tcore(j) .ge. 36.6) .and. (tsk .gt. 33.850)) goto 80
goto 90
60 if ((tcore(j) .lt. 36.6) .and. (tsk .le. 34.000)) goto 80
goto 90
70 if ((tcore(j) .lt. 36.6) .and. (tsk .gt. 34.000)) goto 80
goto 90
80 if ((j .ne. 4) .and. (vb .ge. 91.)) goto 90
if ((j. eq. 4) .and. (vb .lt. 89.)) goto 90
if (vb .gt. 90.) vb = 90.
C LOSSES
ws = sw * 3600. * 1000.
if (ws .gt.2000.) ws = 2000.
wd = ed / evap * 3600. * (-1000.)
wr = erel / evap * 3600. * (-1000.)
wsum = ws + wr + wd
goto 100
90 continue
100 return
end
这里是 C# 尝试复制上层 fortran 子例程:
using System;
public static class GlobalMembers_TEMP
{
//C PROGRAM TEMP
// --------------------------------------------
public static void TEMP(acl, adu, aeff, c, cair, cb, cbare, cclo, count1, csum, di, ed , emcl , emsk , enbal , enbal2 , ere , erel , esw , eswdif , eswphy , eswpot , evap , facl , fcl , fec , feff , food , h , hc , he , ht , htcl , icl , j , mbody , p , po , r1 , r2 , rbare , rcl , rclo , rclo2 , rdcl , rdsk , rob , rsum , sex , sigm , sw , swf , swm , ta , tbody , tcl, tcore , tmrt , tsk , v , vb , vb1 , vb2 , vpa , vpts , wetsk , wd , wr , ws , wsum , xx)
{
float acl;
float adu;
float aeff;
float[] c = new float[11]; // ???
float cair;
float cb;
float cbare;
float cclo;
float csum;
float di;
float ed;
float emcl;
float emsk;
float enbal;
float enbal2;
float ere;
float erel;
float esw;
float eswdif;
float eswphy;
float eswpot;
float evap;
float facl;
float fcl;
float fec;
float feff;
float food;
float h;
float hc;
float he;
float ht;
float htcl;
float icl;
float mbody;
float p;
float po;
float r1;
float r2;
float rbare;
float rcl;
float rclo;
float rclo2;
float rdcl;
float rdsk;
float rob;
float rsum;
float sigm;
float sw;
float swf;
float swm;
float ta;
float tbody;
float tcl;
float[] tcore = new float[7]; // ???
float tmrt;
float tsk;
float v;
float vb;
float vb1;
float vb2;
float vpa;
float vpts;
float wetsk;
float wd;
float wr;
float ws;
float wsum;
float xx;
int count1;
int count3;
int j;
int sex;
wetsk = 0.0;
adu = 0.203 * mbody * *0.425 * ht * *0.725;
hc = 2.67 + (6.5 * v * *0.67);
hc = hc * (p / po) * *0.55;
feff = 0.725;
//C rcl = icl / 6.45
facl = (-2.36 + 173.51 * icl - 100.76 * icl * icl + 19.28 * (icl * *3.0)) / 100.0;
//C
if (facl > 1.0)
{
facl = 1.0;
}
rcl = (icl / 6.45) / facl;
if (icl >= 2.0)
{
y = 1.0;
}
if ((icl > 0.6)) && ((icl < 2.0))
{
y = (ht - 0.2) / ht;
}
if ((icl <= 0.6)) && ((icl > 0.3))
{
y = 0.5;
}
if ((icl <= 0.3)) && ((icl > 0.0))
{
y = 0.1;
}
r2 = adu * (fcl - 1.0 + facl) / (2.0 * 3.14 * ht * y);
r1 = facl * adu / (2.0 * 3.14 * ht * y);
di = r2 - r1;
//C TEMPERATURE
for (90 j = 1, 7)
{
tsk = 34.0;
count1 = 0;
tcl = (ta + tmrt + tsk) / 3.0;
count3 = 1;
enbal2 = 0.0;
g20:
acl = adu * facl + adu * (fcl - 1.0);
rclo2 = emcl * sigm * ((tcl + 273.2) * *4.0 - (tmrt + 273.2) * *4.0) * feff;
htcl = 6.28 * ht * y * di / (rcl * alog(r2 / r1) * acl);
tsk = 1.0 / htcl * (hc * (tcl - ta) + rclo2) + tcl;
//C RADIATION
aeff = adu * feff;
rbare = aeff * (1.0 - facl) * emsk * sigm * ((tmrt + 273.2) * *4.0 - (tsk + 273.2) * *4.0);
rclo = feff * acl * emcl * sigm * ((tmrt + 273.2) * *4.0 - (tcl + 273.2) * *4.0);
rsum = rbare + rclo;
//C CONVECT
cbare = hc * (ta - tsk) * adu * (1.0 - facl);
cclo = hc * (ta - tcl) * acl;
csum = cbare + cclo;
//C CORE
c[0] = h + ere;
c[1] = adu * rob * cb;
c[2] = 18.0 - 0.5 * tsk;
c[3] = 5.28 * adu * c[2];
c[4] = 0.0208 * c[1];
c[5] = 0.76075 * c[1];
c[6] = c[3] - c[5] - tsk * c[4];
c[7] = -c[0] * c[2] - tsk * c[3] + tsk * c[5];
c[8] = c[6] * c[6] - 4.0 * c[4] * c[7];
c[9] = 5.28 * adu - c[5] - c[4] * tsk;
c[10] = c[9] * c[9] - 4.0 * c[4] * (c[5] * tsk - c[0] - 5.28 * adu * tsk);
//C
if (tsk == 36.0)
{
tsk = 36.01;
}
tcore[7] = c[0] / (5.28 * adu + c[1] * 6.3 / 3600.0) + tsk;
tcore[3] = c[0] / (5.28 * adu + (c[1] * 6.3 / 3600.0) / (1 + 0.5 * (34.0 - tsk))) + tsk;
if (c[10] < 0.0)
{
goto g22;
}
tcore[6] = (-c[9] - c[10] * *0.5) / (2.0 * c[4]);
tcore[1] = (-c[9] + c[10] * *0.5) / (2.0 * c[4]);
g22:
if (c[8] < 0.0)
{
goto g24;
}
tcore[2] = (-c[6] + Math.Abs(c[8]) * *0.5) / (2.0 * c[4]);
tcore[5] = (-c[6] - Math.Abs(c[8]) * *0.5) / (2.0 * c[4]);
g24:
tcore[4] = c[0] / (5.28 * adu + c[1] * 1.0 / 40.0) + tsk;
//C TRANSPARENCE
tbody = 0.1 * tsk + 0.9 * tcore[j];
swm = 304.94 * (tbody - 36.6) * adu / 3600000.0;
vpts = 6.11 * 10.0 * *(7.45 * tsk / (235.0 + tsk));
if (tbody <= 36.6)
{
swm = 0.0;
}
swf = 0.7 * swm;
if (sex == 1)
{
sw = swm;
}
if (sex == 2)
{
sw = swf;
}
eswphy = -sw * evap;
he = 0.633 * hc / (p * cair);
fec = 1.0 / (1.0 + 0.92 * hc * rcl);
eswpot = he * (vpa - vpts) * adu * evap * fec;
wetsk = eswphy / eswpot;
if (wetsk > 1.0)
{
wetsk = 1.0;
}
eswdif = eswphy - eswpot;
if (eswdif <= 0.0)
{
esw = eswpot;
}
if (eswdif > 0.0)
{
esw = eswphy;
}
if (esw > 0.0)
{
esw = 0.0;
}
//C DIFFERENCE
rdsk = 0.79 * 10.0 * *7.0;
rdcl = 0.0;
ed = evap / (rdsk + rdcl) * adu * (1 - wetsk) * (vpa - vpts);
//C VB
vb1 = 34.0 - tsk;
vb2 = tcore[j] - 36.6;
if (vb2 < 0.0)
{
vb2 = 0.0;
}
if (vb1 < 0.0)
{
vb1 = 0.0;
}
vb = (6.3 + 75.0 * (vb2)) / (1.0 + 0.5 * vb1);
//C BALANCE
enbal = h + ed + ere + esw + csum + rsum + food;
//C COVER
if (count1 == 0)
{
xx = 1.0;
}
if (count1 == 1)
{
xx = 0.1;
}
if (count1 == 2)
{
xx = 0.01;
}
if (count1 == 3)
{
xx = 0.001;
}
if (enbal > 0.0)
{
tcl = tcl + xx;
}
if (enbal < 0.0)
{
tcl = tcl - xx;
}
if ((enbal <= 0.0)) && ((enbal2 > 0.0))
{
goto g30;
}
if ((enbal >= 0.0)) && ((enbal2 < 0.0))
{
goto g30;
}
enbal2 = enbal;
count3 = count3 + 1;
//C
if (count3 > 200)
{
goto g30;
}
goto g20;
g30:
if ((count1 == 0.0))
{
|| ((count1 == 1.0)) || ((count1 == 2.0))
{
count1 = count1 + 1.0;
enbal2 = 0.0;
goto g20;
}
}
//C
if (count1 == 3.0)
{
//C
if ((j == 2))
{
|| ((j == 5)) goto g40;
}
if ((j == 6))
{
|| ((j == 1)) goto g50;
}
if (j == 3)
{
goto g60;
}
if (j == 7)
{
goto g70;
}
if (j == 4)
{
goto g80;
}
}
g40:
if (c[8] < 0.0)
{
goto g90;
}
if ((tcore[j] >= 36.6)) && ((tsk <= 34.050))
{
goto g80;
}
goto g90;
g50:
if (c[10] < 0.0)
{
goto g90;
}
if ((tcore[j] >= 36.6)) && ((tsk > 33.850))
{
goto g80;
}
goto g90;
g60:
if ((tcore[j] < 36.6)) && ((tsk <= 34.000))
{
goto g80;
}
goto g90;
g70:
if ((tcore[j] < 36.6)) && ((tsk > 34.000))
{
goto g80;
}
goto g90;
g80:
if ((j != 4)) && ((vb >= 91.0))
{
goto g90;
}
if ((j.eq.4)) && ((vb < 89.0))
{
goto g90;
}
if (vb > 90.0)
{
vb = 90.0;
}
//C LOSSES
ws = sw * 3600.0 * 1000.0;
if (ws > 2000.0)
{
ws = 2000.0;
}
wd = ed / evap * 3600.0 * (-1000.0);
wr = erel / evap * 3600.0 * (-1000.0);
wsum = ws + wr + wd;
goto g100;
}
g100:
return;
}
}
您可以尝试将此代码转换为状态机,其中每个状态代表循环内的单个 goto。
为此,您需要将您的方法变成一个 class,它包含所有参数和变量作为 class 字段,并且有一个构造函数来初始化适当的字段并(可选)运行"method".
我不会转换你的方法,因为它作为一个例子来说太大了,但我会给你以下内容:
原始未转换方法:
public static void Do(Int32 param1, Int32 param2)
{
Int32 loopI;
Int32 outBefore,
outAfter,
outGoto1 = 0,
outGoto2 = 0;
Int32[] outValues = new Int32[7];
Console.WriteLine("METHOD:");
Console.WriteLine("DoBeforeLoop");
outBefore = param1 + param2;
Boolean wasInGoto2 = false;
for (loopI = 0; loopI < 7; loopI++)
{
Console.WriteLine("Iteration {0}", loopI);
Console.WriteLine("FortranLoopMethodState.LoopCycleStart");
outValues[loopI] = loopI;
goto1:
Console.WriteLine("FortranLoopMethodState.Goto1");
outGoto1 = loopI + param1;
if (wasInGoto2)
{
wasInGoto2 = false;
goto end;
}
wasInGoto2 = true;
Console.WriteLine("FortranLoopMethodState.Goto2");
outGoto2 = loopI + param2;
goto goto1;
end:
DoNothing(); // We don't use break, do we?
}
Console.WriteLine("DoAfterLoop");
outAfter = param1 - param2;
Console.WriteLine(outGoto1);
Console.WriteLine(outGoto2);
}
转换后的方法:
public class ExecuteFortranMethod
{
private enum FortranLoopMethodState
{
LoopCycleStart,
Goto1,
Goto2,
LoopCycleEnded
}
#region Params
private Int32 param1;
private Int32 param2;
#endregion
#region Variables
public Int32 loopI;
public Int32 outBefore,
outAfter,
outGoto1,
outGoto2;
public Int32[] outValues = new Int32[7];
#endregion
#region Constructors
public ExecuteFortranMethod(Int32 param1, Int32 param2)
{
this.param1 = param1;
this.param2 = param2;
this.Invoke();
}
#endregion
#region Methods
private void Invoke()
{
Console.WriteLine("STATE MACHINE CLASS:");
this.DoBeforeLoop();
for (this.loopI = 0; this.loopI < 7; this.loopI++)
{
Console.WriteLine("Iteration {0}", this.loopI);
var state = FortranLoopMethodState.LoopCycleStart;
do
{
state = DoLoop(state);
}
while (state != FortranLoopMethodState.LoopCycleEnded);
}
this.DoAfterLoop();
}
#endregion
#region "Method" body methods
private void DoBeforeLoop()
{
Console.WriteLine("DoBeforeLoop");
this.outBefore = this.param1 + this.param2;
}
private void DoAfterLoop()
{
Console.WriteLine("DoAfterLoop");
this.outAfter = this.param1 - this.param2;
}
Boolean wasInGoto2;
private FortranLoopMethodState DoLoop(FortranLoopMethodState state)
{
switch (state)
{
case FortranLoopMethodState.LoopCycleStart:
{
Console.WriteLine("FortranLoopMethodState.LoopCycleStart");
this.outValues[this.loopI] = this.loopI;
return FortranLoopMethodState.Goto1;
}
case FortranLoopMethodState.Goto1:
{
Console.WriteLine("FortranLoopMethodState.Goto1");
this.outGoto1 = this.loopI + this.param1;
if (this.wasInGoto2)
{
this.wasInGoto2 = false;
return FortranLoopMethodState.LoopCycleEnded;
}
return FortranLoopMethodState.Goto2;
}
case FortranLoopMethodState.Goto2:
{
this.wasInGoto2 = true;
Console.WriteLine("FortranLoopMethodState.Goto2");
this.outGoto2 = this.loopI + this.param2;
return FortranLoopMethodState.Goto1;
}
default:
throw new InvalidOperationException("The state is invalid");
}
}
#endregion
}
Program.cs
Do(10, 20);
Console.WriteLine();
var result = new ExecuteFortranMethod(10, 20);
Console.WriteLine(result.outGoto1);
Console.WriteLine(result.outGoto2);
Console.WriteLine("Press any key");
Console.ReadKey(true);
如您所见,输出是等效的。我不会具体描述如何转移实际方法,因为这是一项乏味(但并非不真实)的任务,没有真正的捷径,即使阅读这个小例子,大部分想法也会很清楚。
P.S.1: 虽然没有真正的捷径(当然除了编写一些可以生成这种状态机的小工具 classes对于给定的代码),您可以通过一些 regular expressions 搜索和替换 (Ctrl+F) 来简化手动工作。例如,将模式 goto(\d+)
替换为 case State.Goto {
会将所有 goto 替换为 switch case 存根。
P.S.2: 如果 switch cases(gotos) 太多,你可能想用一些 Dictionary lookup.
P.S.3: 而且很多人已经指出了,我仍然建议你尝试理解原始代码流程并从头开始重写它。
循环内的 goto 意味着两件事:
- 停止当前循环的执行(这就像使用 continue)
- 跳过 goto 标签之前的所有内容(这就像使用 if)
理论上你可以使用一个大的 switch 语句而不是 if 语句,在每个情况下都没有中断(fall through 模式),所以你可以将 switch 值(创建一个变量)设置为正确的而不是执行 goto position 然后调用 continue.
但这与您在使用 goto 时遇到的问题是相同的逻辑并会导致相同的问题。对于没有编写代码的人来说,它是不可理解的、不可调试的、不可维护的。 3-4个月后,同一个人就更难了。
作为第一步,您应该尝试获取每个块并确定它到底做了什么,如果可能的话将它提取到一个 method/function 并取一个好名字。
当你完成后,你希望有一个巨大的 goto 混乱,里面有一些函数调用。尝试减少 if 语句的混乱。 g70 看起来像是一个可能的候选者,因为它有条件地执行 g80 的东西或跳过它并在 g90 继续,但没有详细查看它。
一旦你达到那个水平,你也可以制作执行图,一行(可能在电子表格中)为每个 goto 标签写下然后调用哪些函数,直到它到达另一个 goto。
好的重构的基础是对问题的良好理解,所以也许采访编写 pascal 代码的人来解释它的作用会对你有很大帮助。
代码完成后,看看你用代码做了什么,以及它真正做了什么,会很有趣!
我正在努力将以下 Fortran 77 子例程复制到 C# 方法中。主要问题是我试图摆脱 fortran 的 goto
语句。
fortran 到 C# 的转换看起来不错,但 goto
语句仍然存在。有没有一种方法可以将它们全部替换为条件语句或其他一些方法?谢谢。
这是 fortran 子例程:
C PROGRAM TEMP
subroutine TEMP (acl,adu,aeff,c,cair,cb,cbare,
+ cclo,count1,csum,di,ed,emcl,emsk,enbal,
+ enbal2,ere,erel,esw,eswdif,eswphy,eswpot,
+ evap,facl,fcl,fec,feff,food,h,hc,he,ht,htcl,icl,j,
+ mbody,p,po,r1,r2,rbare,rcl,
+ rclo,rclo2,rdcl,rdsk,rob,rsum,sex,sigm,sw,swf,swm,
+ ta,tbody,tcl,tcore,tmrt,tsk,v,vb,vb1,vb2,
+ vpa,vpts,wetsk,wd,wr,ws,wsum,xx)
real acl,adu,aeff,c(0:10),cair,cb,cbare,
+ cclo,csum,di,ed,emcl,emsk,enbal,
+ enbal2,ere,erel,esw,eswdif,eswphy,eswpot,
+ evap,facl,fcl,fec,feff,food,h,hc,he,ht,htcl,icl,
+ mbody,p,po,r1,r2,rbare,rcl,
+ rclo,rclo2,rdcl,rdsk,rob,rsum,sigm,sw,swf,swm,
+ ta,tbody,tcl,tcore(1:7),tmrt,tsk,v,vb,vb1,vb2,
+ vpa,vpts,wetsk,wd,wr,ws,wsum,xx
integer count1,count3,j,sex
wetsk = 0.
adu = 0.203 * mbody ** 0.425 * ht ** 0.725
hc = 2.67 + ( 6.5 * v ** 0.67)
hc = hc * (p /po) ** 0.55
feff = 0.725
C
facl = (- 2.36 + 173.51 * icl - 100.76 * icl * icl + 19.28
+ * (icl ** 3.)) / 100.
C
if (facl .gt.1.) facl = 1.
rcl = (icl/6.45)/facl
if (icl.ge.2.) y = 1.
if ((icl .gt. 0.6) .and. (icl .lt. 2.)) y = (ht - 0.2) / ht
if ((icl .le. 0.6) .and. (icl .gt. 0.3)) y = 0.5
if ((icl .le. 0.3) .and. (icl .gt. 0.)) y = 0.1
r2 = adu * (fcl - 1. + facl) / (2. * 3.14 * ht * y)
r1 = facl * adu / (2. * 3.14 * ht * y)
di = r2 - r1
C TEMPERATURE
do 90 j = 1,7
tsk = 34.
count1 = 0
tcl = (ta + tmrt + tsk) / 3.
count3 = 1
enbal2 = 0.
20 acl = adu * facl + adu * (fcl - 1.)
rclo2 = emcl*sigm *((tcl+273.2)** 4.-(tmrt+273.2)** 4.)*feff
htcl = 6.28 * ht * y * di / (rcl * alog(r2/r1) * acl)
tsk = 1. / htcl * (hc * (tcl - ta) + rclo2) + tcl
C RADIATION
aeff = adu * feff
rbare = aeff * (1.-facl) * emsk * sigm *
+ ((tmrt + 273.2) ** 4. - (tsk + 273.2) ** 4.)
rclo = feff * acl * emcl * sigm *
+ ((tmrt + 273.2) ** 4. - (tcl + 273.2) ** 4.)
rsum = rbare + rclo
C CONVECT
cbare = hc * (ta - tsk) * adu * (1. - facl)
cclo = hc * (ta - tcl ) * acl
csum = cbare + cclo
C CORE
c(0) = h + ere
c(1) = adu * rob * cb
c(2) = 18. - 0.5 * tsk
c(3) = 5.28 * adu * c(2)
c(4) = 0.0208 * c(1)
c(5) = 0.76075 * c(1)
c(6) = c(3) - c(5) - tsk * c(4)
c(7) = - c(0) * c(2) - tsk * c(3) + tsk * c(5)
c(8) = c(6) * c(6) - 4. * c(4) * c(7)
c(9) = 5.28 * adu - c(5) - c(4) * tsk
c(10) = c(9) * c(9) - 4. * c(4) *
+ (c(5) * tsk - c(0) - 5.28 * adu * tsk)
C
if (tsk.eq.36.) tsk=36.01
tcore(7) = c(0) / (5.28 * adu + c(1) * 6.3 / 3600.) + tsk
tcore(3) = c(0) / (5.28 * adu + (c(1) * 6.3 / 3600.) /
+ (1 + 0.5 * (34. -tsk))) + tsk
if (c(10) .lt. 0.) goto 22
tcore(6) = (- c(9) - c(10) ** 0.5) / (2. * c(4))
tcore(1) = (- c(9) + c(10) ** 0.5) / (2. * c(4))
22 if (c(8) .lt. 0.) goto 24
tcore(2) = (- c(6) + abs(c(8)) ** 0.5) / (2. * c(4))
tcore(5) = (- c(6) - abs(c(8)) ** 0.5) / (2. * c(4))
24 tcore(4) = c(0) / (5.28 * adu + c(1) * 1. / 40.) + tsk
C TRANSPARENCE
tbody = 0.1 * tsk + 0.9 * tcore (j)
swm = 304.94 * (tbody - 36.6) * adu / 3600000.
vpts = 6.11 * 10. ** (7.45 * tsk / (235. + tsk))
if (tbody .le. 36.6) swm = 0.
swf = 0.7 * swm
if(sex .eq. 1) sw = swm
if(sex .eq. 2) sw = swf
eswphy = - sw * evap
he = 0.633 * hc / (p * cair)
fec = 1. / (1. + 0.92 * hc * rcl)
eswpot = he * (vpa - vpts) * adu * evap * fec
wetsk = eswphy / eswpot
if (wetsk .gt. 1.) wetsk = 1.
eswdif = eswphy - eswpot
if (eswdif .le. 0.) esw = eswpot
if (eswdif .gt. 0.) esw = eswphy
if (esw .gt. 0.) esw = 0.
C DIFFERENCE
rdsk = 0.79 * 10. ** 7.
rdcl = 0.
ed = evap / (rdsk + rdcl) * adu * (1 - wetsk) * (vpa-vpts)
C VB
vb1 = 34. - tsk
vb2 = tcore(j) - 36.6
if (vb2 .lt.0.) vb2 = 0.
if (vb1 .lt.0.) vb1 = 0.
vb = (6.3 + 75. * (vb2)) / (1. + 0.5 * vb1)
C BALANCE
enbal = h + ed + ere + esw + csum + rsum + food
C COVER
if (count1 .eq.0) xx = 1.
if (count1 .eq.1) xx = 0.1
if (count1 .eq.2) xx = 0.01
if (count1 .eq.3) xx = 0.001
if (enbal .gt. 0.) tcl = tcl + xx
if (enbal .lt. 0.) tcl = tcl - xx
if ((enbal .le. 0.) .and. (enbal2 .gt. 0.)) goto 30
if ((enbal .ge. 0.) .and. (enbal2 .lt. 0.)) goto 30
enbal2 = enbal
count3 = count3 + 1
C
if (count3 .gt. 200) goto 30
goto 20
30 if ((count1 .eq.0.).or.(count1.eq.1.).or.(count1.eq.2.)) then
count1 = count1 + 1.
enbal2 = 0.
goto 20
end if
C
if (count1 .eq. 3.) then
C
if ((j .eq. 2) .or. (j .eq. 5)) goto 40
if ((j .eq. 6) .or. (j .eq. 1)) goto 50
if (j .eq. 3) goto 60
if (j .eq. 7) goto 70
if (j .eq. 4) goto 80
end if
40 if (c(8) .lt. 0.) goto 90
if ((tcore(j) .ge. 36.6) .and. (tsk .le. 34.050)) goto 80
goto 90
50 if (c(10) .lt. 0. ) goto 90
if ((tcore(j) .ge. 36.6) .and. (tsk .gt. 33.850)) goto 80
goto 90
60 if ((tcore(j) .lt. 36.6) .and. (tsk .le. 34.000)) goto 80
goto 90
70 if ((tcore(j) .lt. 36.6) .and. (tsk .gt. 34.000)) goto 80
goto 90
80 if ((j .ne. 4) .and. (vb .ge. 91.)) goto 90
if ((j. eq. 4) .and. (vb .lt. 89.)) goto 90
if (vb .gt. 90.) vb = 90.
C LOSSES
ws = sw * 3600. * 1000.
if (ws .gt.2000.) ws = 2000.
wd = ed / evap * 3600. * (-1000.)
wr = erel / evap * 3600. * (-1000.)
wsum = ws + wr + wd
goto 100
90 continue
100 return
end
这里是 C# 尝试复制上层 fortran 子例程:
using System;
public static class GlobalMembers_TEMP
{
//C PROGRAM TEMP
// --------------------------------------------
public static void TEMP(acl, adu, aeff, c, cair, cb, cbare, cclo, count1, csum, di, ed , emcl , emsk , enbal , enbal2 , ere , erel , esw , eswdif , eswphy , eswpot , evap , facl , fcl , fec , feff , food , h , hc , he , ht , htcl , icl , j , mbody , p , po , r1 , r2 , rbare , rcl , rclo , rclo2 , rdcl , rdsk , rob , rsum , sex , sigm , sw , swf , swm , ta , tbody , tcl, tcore , tmrt , tsk , v , vb , vb1 , vb2 , vpa , vpts , wetsk , wd , wr , ws , wsum , xx)
{
float acl;
float adu;
float aeff;
float[] c = new float[11]; // ???
float cair;
float cb;
float cbare;
float cclo;
float csum;
float di;
float ed;
float emcl;
float emsk;
float enbal;
float enbal2;
float ere;
float erel;
float esw;
float eswdif;
float eswphy;
float eswpot;
float evap;
float facl;
float fcl;
float fec;
float feff;
float food;
float h;
float hc;
float he;
float ht;
float htcl;
float icl;
float mbody;
float p;
float po;
float r1;
float r2;
float rbare;
float rcl;
float rclo;
float rclo2;
float rdcl;
float rdsk;
float rob;
float rsum;
float sigm;
float sw;
float swf;
float swm;
float ta;
float tbody;
float tcl;
float[] tcore = new float[7]; // ???
float tmrt;
float tsk;
float v;
float vb;
float vb1;
float vb2;
float vpa;
float vpts;
float wetsk;
float wd;
float wr;
float ws;
float wsum;
float xx;
int count1;
int count3;
int j;
int sex;
wetsk = 0.0;
adu = 0.203 * mbody * *0.425 * ht * *0.725;
hc = 2.67 + (6.5 * v * *0.67);
hc = hc * (p / po) * *0.55;
feff = 0.725;
//C rcl = icl / 6.45
facl = (-2.36 + 173.51 * icl - 100.76 * icl * icl + 19.28 * (icl * *3.0)) / 100.0;
//C
if (facl > 1.0)
{
facl = 1.0;
}
rcl = (icl / 6.45) / facl;
if (icl >= 2.0)
{
y = 1.0;
}
if ((icl > 0.6)) && ((icl < 2.0))
{
y = (ht - 0.2) / ht;
}
if ((icl <= 0.6)) && ((icl > 0.3))
{
y = 0.5;
}
if ((icl <= 0.3)) && ((icl > 0.0))
{
y = 0.1;
}
r2 = adu * (fcl - 1.0 + facl) / (2.0 * 3.14 * ht * y);
r1 = facl * adu / (2.0 * 3.14 * ht * y);
di = r2 - r1;
//C TEMPERATURE
for (90 j = 1, 7)
{
tsk = 34.0;
count1 = 0;
tcl = (ta + tmrt + tsk) / 3.0;
count3 = 1;
enbal2 = 0.0;
g20:
acl = adu * facl + adu * (fcl - 1.0);
rclo2 = emcl * sigm * ((tcl + 273.2) * *4.0 - (tmrt + 273.2) * *4.0) * feff;
htcl = 6.28 * ht * y * di / (rcl * alog(r2 / r1) * acl);
tsk = 1.0 / htcl * (hc * (tcl - ta) + rclo2) + tcl;
//C RADIATION
aeff = adu * feff;
rbare = aeff * (1.0 - facl) * emsk * sigm * ((tmrt + 273.2) * *4.0 - (tsk + 273.2) * *4.0);
rclo = feff * acl * emcl * sigm * ((tmrt + 273.2) * *4.0 - (tcl + 273.2) * *4.0);
rsum = rbare + rclo;
//C CONVECT
cbare = hc * (ta - tsk) * adu * (1.0 - facl);
cclo = hc * (ta - tcl) * acl;
csum = cbare + cclo;
//C CORE
c[0] = h + ere;
c[1] = adu * rob * cb;
c[2] = 18.0 - 0.5 * tsk;
c[3] = 5.28 * adu * c[2];
c[4] = 0.0208 * c[1];
c[5] = 0.76075 * c[1];
c[6] = c[3] - c[5] - tsk * c[4];
c[7] = -c[0] * c[2] - tsk * c[3] + tsk * c[5];
c[8] = c[6] * c[6] - 4.0 * c[4] * c[7];
c[9] = 5.28 * adu - c[5] - c[4] * tsk;
c[10] = c[9] * c[9] - 4.0 * c[4] * (c[5] * tsk - c[0] - 5.28 * adu * tsk);
//C
if (tsk == 36.0)
{
tsk = 36.01;
}
tcore[7] = c[0] / (5.28 * adu + c[1] * 6.3 / 3600.0) + tsk;
tcore[3] = c[0] / (5.28 * adu + (c[1] * 6.3 / 3600.0) / (1 + 0.5 * (34.0 - tsk))) + tsk;
if (c[10] < 0.0)
{
goto g22;
}
tcore[6] = (-c[9] - c[10] * *0.5) / (2.0 * c[4]);
tcore[1] = (-c[9] + c[10] * *0.5) / (2.0 * c[4]);
g22:
if (c[8] < 0.0)
{
goto g24;
}
tcore[2] = (-c[6] + Math.Abs(c[8]) * *0.5) / (2.0 * c[4]);
tcore[5] = (-c[6] - Math.Abs(c[8]) * *0.5) / (2.0 * c[4]);
g24:
tcore[4] = c[0] / (5.28 * adu + c[1] * 1.0 / 40.0) + tsk;
//C TRANSPARENCE
tbody = 0.1 * tsk + 0.9 * tcore[j];
swm = 304.94 * (tbody - 36.6) * adu / 3600000.0;
vpts = 6.11 * 10.0 * *(7.45 * tsk / (235.0 + tsk));
if (tbody <= 36.6)
{
swm = 0.0;
}
swf = 0.7 * swm;
if (sex == 1)
{
sw = swm;
}
if (sex == 2)
{
sw = swf;
}
eswphy = -sw * evap;
he = 0.633 * hc / (p * cair);
fec = 1.0 / (1.0 + 0.92 * hc * rcl);
eswpot = he * (vpa - vpts) * adu * evap * fec;
wetsk = eswphy / eswpot;
if (wetsk > 1.0)
{
wetsk = 1.0;
}
eswdif = eswphy - eswpot;
if (eswdif <= 0.0)
{
esw = eswpot;
}
if (eswdif > 0.0)
{
esw = eswphy;
}
if (esw > 0.0)
{
esw = 0.0;
}
//C DIFFERENCE
rdsk = 0.79 * 10.0 * *7.0;
rdcl = 0.0;
ed = evap / (rdsk + rdcl) * adu * (1 - wetsk) * (vpa - vpts);
//C VB
vb1 = 34.0 - tsk;
vb2 = tcore[j] - 36.6;
if (vb2 < 0.0)
{
vb2 = 0.0;
}
if (vb1 < 0.0)
{
vb1 = 0.0;
}
vb = (6.3 + 75.0 * (vb2)) / (1.0 + 0.5 * vb1);
//C BALANCE
enbal = h + ed + ere + esw + csum + rsum + food;
//C COVER
if (count1 == 0)
{
xx = 1.0;
}
if (count1 == 1)
{
xx = 0.1;
}
if (count1 == 2)
{
xx = 0.01;
}
if (count1 == 3)
{
xx = 0.001;
}
if (enbal > 0.0)
{
tcl = tcl + xx;
}
if (enbal < 0.0)
{
tcl = tcl - xx;
}
if ((enbal <= 0.0)) && ((enbal2 > 0.0))
{
goto g30;
}
if ((enbal >= 0.0)) && ((enbal2 < 0.0))
{
goto g30;
}
enbal2 = enbal;
count3 = count3 + 1;
//C
if (count3 > 200)
{
goto g30;
}
goto g20;
g30:
if ((count1 == 0.0))
{
|| ((count1 == 1.0)) || ((count1 == 2.0))
{
count1 = count1 + 1.0;
enbal2 = 0.0;
goto g20;
}
}
//C
if (count1 == 3.0)
{
//C
if ((j == 2))
{
|| ((j == 5)) goto g40;
}
if ((j == 6))
{
|| ((j == 1)) goto g50;
}
if (j == 3)
{
goto g60;
}
if (j == 7)
{
goto g70;
}
if (j == 4)
{
goto g80;
}
}
g40:
if (c[8] < 0.0)
{
goto g90;
}
if ((tcore[j] >= 36.6)) && ((tsk <= 34.050))
{
goto g80;
}
goto g90;
g50:
if (c[10] < 0.0)
{
goto g90;
}
if ((tcore[j] >= 36.6)) && ((tsk > 33.850))
{
goto g80;
}
goto g90;
g60:
if ((tcore[j] < 36.6)) && ((tsk <= 34.000))
{
goto g80;
}
goto g90;
g70:
if ((tcore[j] < 36.6)) && ((tsk > 34.000))
{
goto g80;
}
goto g90;
g80:
if ((j != 4)) && ((vb >= 91.0))
{
goto g90;
}
if ((j.eq.4)) && ((vb < 89.0))
{
goto g90;
}
if (vb > 90.0)
{
vb = 90.0;
}
//C LOSSES
ws = sw * 3600.0 * 1000.0;
if (ws > 2000.0)
{
ws = 2000.0;
}
wd = ed / evap * 3600.0 * (-1000.0);
wr = erel / evap * 3600.0 * (-1000.0);
wsum = ws + wr + wd;
goto g100;
}
g100:
return;
}
}
您可以尝试将此代码转换为状态机,其中每个状态代表循环内的单个 goto。
为此,您需要将您的方法变成一个 class,它包含所有参数和变量作为 class 字段,并且有一个构造函数来初始化适当的字段并(可选)运行"method".
我不会转换你的方法,因为它作为一个例子来说太大了,但我会给你以下内容:
原始未转换方法:
public static void Do(Int32 param1, Int32 param2)
{
Int32 loopI;
Int32 outBefore,
outAfter,
outGoto1 = 0,
outGoto2 = 0;
Int32[] outValues = new Int32[7];
Console.WriteLine("METHOD:");
Console.WriteLine("DoBeforeLoop");
outBefore = param1 + param2;
Boolean wasInGoto2 = false;
for (loopI = 0; loopI < 7; loopI++)
{
Console.WriteLine("Iteration {0}", loopI);
Console.WriteLine("FortranLoopMethodState.LoopCycleStart");
outValues[loopI] = loopI;
goto1:
Console.WriteLine("FortranLoopMethodState.Goto1");
outGoto1 = loopI + param1;
if (wasInGoto2)
{
wasInGoto2 = false;
goto end;
}
wasInGoto2 = true;
Console.WriteLine("FortranLoopMethodState.Goto2");
outGoto2 = loopI + param2;
goto goto1;
end:
DoNothing(); // We don't use break, do we?
}
Console.WriteLine("DoAfterLoop");
outAfter = param1 - param2;
Console.WriteLine(outGoto1);
Console.WriteLine(outGoto2);
}
转换后的方法:
public class ExecuteFortranMethod
{
private enum FortranLoopMethodState
{
LoopCycleStart,
Goto1,
Goto2,
LoopCycleEnded
}
#region Params
private Int32 param1;
private Int32 param2;
#endregion
#region Variables
public Int32 loopI;
public Int32 outBefore,
outAfter,
outGoto1,
outGoto2;
public Int32[] outValues = new Int32[7];
#endregion
#region Constructors
public ExecuteFortranMethod(Int32 param1, Int32 param2)
{
this.param1 = param1;
this.param2 = param2;
this.Invoke();
}
#endregion
#region Methods
private void Invoke()
{
Console.WriteLine("STATE MACHINE CLASS:");
this.DoBeforeLoop();
for (this.loopI = 0; this.loopI < 7; this.loopI++)
{
Console.WriteLine("Iteration {0}", this.loopI);
var state = FortranLoopMethodState.LoopCycleStart;
do
{
state = DoLoop(state);
}
while (state != FortranLoopMethodState.LoopCycleEnded);
}
this.DoAfterLoop();
}
#endregion
#region "Method" body methods
private void DoBeforeLoop()
{
Console.WriteLine("DoBeforeLoop");
this.outBefore = this.param1 + this.param2;
}
private void DoAfterLoop()
{
Console.WriteLine("DoAfterLoop");
this.outAfter = this.param1 - this.param2;
}
Boolean wasInGoto2;
private FortranLoopMethodState DoLoop(FortranLoopMethodState state)
{
switch (state)
{
case FortranLoopMethodState.LoopCycleStart:
{
Console.WriteLine("FortranLoopMethodState.LoopCycleStart");
this.outValues[this.loopI] = this.loopI;
return FortranLoopMethodState.Goto1;
}
case FortranLoopMethodState.Goto1:
{
Console.WriteLine("FortranLoopMethodState.Goto1");
this.outGoto1 = this.loopI + this.param1;
if (this.wasInGoto2)
{
this.wasInGoto2 = false;
return FortranLoopMethodState.LoopCycleEnded;
}
return FortranLoopMethodState.Goto2;
}
case FortranLoopMethodState.Goto2:
{
this.wasInGoto2 = true;
Console.WriteLine("FortranLoopMethodState.Goto2");
this.outGoto2 = this.loopI + this.param2;
return FortranLoopMethodState.Goto1;
}
default:
throw new InvalidOperationException("The state is invalid");
}
}
#endregion
}
Program.cs
Do(10, 20);
Console.WriteLine();
var result = new ExecuteFortranMethod(10, 20);
Console.WriteLine(result.outGoto1);
Console.WriteLine(result.outGoto2);
Console.WriteLine("Press any key");
Console.ReadKey(true);
如您所见,输出是等效的。我不会具体描述如何转移实际方法,因为这是一项乏味(但并非不真实)的任务,没有真正的捷径,即使阅读这个小例子,大部分想法也会很清楚。
P.S.1: 虽然没有真正的捷径(当然除了编写一些可以生成这种状态机的小工具 classes对于给定的代码),您可以通过一些 regular expressions 搜索和替换 (Ctrl+F) 来简化手动工作。例如,将模式 goto(\d+)
替换为 case State.Goto {
会将所有 goto 替换为 switch case 存根。
P.S.2: 如果 switch cases(gotos) 太多,你可能想用一些 Dictionary lookup.
P.S.3: 而且很多人已经指出了,我仍然建议你尝试理解原始代码流程并从头开始重写它。
循环内的 goto 意味着两件事:
- 停止当前循环的执行(这就像使用 continue)
- 跳过 goto 标签之前的所有内容(这就像使用 if)
理论上你可以使用一个大的 switch 语句而不是 if 语句,在每个情况下都没有中断(fall through 模式),所以你可以将 switch 值(创建一个变量)设置为正确的而不是执行 goto position 然后调用 continue.
但这与您在使用 goto 时遇到的问题是相同的逻辑并会导致相同的问题。对于没有编写代码的人来说,它是不可理解的、不可调试的、不可维护的。 3-4个月后,同一个人就更难了。
作为第一步,您应该尝试获取每个块并确定它到底做了什么,如果可能的话将它提取到一个 method/function 并取一个好名字。
当你完成后,你希望有一个巨大的 goto 混乱,里面有一些函数调用。尝试减少 if 语句的混乱。 g70 看起来像是一个可能的候选者,因为它有条件地执行 g80 的东西或跳过它并在 g90 继续,但没有详细查看它。
一旦你达到那个水平,你也可以制作执行图,一行(可能在电子表格中)为每个 goto 标签写下然后调用哪些函数,直到它到达另一个 goto。
好的重构的基础是对问题的良好理解,所以也许采访编写 pascal 代码的人来解释它的作用会对你有很大帮助。
代码完成后,看看你用代码做了什么,以及它真正做了什么,会很有趣!