Mad Libs: Difference between revisions

3,368 bytes added ,  9 years ago
→‎{{header|Fortran}}: Escalate to laying out the output.
(→‎{{header|Fortran}}: Enable recursive usage...)
(→‎{{header|Fortran}}: Escalate to laying out the output.)
Line 752:
The plan here is to use a pool of storage, one large character storage area (STASH) along with an array ISTASH giving the positions of the initial character of each stashed text string. Such strings can then be referred to by their index number, a simple integer, which can be stored in other lists as ordinary arrays. The key point is that there is no change to the size of any stashed strings, nor are any deleted. This means no shuffling about with the need to update various arrays. Thus, the i'th stashed string is found in STASH starting at character <code>ISTASH(i)</code> and so by extension, its last character is at <code>ISTASH(i + 1) - 1</code> Thereby the storage of the strings wastes no space, though STASH still has to be "big enough".
 
Because there is no "length" attribute as a part of a character variable, pieces of a character variable can be passed to subprograms without the need for copies. Specifically, if a string consisted of a one-byte length followed by the storage area for the content (as say in Pascal), passing a substring of characters 7-11 would mean creating a copy in other storage with a length byte having 5 followed by the content. The subprogram could ''not'' receive some sort of pointer to the parent string, because the need to place the length byte for the string type would mean overwriting a character of the parent string. Because Fortran has no such length feature, specifying say <code>STASH(7:11)</code> as a parameter involves ''no'' copying of the content. A subprogram to convert text to uppercase would work on the source data in place rather than copy-in, copy-out. SpecificallyIn particular, the comparison of a portion of a character variable to another portion of another character variable is achieved ''without copying text''.
 
The absence of a "current length" notion also means that when an assignment is made to a character variable, if the source is shorter than the recipient, trailing spaces will be supplied, possibly in very large numbers. A statement <code>STASH = "Short"</code> would copy five characters and then follow with spaces to the end. This can be avoided by specifying <code>STASH(1:5) = "Short"</code> with the usual risk of miscounting in the programming. Character variables typically contain trailing spaces, and the rule for comparison ignores them. Thus "Short" and "Short " are deemed equal. For this reason, the < and > bounding the target texts are included in their string: "<Short>" and "<Short >" are definitely not equal.
 
There is only token checking for ill-formed stories. The checking for array overflow and the like is present on principle, but is unlikely to be exercised in simple runs, and so only uncivilised messages are evoked. Although allowing replacement texts to themselves refer to <...> entries (and add their own) is not called for, the notion adds to the entertainment value, so... This is most easily done via recursion. There has never been any difficulty in having a Fortran subprogrammessubprogram invoke itself or otherwise engage in recursion, it is just that early implementations did not support any sort of stack mechanism to handle the returns to the correct places, just one level of return, and so it was endlessly said that recursion is impossible in Fortran. But it is not actually a language problem, as demonstrated by the Burroughs 6700 implementation, whereby it just worked. This has been ignored in later versions of Fortran, which require the use of the magic word RECURSIVE for all those subprogrammessubprograms that may dabble in unnatural practices.
 
Otherwise, the older styles of Fortran could dabble in recursion only via the provision of an array or two and a counter and some control code to save and restore whatever state is necessary for the task at hand. This is done in WRITESTORY where the line being transcribed to output has its scan interrupted by the replacement text, which is scanned for replacements, after which the interrupted scan is resumed. The earlier version without this extension merely scanned each line character-by-character via a DO-loop, using the bounds of the text being scanned. Some compilers allow the index and bound of a DO-loop to be adjusted during the progress of the loop (as when the scan of one text would be interrupted by a new scan of a replacement) however many do not and some even declare such attempts an error. So instead a DO WHILE statement is used. Older Fortrans would of course lack such constructions, and so many GO TO statements would appear.
 
Output is a problem. Languages such as Pascal have their default action after a WRITE statement of not starting a new line, so pieces of text could be written one after another. Fortran is the other way around, always ending the line. This could be countered with lineprinter output through the use of "overprint" options, but such piecemeal hammering at a line would be frowned upon. Later Fortrans offer gibberish to achieve continued-style output, but instead, the plan here is to place the pieces in an output area, the unused space in STASH being the obvious candidate. This alas means that texts have to be copied into place rather than being written to output directly from their storage area. But it also makes it easy to improve the layout: lines can be written no longer than some limit with breaks at the spaces between words. Further, a very simple protocol can be used to preserve paragraphs. Each line of the story is deemed a continuation of the previous line's text (and a space is supplied to separate the last word's end from the start of the next line's first word), however, should the incoming line start with a space, it is deemed to mark the start of a new paragraph: existing text is flushed and the new line of input starts a new line of output, thus preserving the indentation marking a paragraph. Unfortunately, web pages routinely employ blank lines between paragraphs, which should only appear between sections.
 
An example of the resulting opportunities for confusion:
Line 768 ⟶ 770:
Here goes... Reading file Madlib.txt
 
Enter your text for <name>: JoeJoseph the <adjective>
Enter your text for <he or she>: He was poor but <adjective> and
Enter your text for <adjective>: the lucky
Enter your text for <noun>: high-value banknote with "<name>" written on it
 
Righto!
 
JoeJoseph the lucky went for a walk in the park. He was poor but
lucky and found a high-value banknote with Joe"Joseph the lucky written on it. Joe the lucky decided to take it home."
written on it. Joseph the lucky decided to take it home.
 
<lang Fortran>
Line 906 ⟶ 909:
END SUBROUTINE READSTORY!Simple enough, anyway.
 
SUBROUTINE WRITESTORY(WIDTH) !Applying the replacements, with replacement replacement too.
Co-opts the as-yet unused space in STASH as its output scratchpad.
Can't rely on changing the index and bounds of a DO-loop on the fly.
INTEGER WIDTH
INTEGER LINE,IT,I,J !Steppers.
INTEGER L,L0,N !Fingers.
Line 914 ⟶ 918:
INTEGER SP,STACKI(STACKLIMIT),STACKL(STACKLIMIT) !Ah, recursion.
L0 = ISTASH(NSTASH + 1) !The first available place in the stash.
L = L0 - 1 !Syncopation for my output finger.
LL:DO LINE = 1,NSTORY !Step through the lines of the story.
L = L0 - 1 !Syncopation for my output finger.
SP = 0 !Start with the task in hand.
IT = STORY(LINE) !Finger the stashed line.
Line 921 ⟶ 925:
I = ISTASH(IT) !Find its first character in STASH.
TAIL = I - 1 !Syncopation. No text from this line yet.
IF (STASH(I:I).LE." ") THEN !The line starts with a space?
CALL BURP !Yes. Flush, so as to start a new paragraph.
ELSE IF (LINE.GT.1) THEN !Otherwise, the line is a continuation.
L = L + 1 !So, squeeze in a space as a separator.
STASH(L:L) = " " !Since its text follows on.
END IF !Now for the content of the line.
666 II:DO WHILE(I.LE.LAST) !Step along its text.
IF (STASH(I:I).EQ."<") THEN !Trouble starter?
Line 955 ⟶ 965:
END IF !But if all is unstacked,
CALL APPEND(TAIL + 1,LAST) !Don't forget the tail end.
WRITE (MSG,"(A)") STASH(L0:L) !Yay!
END DO LL !On to the next story line.
CONTAINS CALL BURP !AnAny waiting text must assistant,be definedless afterthan usage..WIDTH.
CONTAINS !Some assistants, defined after usage...
SUBROUTINE APPEND(IST,LST) !Has access to L.
INTEGER IST,LST !To copy STASH(IST:LST) to the scratchpad.
Line 966 ⟶ 976:
STASH(L + 1:L + N) = STASH(IST:LST) !There they go.
L = L + N !Advance my oputput finger.
IF (L - L0 + 1.GE.WIDTH) CALL BURP !Enough to be going on with?
END SUBROUTINE APPEND !Few invocations, if with tricky parameters.
SUBROUTINE BURP !Flushes forth up to WIDTH characters.
INTEGER N,W,L1 !And slides any remnant back.
N = L - L0 + 1 !So, how many characters are waiting?
IF (N.LE.WIDTH) THEN !Too many for one line of output?
L1 = L !Nope. Roll the lot.
ELSE !Otherwise, a partial flush.
W = L0 + WIDTH - 1 !Last character that can be fitted into WIDTH.
DO L1 = W,L0,-1 !Look for a good split.
IF (STASH(L1:L1).LE." ") EXIT !Like, at a space.
END DO !Keep winding back.
IF (L1.LE.L0) L1 = W !No pleasing split found. Just roll a full width.
END IF !Ready to roll.
WRITE (MSG,"(A)") STASH(L0:LL1) !YayThus!
IF (N.LE.WIDTH) THEN !If the whole text was written,
L = L0 - 1 !Then there is no text in the scratchpad.
ELSE !If only L0:L1 were written of L0:L,
W = L0 + L - L1 - 1 !How far will the remaining text extend?
STASH(L0:W) = STASH(L1 + 1:L) !Shift it.
L = W !Finger the last used character position.
END IF !One trim is enough, even if the scracchpad contains multiple widths' worth..
END SUBROUTINE BURP !Since I don't want to flush the lot.
END SUBROUTINE WRITESTORY !Just a sequence of lines.
END MODULE MADLIB !Enough of that.
Line 973 ⟶ 1,005:
USE MADLIB
WRITE (MSG,1) !It's polite to explain.
1 FORMAT10FORMAT ("Reads a story in template form, containing special ",
21 "entries such as <dog's name> amongst the text.",/,
32 "You will be invited to supply a replacement text for each "
43 "such entry, as encountered,",/,
54 "after which the story will be presented with your ",
65 "substitutions made.",//,
6 "Here goes... Reading file Madlib.txt",/)
OPEN(INF,STATUS="OLD",ACTION="READ",FORM="FORMATTED",
Line 987 ⟶ 1,019:
WRITE (MSG,*) " Righto!"
WRITE (MSG,*)
CALL WRITESTORY(66)
END</lang>
</lang>
 
=={{header|Go}}==
1,220

edits