Initial data commit
This commit is contained in:
parent
72d218235f
commit
f23f22d71c
199087 changed files with 3378941 additions and 0 deletions
365
Task/Natural-sorting/Fortran/natural-sorting-1.f
Normal file
365
Task/Natural-sorting/Fortran/natural-sorting-1.f
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
MODULE STASHTEXTS !Using COMMON is rather more tedious.
|
||||
INTEGER MSG,KBD !I/O unit numbers.
|
||||
DATA MSG,KBD/6,5/ !Output, input.
|
||||
|
||||
INTEGER LSTASH,NSTASH,MSTASH !Prepare a common text stash.
|
||||
PARAMETER (LSTASH = 2468, MSTASH = 234) !LSTASH characters for MSTASH texts.
|
||||
INTEGER ISTASH(MSTASH + 1) !Index to start positions.
|
||||
CHARACTER*(LSTASH) STASH !One pool.
|
||||
DATA NSTASH,ISTASH(1)/0,1/ !Which is empty.
|
||||
CONTAINS
|
||||
SUBROUTINE CROAK(GASP) !A dying remark.
|
||||
CHARACTER*(*) GASP !The last words.
|
||||
WRITE (MSG,*) "Oh dear." !Shock.
|
||||
WRITE (MSG,*) GASP !Aargh!
|
||||
STOP "How sad." !Farewell, cruel world.
|
||||
END SUBROUTINE CROAK !Farewell...
|
||||
|
||||
SUBROUTINE UPCASE(TEXT) !In the absence of an intrinsic...
|
||||
Converts any lower case letters in TEXT to upper case...
|
||||
Concocted yet again by R.N.McLean (whom God preserve) December MM.
|
||||
Converting from a DO loop evades having both an iteration counter to decrement and an index variable to adjust.
|
||||
CHARACTER*(*) TEXT !The stuff to be modified.
|
||||
c CHARACTER*26 LOWER,UPPER !Tables. a-z may not be contiguous codes.
|
||||
c PARAMETER (LOWER = "abcdefghijklmnopqrstuvwxyz")
|
||||
c PARAMETER (UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
CAREFUL!! The below relies on a-z and A-Z being contiguous, as is NOT the case with EBCDIC.
|
||||
INTEGER I,L,IT !Fingers.
|
||||
L = LEN(TEXT) !Get a local value, in case LEN engages in oddities.
|
||||
I = L !Start at the end and work back..
|
||||
1 IF (I.LE.0) RETURN !Are we there yet? Comparison against zero should not require a subtraction.
|
||||
c IT = INDEX(LOWER,TEXT(I:I)) !Well?
|
||||
c IF (IT .GT. 0) TEXT(I:I) = UPPER(IT:IT) !One to convert?
|
||||
IT = ICHAR(TEXT(I:I)) - ICHAR("a") !More symbols precede "a" than "A".
|
||||
IF (IT.GE.0 .AND. IT.LE.25) TEXT(I:I) = CHAR(IT + ICHAR("A")) !In a-z? Convert!
|
||||
I = I - 1 !Back one.
|
||||
GO TO 1 !Inspect..
|
||||
END SUBROUTINE UPCASE !Easy.
|
||||
|
||||
SUBROUTINE SHOWSTASH(BLAH,I) !One might be wondering.
|
||||
CHARACTER*(*) BLAH !An annotation.
|
||||
INTEGER I !The desired stashed text.
|
||||
IF (I.LE.0 .OR. I.GT.NSTASH) THEN !Paranoia rules.
|
||||
WRITE (MSG,1) BLAH,I !And is not always paranoid.
|
||||
1 FORMAT (A,': Text(',I0,') is not in the stash!') !Hopefully, helpful.
|
||||
ELSE !But surely I will only be asked for what I have.
|
||||
WRITE (MSG,2) BLAH,I,STASH(ISTASH(I):ISTASH(I + 1) - 1) !Whee!
|
||||
2 FORMAT (A,': Text(',I0,')=>',A,'<') !Hopefully, informative.
|
||||
END IF !So, it is shown.
|
||||
END SUBROUTINE SHOWSTASH !Ah, debugging.
|
||||
|
||||
INTEGER FUNCTION STASHIN(L2) !Assimilate the text ending at L2.
|
||||
Careful: furrytran regards "blah" and "blah " as equal, so, compare lengths first.
|
||||
INTEGER L2 !The text to add is at ISTASH(NSTASH + 1):L2.
|
||||
INTEGER I,L1 !Assistants.
|
||||
L1 = ISTASH(NSTASH + 1)!Where the scratchpad starts.
|
||||
L = L2 - L1 + 1 !The length of the text.
|
||||
Check to see if I already have stashed this exact text.
|
||||
DO I = 1,NSTASH !Search my existing texts.
|
||||
IF (L.EQ.ISTASH(I + 1) - ISTASH(I)) THEN !Matching lengths?
|
||||
IF (STASH(L1:L2) !Yes. Does the scratchpad
|
||||
1 .EQ.STASH(ISTASH(I):ISTASH(I + 1) - 1)) THEN !Match the stashed text?
|
||||
STASHIN = I !Yes! I already have this exact text.
|
||||
RETURN !And there is no need to duplicate it.
|
||||
END IF !So much for matching text, furrytran style.
|
||||
END IF !This time, trailing space differences will count.
|
||||
END DO !On to the next stashed text.
|
||||
Can't find it. Assimilate the scratchpad. No text is moved, just extend the fingers.
|
||||
IF (NSTASH.GE.MSTASH) CALL CROAK("The text pool is crowded!") !Alas.
|
||||
IF (L2.GT.LSTASH) CALL CROAK("Overtexted!") !Alack.
|
||||
NSTASH = NSTASH + 1 !Count in another entry.
|
||||
ISTASH(NSTASH + 1) = L2 + 1 !The new "first available" position.
|
||||
STASHIN = NSTASH !Fingered for the caller.
|
||||
END FUNCTION STASHIN !Rather than assimilating a supplied text.
|
||||
END MODULE STASHTEXTS !Others can extract text as they wish.
|
||||
|
||||
MODULE BADCHARACTER !Some characters are not for glyphs but for action.
|
||||
CHARACTER*1 BS,HT,LF,VT,FF,CR !Nicknames for a bunch of troublemakers.
|
||||
CHARACTER*6 BADC,GOODC !I want a system.
|
||||
INTEGER*1 IBADC(6) !Initialisation syntax is restricive.
|
||||
PARAMETER (GOODC="btnvfr") !Mnemonics.
|
||||
EQUIVALENCE (BADC(1:1),BS),(BADC(2:2),HT),(BADC(3:3),LF),!Match the names
|
||||
1 (BADC(4:4),VT),(BADC(5:5),FF),(BADC(6:6),CR), !To their character.
|
||||
2 (IBADC,BADC) !Alas, a PARAMETER style is rejected.
|
||||
DATA IBADC/8,9,10,11,12,13/ !ASCII encodements.
|
||||
PRIVATE IBADC !Keep this quiet.
|
||||
END MODULE BADCHARACTER !They can disrupt layout.
|
||||
|
||||
MODULE COMPOUND !Stores entries, each of multiple parts, each part a text and a number.
|
||||
USE STASHTEXTS !Gain access to the text repository.
|
||||
INTEGER LENTRY,NENTRY,MENTRY !Entry counting.
|
||||
PARAMETER (MENTRY = 28) !Should be enough for the test runs.
|
||||
INTEGER TENTRY(MENTRY) !Each entry has a source text somewhere in STASH.
|
||||
INTEGER IENTRY(MENTRY + 1) !This fingers its first part in PARTT and PARTI.
|
||||
INTEGER MPART,NPART !Now for the pool of parts.
|
||||
PARAMETER (MPART = 120) !Should suffice.
|
||||
INTEGER PARTT(MPART) !A part's text number in STASH.
|
||||
INTEGER PARTI(MPART) !A part's number, itself.
|
||||
DATA NENTRY,NPART,IENTRY(1)/0,0,1/ !There are no entries, with no parts either.
|
||||
CONTAINS !The fun begins.
|
||||
INTEGER FUNCTION ADDENTRY(X) !Create an entry holding X.
|
||||
Chops X into many parts, alternating <text><integer>,<text><integer>,...
|
||||
Converts the pieces' texts to upper case, as they will be used as a sort key later.
|
||||
CHARACTER*(*) X !The text.
|
||||
INTEGER BORED,GRIST,NUMERIC !Might as well supply some mnemonics.
|
||||
PARAMETER (BORED = 0, GRIST = 1, NUMERIC = 2) !For nearly arbitrary integers.
|
||||
INTEGER I,STATE,D !For traipsing through the text.
|
||||
INTEGER L1,L2 !Bounds of the scratchpad in STASH.
|
||||
CHARACTER*1 C !Save on some typing.
|
||||
Create a new entry. First, save its source text exactly as supplied.
|
||||
IF (NENTRY.GE.MENTRY) CALL CROAK("Too many entries!") !Perhaps I can't.
|
||||
NENTRY = NENTRY + 1 !Another entry.
|
||||
L2 = ISTASH(NSTASH + 1) - 1 !Find my scratchpad.
|
||||
STASH(L2 + 1:L2 + LEN(X)) = X !Place the text as it stands.
|
||||
TENTRY(NENTRY) = STASHIN(L2 + LEN(X)) !Find a finger to it in my text stash.
|
||||
CALL SHOWSTASH("Entering",TENTRY(NENTRY)) !Ah, debugging.
|
||||
ADDENTRY = NENTRY !I shall return this.
|
||||
Contemplate the text of the entry. Leading spaces, multiple spaces, numeric portions...
|
||||
STATE = BORED !As if in leading space stuff.
|
||||
L2 = ISTASH(NSTASH + 1) - 1 !Syncopation for text piece placement.
|
||||
N = 0 !A number may be encountered.
|
||||
DO I = 1,LEN(X) !Step through the text.
|
||||
C = X(I:I) !Grab a character.
|
||||
IF (C.LE." ") THEN !A space, or somesuch.
|
||||
SELECT CASE(STATE) !What were we doing?
|
||||
CASE(BORED) !Ignoring spaces.
|
||||
!Do nothing with this one too.
|
||||
CASE(GRIST) !We were in stuff.
|
||||
CALL ONESPACE !So accept one space only.
|
||||
CASE(NUMERIC) !We were in a number.
|
||||
CALL ADDPART !So, the number has been ended.
|
||||
STATE = BORED !But the space wot did it is ignored.
|
||||
CASE DEFAULT !This should never happen.
|
||||
CALL CROAK("Confused state!") !So this shouldn't.
|
||||
END SELECT !So much for encountering spaceish stuff.
|
||||
ELSE IF ("0".LE.C .AND. C.LE."9") THEN !A digit?
|
||||
D = ICHAR(C) - ICHAR("0") !Yes. Convert to a numerical digit.
|
||||
N = N*10 + D !Assimilate into a proper number.
|
||||
STATE = NUMERIC !Perhaps more digits follow.
|
||||
ELSE !All other characters are accepted as they stand.
|
||||
IF (STATE.EQ.NUMERIC) CALL ADDPART !A number has just ended.
|
||||
L2 = L2 + 1 !Starting a new pair's text.
|
||||
STASH(L2:L2) = C !With this.
|
||||
STATE = GRIST !And anticipating more to come.
|
||||
END IF !Types are: spaceish, grist, digits.
|
||||
END DO !On to the next character.
|
||||
CALL ADDPART !Ended by the end-of-text.
|
||||
IENTRY(NENTRY + 1) = NPART + 1 !Thus be able to find an entry's last part.
|
||||
CONTAINS !Odd assistants.
|
||||
SUBROUTINE ONESPACE !Places a space, then declares BORED.
|
||||
L2 = L2 + 1 !Advance one.
|
||||
STASH(L2:L2) = " " !An actual blank.
|
||||
STATE = BORED !Any subsequent spaces are to be ignored.
|
||||
END SUBROUTINE ONESPACE!Skipping them.
|
||||
SUBROUTINE ADDPART !Augment the paired PARTT and PARTI.
|
||||
IF (NPART.GE.MPART) CALL CROAK("Too many parts!") !If space remains.
|
||||
NPART = NPART + 1 !So, another part.
|
||||
IF (STASH(L2:L2).EQ." ") L2 = L2 - 1 !A trailing space trimmed. BORED means at most only one.
|
||||
L1 = ISTASH(NSTASH + 1) !My scratchpad starts after the last stashed text.
|
||||
CALL UPCASE(STASH(L1:L2)) !Simplify the text to be a sort key part.
|
||||
IF (IENTRY(NENTRY).EQ.NPART) CALL LIBRARIAN !The first part of an entry?
|
||||
PARTT(NPART) = STASHIN(L2) !Finger the text part.
|
||||
PARTI(NPART) = N !Save the numerical value.
|
||||
L2 = ISTASH(NSTASH + 1) - 1 !The text may not have been a newcomer.
|
||||
N = 0 !Ready for another number.
|
||||
END SUBROUTINE ADDPART !Always paired, even if no number was found.
|
||||
SUBROUTINE LIBRARIAN !Adjusts names starting "The ..." or "An ..." or "A ...", library style.
|
||||
CHARACTER*4 ARTICLE(3) !By chance, three, by happy chance, lengths 1, 2, 3!
|
||||
PARAMETER (ARTICLE = (/"A","AN","THE"/)) !These each have trailing space.
|
||||
INTEGER I !A stepper.
|
||||
DO I = 1,3 !So step through the known articles.
|
||||
IF (L1 + I.GT.L2) RETURN !Insufficient text? Give up.
|
||||
IF (STASH(L1:L1 + I).EQ.ARTICLE(I)(1:I + 1)) THEN !Starts with this one?
|
||||
STASH(L1:L2 - I - 1) = STASH(L1 + I + 1:L2) !Yes! Shift the rest back over it.
|
||||
STASH(L2 - I:L2 + 1) = ", "//ARTICLE(I)(1:I) !Place the article at the end.
|
||||
L2 = L2 + 1 !One more, for the comma.
|
||||
RETURN !Done!
|
||||
END IF !But if that article didn't match,
|
||||
END DO !Try the next.
|
||||
END SUBROUTINE LIBRARIAN !Ah, catalogue order. Blah, The.
|
||||
END FUNCTION ADDENTRY !That was fun!
|
||||
|
||||
SUBROUTINE SHOWENTRY(BLAH,E) !Ah, debugging.
|
||||
CHARACTER*(*) BLAH !With distinguishing mark.
|
||||
INTEGER E,P !Entry and part fingering.
|
||||
INTEGER L1,L2 !Fingers.
|
||||
L1 = ISTASH(TENTRY(E)) !The source text is stashed as text #TENTRY(E).
|
||||
L2 = ISTASH(TENTRY(E) + 1) - 1 !ISTASH(i) is where in STASH text #i starts.
|
||||
WRITE (MSG,1) BLAH,E,IENTRY(E),IENTRY(E + 1) - 1,STASH(L1:L2)
|
||||
1 FORMAT (/,A," Entry(",I0,")=Pt ",I0," to ",I0,", text >",A,"<")
|
||||
DO P = IENTRY(E),IENTRY(E + 1) - 1 !Step through the part list.
|
||||
L1 = ISTASH(PARTT(P)) !Find the text of the part.
|
||||
L2 = ISTASH(PARTT(P) + 1) - 1 !Saved in STASH.
|
||||
WRITE (MSG,2) P,PARTT(P),PARTI(P),STASH(L1:L2) !The text is of variable length,
|
||||
2 FORMAT ("Part(",I0,") = text#",I0,", N = ",I0," >",A,"<") !So present it *after* the number.
|
||||
END DO !On to the next part.
|
||||
END SUBROUTINE SHOWENTRY !Shows entry = <text><number>, <text><number>, ...
|
||||
|
||||
INTEGER FUNCTION ENTRYORDER(E1,E2) !Report on the order of entries E1 and E2.
|
||||
Chug through the parts list of the two entries, for each part comparing the text, then the number.
|
||||
INTEGER E1,E2 !Finger entries via TENTRY(i) and IENTRY(i)...
|
||||
INTEGER T1,T2 !Fingers texts in STASH.
|
||||
INTEGER I1,N1,I2,N2 !Fingers and counts.
|
||||
INTEGER I,D !A stepper and a difference.
|
||||
c CALL SHOWENTRY("E1",E1)
|
||||
c CALL SHOWENTRY("E2",E2)
|
||||
P1 = IENTRY(E1) !Finger the first parts
|
||||
P2 = IENTRY(E2) !Of the two entries.
|
||||
Compare the text part of the two parts.
|
||||
10 T1 = PARTT(P1) !So, what is the number of the text,
|
||||
T2 = PARTT(P2) !Safely stored in STASH.
|
||||
IF (T1.NE.T2) THEN !Inspect text only if the text parts differ.
|
||||
I1 = ISTASH(T1) !Where its text is stashed.
|
||||
N1 = ISTASH(T1 + 1) - I1 !Thus the length of that text.
|
||||
I2 = ISTASH(T2) !First character of the other text.
|
||||
N2 = ISTASH(T2 + 1) - I2 !Thus its length.
|
||||
DO I = 1,MIN(N1,N2) !Step along both texts while they have characters to match.
|
||||
D = ICHAR(STASH(I2:I2)) - ICHAR(STASH(I1:I1)) !The difference.
|
||||
IF (D.NE.0) GO TO 666 !Is there a difference?
|
||||
I1 = I1 + 1 !No.
|
||||
I2 = I2 + 1 !Advance to the next character for both.
|
||||
END DO !And try again.
|
||||
Can't compare character pairs beyond the shorter of the two texts.
|
||||
D = N2 - N1 !Very well, which text is the shorter?
|
||||
IF (D.NE.0) GO TO 666 !No difference in length?
|
||||
END IF !So much for the text comparison.
|
||||
Compare the numeric part.
|
||||
D = PARTI(P2) - PARTI(P1) !Righto, compare the numeric side.
|
||||
IF (D.NE.0) GO TO 666 !A difference here?
|
||||
Can't find any difference between those two parts.
|
||||
P1 = P1 + 1 !Move on to the next part.
|
||||
P2 = P2 + 1 !For both entries.
|
||||
N1 = IENTRY(E1 + 1) - P1 !Knowing where the next entry's parts start
|
||||
N2 = IENTRY(E2 + 1) - P2 !Means knowing where an entry's parts end.
|
||||
IF (N1.GT.0 .AND. N2.GT.0) GO TO 10 !At least one for both, so compare the next pair.
|
||||
D = N2 - N1 !Thus, the shorter precedes the longer.
|
||||
Conclusion.
|
||||
666 ENTRYORDER = D !Zero sez "equal".
|
||||
END FUNCTION ENTRYORDER !That was a struggle.
|
||||
|
||||
SUBROUTINE ORDERENTRY(LIST,N)
|
||||
Crank up a Comb sort of the entries fingered by LIST. Working backwards, just for fun.
|
||||
Caution: the H*10/13 means that H ought not be INTEGER*2. Otherwise, use H/1.3.
|
||||
INTEGER LIST(*) !This is an index to the items being compared.
|
||||
INTEGER T !In the absence of a SWAP(a,b). Same type as LIST.
|
||||
INTEGER N !The number of entries.
|
||||
INTEGER I,H !Tools. H ought not be a small integer.
|
||||
LOGICAL CURSE !Annoyance.
|
||||
H = N - 1 !Last - First, and not +1.
|
||||
IF (H.LE.0) RETURN !Ha ha.
|
||||
1 H = MAX(1,H*10/13) !The special feature.
|
||||
IF (H.EQ.9 .OR. H.EQ.10) H = 11 !A twiddle.
|
||||
CURSE = .FALSE. !So far, so good.
|
||||
DO I = N - H,1,-1 !If H = 1, this is a BubbleSort.
|
||||
IF (ENTRYORDER(LIST(I),LIST(I + H)).LT.0) THEN !One compare.
|
||||
T=LIST(I); LIST(I)=LIST(I+H); LIST(I+H)=T !One swap.
|
||||
CURSE = .TRUE. !One curse.
|
||||
END IF !One test.
|
||||
END DO !One loop.
|
||||
IF (CURSE .OR. H.GT.1) GO TO 1 !Work remains?
|
||||
END SUBROUTINE ORDERENTRY
|
||||
|
||||
CHARACTER*44 FUNCTION ENTRYTEXT(E) !Ad-hoc extraction of an entry's source text.
|
||||
INTEGER E !The desired entry's number.
|
||||
INTEGER P !A stage in the dereferencing.
|
||||
P = TENTRY(E) !Entry E's source text is #P.
|
||||
ENTRYTEXT = STASH(ISTASH(P):ISTASH(P + 1) - 1) !Stashed here.
|
||||
END FUNCTION ENTRYTEXT !Fixed size only, with trailing spaces.
|
||||
|
||||
CHARACTER*44 FUNCTION ENTRYTEXTCHAR(E) !The same, but with nasty characters defanged.
|
||||
USE BADCHARACTER !Just so.
|
||||
INTEGER E !The desired entry's number.
|
||||
INTEGER P !A stage in the dereferencing.
|
||||
CHARACTER*44 TEXT !A scratchpad, to avoid confusing the compiler.
|
||||
INTEGER I,L,H !Fingers.
|
||||
CHARACTER*1 C !A waystation.
|
||||
L = 0 !No text has been extracted.
|
||||
P = TENTRY(E) !Entry E's source text is #P.
|
||||
DO I = ISTASH(P),ISTASH(P + 1) - 1 !Step along the stash..
|
||||
C = STASH(I:I) !Grab a character.
|
||||
H = INDEX(BADC,C) !Scan the shit list.
|
||||
IF (H.LE.0) THEN !One of the troublemakers?
|
||||
CALL PUT(C) !No. Just copy it.
|
||||
ELSE !Otherwise,
|
||||
CALL PUT("!") !Place a context changer.
|
||||
CALL PUT(GOODC(H:H)) !Place the corresponding mnemonic.
|
||||
END IF !So much for that character.
|
||||
END DO !On to the next.
|
||||
ENTRYTEXTCHAR = TEXT(1:MIN(L,44)) !Protect against overflow.
|
||||
CONTAINS !A trivial assistant.
|
||||
SUBROUTINE PUT(C) !But too messy to have in-line.
|
||||
CHARACTER*1 C !The character of the moment.
|
||||
L = L + 1 !Advance to place it.
|
||||
IF (L.LE.44) TEXT(L:L) = C !If within range.
|
||||
END SUBROUTINE PUT !Simple enough.
|
||||
END FUNCTION ENTRYTEXTCHAR !On output, the troublemakers make trouble.
|
||||
|
||||
SUBROUTINE ORDERENTRYTEXT(LIST,N)
|
||||
Crank up a Comb sort of the entries fingered by LIST. Working backwards, just for fun.
|
||||
Caution: the H*10/13 means that H ought not be INTEGER*2. Otherwise, use H/1.3.
|
||||
INTEGER LIST(*) !This is an index to the items being compared.
|
||||
INTEGER T !In the absence of a SWAP(a,b). Same type as LIST.
|
||||
INTEGER N !The number of entries.
|
||||
INTEGER I,H !Tools. H ought not be a small integer.
|
||||
LOGICAL CURSE !Annoyance.
|
||||
H = N - 1 !Last - First, and not +1.
|
||||
IF (H.LE.0) RETURN !Ha ha.
|
||||
1 H = MAX(1,H*10/13) !The special feature.
|
||||
IF (H.EQ.9 .OR. H.EQ.10) H = 11 !A twiddle.
|
||||
CURSE = .FALSE. !So far, so good.
|
||||
DO I = N - H,1,-1 !If H = 1, this is a BubbleSort.
|
||||
IF (ENTRYTEXT(LIST(I)).GT.ENTRYTEXT(LIST(I+H))) THEN !One compare.
|
||||
T=LIST(I); LIST(I)=LIST(I+H); LIST(I+H)=T !One swap.
|
||||
CURSE = .TRUE. !One curse.
|
||||
END IF !One test.
|
||||
END DO !One loop.
|
||||
IF (CURSE .OR. H.GT.1) GO TO 1 !Work remains?
|
||||
END SUBROUTINE ORDERENTRYTEXT
|
||||
END MODULE COMPOUND !Accepts, stores, lists and sorts the content.
|
||||
|
||||
PROGRAM MR NATURAL !Presents a list in sorted order.
|
||||
USE COMPOUND !Stores text in a complicated way.
|
||||
USE BADCHARACTER !Some characters wreck the layout.
|
||||
INTEGER I,ITEM(30),PLAIN(30) !Two sets of indices.
|
||||
I = 0 !An array must have equal-length items, so trailing spaces would result.
|
||||
I=I+1;ITEM(I) = ADDENTRY("ignore leading spaces: 2-2")
|
||||
I=I+1;ITEM(I) = ADDENTRY(" ignore leading spaces: 2-1")
|
||||
I=I+1;ITEM(I) = ADDENTRY(" ignore leading spaces: 2+0")
|
||||
I=I+1;ITEM(I) = ADDENTRY(" ignore leading spaces: 2+1")
|
||||
I=I+1;ITEM(I) = ADDENTRY("ignore m.a.s spaces: 2-2")
|
||||
I=I+1;ITEM(I) = ADDENTRY("ignore m.a.s spaces: 2-1")
|
||||
I=I+1;ITEM(I) = ADDENTRY("ignore m.a.s spaces: 2+0")
|
||||
I=I+1;ITEM(I) = ADDENTRY("ignore m.a.s spaces: 2+1")
|
||||
I=I+1;ITEM(I) = ADDENTRY("Equiv."//" "//"spaces: 3-3")
|
||||
I=I+1;ITEM(I) = ADDENTRY("Equiv."//CR//"spaces: 3-2") !CR can't appear as itself.
|
||||
I=I+1;ITEM(I) = ADDENTRY("Equiv."//FF//"spaces: 3-1") !As it is used to mark line endings.
|
||||
I=I+1;ITEM(I) = ADDENTRY("Equiv."//VT//"spaces: 3+0") !And if typed in an editor,
|
||||
I=I+1;ITEM(I) = ADDENTRY("Equiv."//LF//"spaces: 3+1") !It is acted upon there and then.
|
||||
I=I+1;ITEM(I) = ADDENTRY("Equiv."//HT//"spaces: 3+2") !So, name instead of value.
|
||||
I=I+1;ITEM(I) = ADDENTRY("cASE INDEPENDENT: 3-2")
|
||||
I=I+1;ITEM(I) = ADDENTRY("caSE INDEPENDENT: 3-1")
|
||||
I=I+1;ITEM(I) = ADDENTRY("casE INDEPENDENT: 3+0")
|
||||
I=I+1;ITEM(I) = ADDENTRY("case INDEPENDENT: 3+1")
|
||||
I=I+1;ITEM(I) = ADDENTRY("foo100bar99baz0.txt")
|
||||
I=I+1;ITEM(I) = ADDENTRY("foo100bar10baz0.txt")
|
||||
I=I+1;ITEM(I) = ADDENTRY("foo1000bar99baz10.txt")
|
||||
I=I+1;ITEM(I) = ADDENTRY("foo1000bar99baz9.txt")
|
||||
I=I+1;ITEM(I) = ADDENTRY("The Wind in the Willows")
|
||||
I=I+1;ITEM(I) = ADDENTRY("The 40th step more")
|
||||
I=I+1;ITEM(I) = ADDENTRY("The 39 steps")
|
||||
I=I+1;ITEM(I) = ADDENTRY("Wanda")
|
||||
c I=I+1;ITEM(I) = ADDENTRY("A Dinosaur Grunts: Fortran Emerges")
|
||||
c I=I+1;ITEM(I) = ADDENTRY("The Joy of Text Twiddling with Fortran")
|
||||
c I=I+1;ITEM(I) = ADDENTRY("An Aversion to Unused Trailing Spaces")
|
||||
WRITE (MSG,*) "nEntry=",NENTRY !Reach into the compound storage area.
|
||||
PLAIN = ITEM !Copy the list of entries.
|
||||
CALL ORDERENTRY(ITEM,NENTRY) !"Natural" order.
|
||||
CALL ORDERENTRYTEXT(PLAIN,NENTRY) !Plain text order.
|
||||
WRITE (MSG,1) "Character","'Natural'" !Provide a heading.
|
||||
1 FORMAT (2("Entry|Text ",A9," Order",24X)) !Usual trickery.
|
||||
DO I = 1,NENTRY !Step through the lot.
|
||||
WRITE (MSG,2) PLAIN(I),ENTRYTEXTCHAR(PLAIN(I)), !Plain order,
|
||||
1 ITEM(I), ENTRYTEXTCHAR(ITEM(I)) !Followed by natural order.
|
||||
2 FORMAT (2(I5,"|",A44)) !This follows function ENTRYTEXT.
|
||||
END DO !On to the next.
|
||||
END !A handy hint from Mr. Natural: "At home or at work, get the right tool for the job!"
|
||||
325
Task/Natural-sorting/Fortran/natural-sorting-2.f
Normal file
325
Task/Natural-sorting/Fortran/natural-sorting-2.f
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
MODULE ASSISTANCE
|
||||
INTEGER MSG,KBD !I/O unit numbers.
|
||||
DATA MSG,KBD/6,5/ !Output, input.
|
||||
CONTAINS
|
||||
SUBROUTINE CROAK(GASP) !A dying remark.
|
||||
CHARACTER*(*) GASP !The last words.
|
||||
WRITE (MSG,*) "Oh dear." !Shock.
|
||||
WRITE (MSG,*) GASP !Aargh!
|
||||
STOP "How sad." !Farewell, cruel world.
|
||||
END SUBROUTINE CROAK !Farewell...
|
||||
|
||||
SUBROUTINE UPCASE(TEXT) !In the absence of an intrinsic...
|
||||
Converts any lower case letters in TEXT to upper case...
|
||||
Concocted yet again by R.N.McLean (whom God preserve) December MM.
|
||||
Converting from a DO loop evades having both an iteration counter to decrement and an index variable to adjust.
|
||||
CHARACTER*(*) TEXT !The stuff to be modified.
|
||||
c CHARACTER*26 LOWER,UPPER !Tables. a-z may not be contiguous codes.
|
||||
c PARAMETER (LOWER = "abcdefghijklmnopqrstuvwxyz")
|
||||
c PARAMETER (UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
CAREFUL!! The below relies on a-z and A-Z being contiguous, as is NOT the case with EBCDIC.
|
||||
INTEGER I,L,IT !Fingers.
|
||||
L = LEN(TEXT) !Get a local value, in case LEN engages in oddities.
|
||||
I = L !Start at the end and work back..
|
||||
1 IF (I.LE.0) RETURN !Are we there yet? Comparison against zero should not require a subtraction.
|
||||
c IT = INDEX(LOWER,TEXT(I:I)) !Well?
|
||||
c IF (IT .GT. 0) TEXT(I:I) = UPPER(IT:IT) !One to convert?
|
||||
IT = ICHAR(TEXT(I:I)) - ICHAR("a") !More symbols precede "a" than "A".
|
||||
IF (IT.GE.0 .AND. IT.LE.25) TEXT(I:I) = CHAR(IT + ICHAR("A")) !In a-z? Convert!
|
||||
I = I - 1 !Back one.
|
||||
GO TO 1 !Inspect..
|
||||
END SUBROUTINE UPCASE !Easy.
|
||||
|
||||
INTEGER FUNCTION LSTNB(TEXT) !Sigh. Last Not Blank.
|
||||
Concocted yet again by R.N.McLean (whom God preserve) December MM.
|
||||
Code checking reveals that the Compaq compiler generates a copy of the string and then finds the length of that when using the latter-day intrinsic LEN_TRIM. Madness!
|
||||
Can't DO WHILE (L.GT.0 .AND. TEXT(L:L).LE.' ') !Control chars. regarded as spaces.
|
||||
Curse the morons who think it good that the compiler MIGHT evaluate logical expressions fully.
|
||||
Crude GO TO rather than a DO-loop, because compilers use a loop counter as well as updating the index variable.
|
||||
Comparison runs of GNASH showed a saving of ~3% in its mass-data reading through the avoidance of DO in LSTNB alone.
|
||||
Crappy code for character comparison of varying lengths is avoided by using ICHAR which is for single characters only.
|
||||
Checking the indexing of CHARACTER variables for bounds evoked astounding stupidities, such as calculating the length of TEXT(L:L) by subtracting L from L!
|
||||
Comparison runs of GNASH showed a saving of ~25-30% in its mass data scanning for this, involving all its two-dozen or so single-character comparisons, not just in LSTNB.
|
||||
CHARACTER*(*),INTENT(IN):: TEXT !The bumf. If there must be copy-in, at least there need not be copy back.
|
||||
INTEGER L !The length of the bumf.
|
||||
L = LEN(TEXT) !So, what is it?
|
||||
1 IF (L.LE.0) GO TO 2 !Are we there yet?
|
||||
IF (ICHAR(TEXT(L:L)).GT.ICHAR(" ")) GO TO 2 !Control chars are regarded as spaces also.
|
||||
L = L - 1 !Step back one.
|
||||
GO TO 1 !And try again.
|
||||
2 LSTNB = L !The last non-blank, possibly zero.
|
||||
RETURN !Unsafe to use LSTNB as a variable.
|
||||
END FUNCTION LSTNB !Compilers can bungle it.
|
||||
END MODULE ASSISTANCE
|
||||
|
||||
MODULE BADCHARACTER !Some characters are not for glyphs but for action.
|
||||
CHARACTER*1 BS,HT,LF,VT,FF,CR !Nicknames for a bunch of troublemakers.
|
||||
CHARACTER*6 BADC,GOODC !I want a system.
|
||||
INTEGER*1 IBADC(6) !Initialisation syntax is restricive.
|
||||
PARAMETER (GOODC="btnvfr") !Mnemonics.
|
||||
EQUIVALENCE (BADC(1:1),BS),(BADC(2:2),HT),(BADC(3:3),LF),!Match the names
|
||||
1 (BADC(4:4),VT),(BADC(5:5),FF),(BADC(6:6),CR), !To their character.
|
||||
2 (IBADC,BADC) !Alas, a PARAMETER style is rejected.
|
||||
DATA IBADC/8,9,10,11,12,13/ !ASCII encodements.
|
||||
PRIVATE IBADC !Keep this quiet.
|
||||
CONTAINS
|
||||
CHARACTER*44 FUNCTION DEFANG(THIS) !Ad-hoc text conversion with nasty characters defanged.
|
||||
CHARACTER*(*) THIS !The text.
|
||||
CHARACTER*44 TEXT !A scratchpad, to avoid confusing the compiler.
|
||||
INTEGER I,L,H !Fingers.
|
||||
CHARACTER*1 C !A waystation.
|
||||
L = 0 !No text has been extracted.
|
||||
DO I = 1,LEN(THIS) !Step along the stash..
|
||||
C = THIS(I:I) !Grab a character.
|
||||
H = INDEX(BADC,C) !Scan the shit list.
|
||||
IF (H.LE.0) THEN !One of the troublemakers?
|
||||
CALL PUT(C) !No. Just copy it.
|
||||
ELSE !Otherwise,
|
||||
CALL PUT("!") !Place a context changer.
|
||||
CALL PUT(GOODC(H:H)) !Place the corresponding mnemonic.
|
||||
END IF !So much for that character.
|
||||
END DO !On to the next.
|
||||
DEFANG = TEXT(1:MIN(L,44)) !Protect against overflow.
|
||||
CONTAINS !A trivial assistant.
|
||||
SUBROUTINE PUT(C) !But too messy to have in-line.
|
||||
CHARACTER*1 C !The character of the moment.
|
||||
L = L + 1 !Advance to place it.
|
||||
IF (L.LE.44) TEXT(L:L) = C !If within range.
|
||||
END SUBROUTINE PUT !Simple enough.
|
||||
END FUNCTION DEFANG !On output, the troublemakers make trouble.
|
||||
END MODULE BADCHARACTER !They can disrupt layout.
|
||||
|
||||
MODULE COMPOUND !Stuff to store the text entries, and to sort lists.
|
||||
USE ASSISTANCE
|
||||
INTEGER LENTRY,NENTRY,MENTRY !Size information.
|
||||
PARAMETER (LENTRY = 66, MENTRY = 666) !Should suffice.
|
||||
INTEGER ENTRYLENGTH(MENTRY) !Lengths for the entries.
|
||||
CHARACTER*(LENTRY) ENTRYTEXT(MENTRY) !Their texts.
|
||||
CHARACTER*(LENTRY) ENTRYKEY(MENTRY) !Comparison keys.
|
||||
CONTAINS !The details.
|
||||
INTEGER FUNCTION ADDENTRY(X) !Create an entry holding X.
|
||||
CHARACTER*(*) X !The text to be stashed.
|
||||
INTEGER L !It may have trailing space stuff.
|
||||
L = LSTNB(X) !Thus, LEN(X) won't do.
|
||||
IF (L.GT.LENTRY) CALL CROAK("Over-long text!") !Even though any trailing spaces have been lost.
|
||||
IF (NENTRY.GE.MENTRY) CALL CROAK("Too many entries!") !Perhaps I can't.
|
||||
NENTRY = NENTRY + 1 !Righto, another one.
|
||||
ENTRYTEXT(NENTRY)(1:L) = X(1:L)!Place. Trailing spaces will not be supplied.
|
||||
ENTRYLENGTH(NENTRY) = L !But I won't be looking where they won't be.
|
||||
ADDENTRY = NENTRY !The caller needn't keep count.
|
||||
END FUNCTION ADDENTRY !That was simple.
|
||||
|
||||
INTEGER FUNCTION TEXTORDER(E1,E2) !Compare the texts as they stand.
|
||||
INTEGER E1,E2 !Finger the entries holding the texts.
|
||||
IF (ENTRYTEXT(E1)(1:ENTRYLENGTH(E1)) !If the text of entry E1
|
||||
1 .LT.ENTRYTEXT(E2)(1:ENTRYLENGTH(E2))) THEN !Precedes that of E2,
|
||||
TEXTORDER = +1 !Then the order is good.
|
||||
ELSE IF (ENTRYTEXT(E1)(1:ENTRYLENGTH(E1)) !ENTRYLENGTH means no trailing spaces.
|
||||
1 .GT.ENTRYTEXT(E2)(1:ENTRYLENGTH(E2))) THEN !Accordingly, no "x" = "x " accommodation.
|
||||
TEXTORDER = -1 !So, reversed order.
|
||||
ELSE !Otherwise,
|
||||
TEXTORDER = 0 !They're equal.
|
||||
END IF !So, decided.
|
||||
END FUNCTION TEXTORDER !Thus use the character collation sequence.
|
||||
|
||||
INTEGER FUNCTION NATURALORDER(E1,E2) !Compares the texts in "natural" order.
|
||||
INTEGER E1,E2 !Pity this couldn't be an array of two values.
|
||||
CHARACTER*4 ARTICLE(3) !By chance, three, by happy chance, lengths 1, 2, 3!
|
||||
PARAMETER (ARTICLE = (/"A","AN","THE"/)) !These each have trailing space.
|
||||
INTEGER DONE,BORED,GRIST,NUMERIC !Might as well supply some mnemonics.
|
||||
PARAMETER (DONE=-1,BORED=0,GRIST=1,NUMERIC=2) !For nearly arbitrary integers.
|
||||
INTEGER WOT(2) !Collect the two entry numbers.
|
||||
INTEGER L(2),LST(2) !Scan text with finger L, ending with LST.
|
||||
INTEGER N !Counter for comparisons.
|
||||
INTEGER DCOUNT(2) !Counts the number of digits for L(is) onwards.
|
||||
INTEGER STATE(2) !The scans vary in mood.
|
||||
INTEGER TAIL(2) !The LIBRARIAN may discover an ARTICLE and put it in the TAIL.
|
||||
INTEGER D !A difference.
|
||||
CHARACTER*1 C(2) !Character pairs ascertained one-by-one by ANOTHER.
|
||||
WOT(1) = E1 !Alright,
|
||||
WOT(2) = E2 !Into an array to play.
|
||||
L = 0 !Syncopation to start the scan.
|
||||
LST = ENTRYLENGTH(WOT) !End markers.
|
||||
STATE = BORED !So far, and no matter what the librarian discovers.
|
||||
DCOUNT = 0 !Nor have any digits been counted.
|
||||
CALL LIBRARIAN !Assess the start of the texts.
|
||||
N = 0 !No comparisons so far.
|
||||
Chug along the texts, character by character.
|
||||
10 CALL ANOTHER !Grab one from each text.
|
||||
N = N + 1 !Count another compare.
|
||||
ENTRYKEY(WOT)(N:N) = C !Place the characters being compared.
|
||||
D = ICHAR(C(2)) - ICHAR(C(1)) !Their difference.
|
||||
IF (D.NE.0) GO TO 666 !A decision yet?
|
||||
L = L + 1 !No. Advance both fingers.
|
||||
IF (ANY(STATE.NE.DONE)) GO TO 10 !And try again.
|
||||
666 NATURALORDER = D !The decision.
|
||||
RETURN !Despite the lack of an END, this is the end of the function.
|
||||
CONTAINS !Which however contains some assistants, defined after use.
|
||||
SUBROUTINE CRUSH(C) !Reduces annoying variation.
|
||||
CHARACTER*1 C !The victim.
|
||||
IF (C.LE." ") THEN !Spaceish?
|
||||
C = " " !Yes. Standardise.
|
||||
ELSE !For all others,
|
||||
CALL UPCASE(C) !Simplify.
|
||||
END IF !Righto, ready to compare.
|
||||
END SUBROUTINE CRUSH !This should do the deed in place.
|
||||
|
||||
SUBROUTINE ANOTHER !The entry's text may be followed by an article in the tail.
|
||||
Claws along the text strings, looking for the next character pair to report for matching.
|
||||
INTEGER IS !Steps through the two texts.
|
||||
INTEGER L2 !A second finger, for probing ahead and the TAIL.
|
||||
CHARACTER*1 D !Potentially a digit character.
|
||||
EE:DO IS = 1,2 !Dealing with both texts in the same way.
|
||||
10 L2 = L(IS) - LST(IS) !Compare the finger to the end-of-text.
|
||||
IF (L2.GT.0) THEN !Perhaps we have reached the tail.
|
||||
IF (TAIL(IS).GT.0 .AND. L2.LE.TAIL(IS)) THEN !Yes. What about the possible tail?
|
||||
C(IS) = ARTICLE(TAIL(IS))(L2:L2) !Still wagging.
|
||||
ELSE !But if no tail (or the tail is exhausted)
|
||||
C(IS) = CHAR(0) !Empty space.
|
||||
STATE(IS) = DONE !Declare this.
|
||||
END IF !So much for the librarian's tail.
|
||||
CYCLE EE !On to the next text.
|
||||
END IF !But if we have text yet to scan,
|
||||
C(IS) = ENTRYTEXT(WOT(IS))(L(IS):L(IS)) !Grab the character.
|
||||
CALL CRUSH(C(IS)) !Simplify.
|
||||
IF (C(IS).EQ." ") THEN !So, what have we received?
|
||||
IF (STATE(IS).EQ.BORED) THEN !A space. Are we ignoring them?
|
||||
L(IS) = L(IS) + 1 !Yes. Advance in hope.
|
||||
GO TO 10 !And try again.
|
||||
END IF !So much for another space.
|
||||
STATE(IS) = BORED !If we weren't in spaces, we are now.
|
||||
ELSE IF (C(IS).GE."0" .AND. C(IS).LE."9") THEN !A digit?
|
||||
STATE(IS) = NUMERIC !Double trouble might ensue.
|
||||
ELSE !For all other characters,
|
||||
STATE(IS) = GRIST !We have grist.
|
||||
END IF !So much for the character.
|
||||
END DO EE !On to the next text.
|
||||
Comparing digit sequences is to be done as numbers. "007" vs "70" is to become vs. "070" by length matching.
|
||||
IF (ALL(STATE.EQ.NUMERIC)) THEN !If we're comparing a digit to a digit,
|
||||
IF (ALL(DCOUNT.EQ.0)) THEN !I want to align the comparison from the right.
|
||||
DD:DO IS = 1,2 !So I need to determine how many digits follow in both.
|
||||
20 DCOUNT(IS) = DCOUNT(IS) + 1 !Count one more.
|
||||
L2 = L(IS) + DCOUNT(IS) !Finger the next position.
|
||||
IF (L2.GT.LST(IS)) CYCLE DD !If we're off the end, we're done.
|
||||
D = ENTRYTEXT(WOT(IS))(L2:L2) !Otherwise, grab the character.
|
||||
IF (D.LT."0" .OR. D.GT."9") CYCLE DD !Not a digit: done counting.
|
||||
GO TO 20 !Otherwise, keep on looking.
|
||||
END DO DD !On to the other text.
|
||||
END IF !Righto, I now know how many digits are in each sequence.
|
||||
Choose the shorter, and notionally insert a leading zero for it to be matched against the longer's digit..
|
||||
IF (DCOUNT(1).LT.DCOUNT(2)) THEN !Righto, if the first has fewer digits,
|
||||
DCOUNT(2) = DCOUNT(2) - 1 !Then only the second's digit will be used up.
|
||||
L(1) = L(1) - 1 !Step back to re-encounter this next time.
|
||||
C(1) = "0" !And create a leading zero from nothing.
|
||||
ELSE IF (DCOUNT(2).LT.DCOUNT(1)) THEN !Likewise if the other way around.
|
||||
DCOUNT(1) = DCOUNT(1) - 1 !The scan will consume this side's digit.
|
||||
L(2) = L(2) - 1 !The next time here (if there is one)
|
||||
C(2) = "0" !Will find a reduced difference in length.
|
||||
ELSE !But if both have the same number of digits remaining,
|
||||
DCOUNT = DCOUNT - 1 !They are used in parallel.
|
||||
END IF !Perhaps even equal digit remnants.
|
||||
END IF !Thus, arbitrary-size numbers are allowed, as they're never numbers.
|
||||
END SUBROUTINE ANOTHER !Characters are announced in array C, moods in array STATE.
|
||||
|
||||
SUBROUTINE LIBRARIAN !Looks for texts starting "The ..." or "An ..." or "A ...", library style.
|
||||
Checks the starts of the two texts, skipping leading spaceish stuff.
|
||||
INTEGER IS,A,I !Steppers.
|
||||
CHARACTER*1 C !A character to mess with.
|
||||
EE:DO IS = 1,2 !Two texts to inspect.
|
||||
TAIL(IS) = 0 !Nothing special found.
|
||||
10 L(IS) = L(IS) + 1 !Advance one.
|
||||
IF (L(IS).GT.LST(IS)) CYCLE EE !Run out of text?
|
||||
IF (ENTRYTEXT(WOT(IS))(L(IS):L(IS)).LE." ") GO TO 10 !Scoot through leading space stuff.
|
||||
AA:DO A = 1,3 !Now step through the known articles.
|
||||
DO I = 0,A !Character by character thereof, with one trailing space.
|
||||
IF (L(IS) + I.GT.LST(IS)) CYCLE EE !Have I a character to probe?
|
||||
C = ENTRYTEXT(WOT(IS))(L(IS) + I:L(IS) + I) !Yes. Grab it.
|
||||
CALL CRUSH(C) !Simplify.
|
||||
IF (C.NE.ARTICLE(A)(1 + I:1 + I)) CYCLE AA !Mismatch? Try another.
|
||||
END DO !On to the next character of ARTICLE(A).
|
||||
TAIL(IS) = A !A match!
|
||||
L(IS) = L(IS) + I !Finger the first character after the space.
|
||||
CYCLE EE !Finished with this text. Also, BORED.
|
||||
END DO AA !Try the next article..
|
||||
END DO EE !Try the next text.
|
||||
END SUBROUTINE LIBRARIAN !Ah, catalogue order. Blah, The.
|
||||
END FUNCTION NATURALORDER !Not natural to a computer.
|
||||
|
||||
SUBROUTINE ORDERENTRY(LIST,N,WOTORDER) !Sorts the list according to the ordering function.
|
||||
Crank up a Comb sort of the entries fingered by LIST. Working backwards, just for fun.
|
||||
Caution: the H*10/13 means that H ought not be INTEGER*2. Otherwise, use H/1.3.
|
||||
INTEGER LIST(*) !This is an index to the items being compared.
|
||||
INTEGER T !In the absence of a SWAP(a,b). Same type as LIST.
|
||||
INTEGER N !The number of entries.
|
||||
EXTERNAL WOTORDER !A function to compare two entries.
|
||||
INTEGER WOTORDER !Returns an integer result, on principle.
|
||||
INTEGER I,H !Tools. H ought not be a small integer.
|
||||
LOGICAL CURSE !Annoyance.
|
||||
H = N - 1 !Last - First, and not +1.
|
||||
IF (H.LE.0) RETURN !Ha ha.
|
||||
1 H = MAX(1,H*10/13) !The special feature.
|
||||
IF (H.EQ.9 .OR. H.EQ.10) H = 11 !A twiddle.
|
||||
CURSE = .FALSE. !So far, so good.
|
||||
DO I = N - H,1,-1 !If H = 1, this is a BubbleSort.
|
||||
IF (WOTORDER(LIST(I),LIST(I + H)) .LT. 0) THEN !One compare.
|
||||
T=LIST(I); LIST(I)=LIST(I+H); LIST(I+H)=T !One swap.
|
||||
CURSE = .TRUE. !One curse.
|
||||
END IF !One test.
|
||||
END DO !One loop.
|
||||
IF (CURSE .OR. H.GT.1) GO TO 1 !Work remains?
|
||||
END SUBROUTINE ORDERENTRY!Fast enough, and simple.
|
||||
END MODULE COMPOUND !Enough.
|
||||
|
||||
PROGRAM MR NATURAL !Presents a list in sorted order.
|
||||
USE ASSISTANCE !Often needed.
|
||||
USE COMPOUND !Deals with text in a complicated way.
|
||||
USE BADCHARACTER !Some characters wreck the layout.
|
||||
INTEGER ITEM(30),FANCY(30)!Two sets of indices.
|
||||
INTEGER I,IT,TI !Assistants.
|
||||
I = 0 !An array must have equal-length items, so trailing spaces would result.
|
||||
I=I+1;ITEM(I) = ADDENTRY("ignore leading spaces: 2-2")
|
||||
I=I+1;ITEM(I) = ADDENTRY(" ignore leading spaces: 2-1")
|
||||
I=I+1;ITEM(I) = ADDENTRY(" ignore leading spaces: 2+0")
|
||||
I=I+1;ITEM(I) = ADDENTRY(" ignore leading spaces: 2+1")
|
||||
I=I+1;ITEM(I) = ADDENTRY("ignore m.a.s spaces: 2-2")
|
||||
I=I+1;ITEM(I) = ADDENTRY("ignore m.a.s spaces: 2-1")
|
||||
I=I+1;ITEM(I) = ADDENTRY("ignore m.a.s spaces: 2+0")
|
||||
I=I+1;ITEM(I) = ADDENTRY("ignore m.a.s spaces: 2+1")
|
||||
I=I+1;ITEM(I) = ADDENTRY("Equiv."//" "//"spaces: 3-3")
|
||||
I=I+1;ITEM(I) = ADDENTRY("Equiv."//CR//"spaces: 3-2") !CR can't appear as itself.
|
||||
I=I+1;ITEM(I) = ADDENTRY("Equiv."//FF//"spaces: 3-1") !As it is used to mark line endings.
|
||||
I=I+1;ITEM(I) = ADDENTRY("Equiv."//VT//"spaces: 3+0") !And if typed in an editor,
|
||||
I=I+1;ITEM(I) = ADDENTRY("Equiv."//LF//"spaces: 3+1") !It is acted upon there and then.
|
||||
I=I+1;ITEM(I) = ADDENTRY("Equiv."//HT//"spaces: 3+2") !So, name instead of value.
|
||||
I=I+1;ITEM(I) = ADDENTRY("cASE INDEPENDENT: 3-2")
|
||||
I=I+1;ITEM(I) = ADDENTRY("caSE INDEPENDENT: 3-1")
|
||||
I=I+1;ITEM(I) = ADDENTRY("casE INDEPENDENT: 3+0")
|
||||
I=I+1;ITEM(I) = ADDENTRY("case INDEPENDENT: 3+1")
|
||||
I=I+1;ITEM(I) = ADDENTRY("foo100bar99baz0.txt")
|
||||
I=I+1;ITEM(I) = ADDENTRY("foo100bar10baz0.txt")
|
||||
I=I+1;ITEM(I) = ADDENTRY("foo1000bar99baz10.txt")
|
||||
I=I+1;ITEM(I) = ADDENTRY("foo1000bar99baz9.txt")
|
||||
I=I+1;ITEM(I) = ADDENTRY("The Wind in the Willows")
|
||||
I=I+1;ITEM(I) = ADDENTRY("The 40th step more")
|
||||
I=I+1;ITEM(I) = ADDENTRY("The 39 steps")
|
||||
I=I+1;ITEM(I) = ADDENTRY("Wanda")
|
||||
c I=I+1;ITEM(I) = ADDENTRY("A Dinosaur Grunts: Fortran Emerges")
|
||||
c I=I+1;ITEM(I) = ADDENTRY("The Joy of Text Twiddling with Fortran")
|
||||
c I=I+1;ITEM(I) = ADDENTRY("An Abundance of Storage Enables Waste")
|
||||
c I=I+1;ITEM(I) = ADDENTRY("Theory Versus Practice: The Chasm")
|
||||
WRITE (MSG,*) "nEntry=",NENTRY !Reach into the compound storage area.
|
||||
FANCY = ITEM !Copy the list of entries.
|
||||
ENTRYKEY = "" !To be written to by NATURALORDER.
|
||||
CALL ORDERENTRY(FANCY,NENTRY,NATURALORDER) !"Natural" order.
|
||||
CALL ORDERENTRY(ITEM,NENTRY,TEXTORDER) !Plain text order.
|
||||
WRITE (MSG,1) "Character","'Natural'","N.Key" !Provide a heading.
|
||||
1 FORMAT (3("Entry|Text ",A9," Order",16X)) !Usual trickery.
|
||||
DO I = 1,NENTRY !Step through the lot.
|
||||
IT = ITEM(I) !Saving on some typing.
|
||||
TI = FANCY(I) !Presenting two lists, line by line.
|
||||
WRITE (MSG,2) IT,DEFANG(ENTRYTEXT(IT)(1:ENTRYLENGTH(IT))) !Plain order,
|
||||
1 ,TI,DEFANG(ENTRYTEXT(TI)(1:ENTRYLENGTH(TI))) !Followed by natural order.
|
||||
2 ,TI,ENTRYKEY(TI) !Already defanged.
|
||||
2 FORMAT (3(I5,"|",A36)) !This follows function ENTRYTEXT.
|
||||
END DO !On to the next.
|
||||
END !A handy hint from Mr. Natural: "At home or at work, get the right tool for the job!"
|
||||
Loading…
Add table
Add a link
Reference in a new issue