The Parodist, which I’ll be describing here, is a small database application that creates parodies of whatever book you feel inclined to feed into it. I offer you this little script for your amusement as a Christmas present. It may even deliver you your next homework essay in postmodernism, or your next marketing White-paper.
This could be quite a productivity tool. Who would quarrel with these profound words about XML and PHP?
An element may also be defined using annotated schema. A plan is expensive or inexpensive. The feature can be indexed. The best choice of XML Data type still remains.
Complete each box for two expressions that are simple to implement. When your data is highly structured, XML data for example, users may want to use the INSERT workload and between 10 and 2. The LSNs still exist in a PHP resource corresponding to the change table after the load on the production workload.
You can refer to the subtype table has a very simple example and it is divided in groups of 64K records called a segment. Note the first and last names of actual companies and products mentioned herein may be used in Example: View and a set is best introduced by contrasting it with a sum measure on the XML schema collection myCollection” within relational schema for your business needs. None of these conditions are true:
For more information about thread settings In addition to that log records to be configured at the leaves and aggregate several partitions at the time of the sections Relevant Wait Types for Subscriptions SQL Server 2008 Analysis Services will scan and aggregate the segments of one partition at a small number of concurrent threads that perform the SWITCH. Optionally a primary key from the example below.
… except that it is gibberish produced by The ‘Parodist’. Still, it is compelling gibberish, isn’t it.
This might look to you something like Homer’s Iliad, but it isn’t ….
Nor yet e’en so Achilles let his counsel take
who should strike the bird
As less in skill not one was absent;
nor of his lofty vessel’s prow The dark whirlwind’s force
as this wine we pour their hearts’ best blood
Theirs and their fam’d Allies Who freely in thy house
receiv’d For twenty days the heav’nly Maid Daughter of Cisseus
sage Antenor’s wife By Trojans built of fir-trees fell’d
and overlaid the roof With rushes mown from off a fly;
Its course she so directed that it struck but drove not through
For near the warlike Mysians next Antilochus From Mermerus and Phalces
stripp’d their armour spoil’d shalt make thy pray’r
I seem to be in the habit of producing frivolous applications at this time of year. Once, I did a SQL Server version of the classic ‘Waffle Generator’. Several readers asked for something just as clever but less restricting, so that one could get waffle from any text. Fine, I thought, time to implement a finite-state Markov chain with stationary transition possibilities. Actually, the process isn’t as scary as the name. The algorithm was described in 1948, long before personal computers, by Claude Shannon, first in The Bell System Technical Journal, Volume XXVII, No. 3 & 4, and then in The Mathematical Theory of Communication ( ISBN: 9780252725487). The best description I know of is in Jon Bentley’s ‘Programming Pearls’, in the chapter ‘Strings of Pearls’.
It may seem surprising that a technique like this preceded the Age of Information Technology, but we were aware of them whilst they were too long-winded to implement by a manual process. One of the great experiences of IT in the 1970s was the sheer pleasure of trying out ideas that were known about, but were impractical until computers were cheap enough to have in the home. With pencil and paper, these were mere party games, but with computers, they became algorithms.
If you had the patience of a saint, you could do this manually. You would open a book at random, put your finger on the page randomly, and track along to the start of the next sentence, You have the starting word of a sentence: Then, you would find the second word in another sentence that started with that initial word. Once you had the third word, by proceeding likewise, you would be ready to start. You find, at random, a sentence containing the previous two words, adjacent and in the same order; and you take this third word and append it. As authors tend to have mannerisms, and use favourite phrases, this technique seems to pick them up: One gets clichés strung together. The more text you have the better the parody. with small amounts, it just sticks to the sentences it selects, but the richer the text bank, the more creative it gets. See what it does with Mark Twain’s Huckleberry Finn
“Why it’s perfectly ridiculous, but I couldn’t cut the bullet and twice I seen where one of his pocket. Then it was here again for I was used to rip around so and swallow the sawdust so it was about done loading and pretty soon I noticed the king made me creep.
They was off working for the fields; and Jim’s eyes bugged out and says: “Why yonder he is, and if we’d a’ whooped him over to t’other village without any handle; and a roundabout and pants of his father would die first”. But Tom said “Now for the shore, a little twenty-foot yard. My! you ought to seen old Henry the Eight; when he said the duke’s room, and hustling away and leave my sisters with them”. A witch PIE in Jim’s pan and we got some of the river but didn’t look at it. What could he ever undertook that Tom and I’ll do it. “
You might have thought that such a powerful parodist as this would consist of a great deal of code. No, actually, it doesn’t because it is doing what SQL Server does best, which is to search and sort vast quantities of data. So without further introduction here is the code, which should speak for itself. You can find the original source in the downloads at the bottom of the article.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
IF OBJECT_ID(N'WordChop') IS NOT NULL DROP FUNCTION WordChop GO CREATE FUNCTION [dbo].[WordChop] (@string VARCHAR(MAX)) RETURNS @Results TABLE (SequenceNumber INT IDENTITY(1, 1), Item VARCHAR(100), Terminator CHAR(1)) /** summary: > This Table-valued function takes any text as a parameter and splits it into its constituent words, passing back the order in which they occured and also the character (usually space) that terminated the word. Author: Phil Factor Revision: 1.0 date: 20 Dec 2010 example: - code: SELECT * FROM WordChop('this tests stuff. Will it work?') - code: SELECT * FROM WordChop('this ------- tests it again; Will it work ...') returns: > Table of SequenceNumber, Item (word) and Terminating character. **/ AS BEGIN DECLARE @LengthOfString INT,--the length of the entire string @Start INT, --the index from the Cursor to the start of the next word @Cursor INT,--the current index into the entire string @LengthOfWord INT --the length of the current word SELECT @Cursor = 1, @LengthOfString = LEN(@string); WHILE @Cursor < @LengthOfString BEGIN --first we find the transition to the start of the word SELECT @start = PATINDEX('%[^A-Za-z0-9][A-Za-z0-9]%', ' ' + SUBSTRING(@string, @Cursor, 100)) - 1; IF @start < 0 BREAK --we've done them all if we cant find any more --otherwise we dind the transition point to the end of the string SELECT @LengthOfWord = PATINDEX('%[^A-Z''a-z0-9-]%', SUBSTRING(@string, @Cursor + @start + 1, 50) + ' ') ; --put the string and the Terminating character into the column INSERT INTO @results (Item, Terminator) SELECT SUBSTRING(@string, @Cursor + @start, @LengthOfWord), SUBSTRING(@string, @Cursor + @start + @LengthOfWord, 1); SELECT @Cursor = @Cursor + @Start + @LengthOfWord + 1; --bump the cursor on END RETURN END GO IF OBJECT_ID(N'LoadBook') IS NOT NULL DROP PROCEDURE LoadBook GO CREATE PROCEDURE LoadBook @NameAndPathOnServer VARCHAR(255) /** summary: > This Procedure loads a file into a variable, chops it up into its constituent words and loads it into a global temporary table for subsequent analysis. It creates a second global temporary table to hold the unique list of words used, and the number of times each word was used. In the analysis table, it records the ids of the two preceeding words in the sentence, or zero if there aren't any. Author: Phil Factor Revision: 1.0 date: 20 Dec 2010 example: - code: EXECUTE LoadBook 'MyDirectory\KJB.txt' - code: EXECUTE LoadBook 'MyDirectory\Dracula.txt' - code: EXECUTE LoadBook 'MyDirectory\SherlockHolmes.txt' - code: EXECUTE LoadBook 'MyDirectory\CanterburyTales.txt' - code: EXECUTE LoadBook 'MyDirectory\HomersIliad.txt' - code: EXECUTE LoadBook 'MyDirectory\CompleteWilliamShakespeare.txt' - code: EXECUTE LoadBook 'MyDirectory\HuckleberryFinn.txt' - code: EXECUTE LoadBook 'MyDirectory\BrerRabbit.txt' returns: > 0 if successful. **/ AS DECLARE @LotsOfText VARCHAR(MAX), @Command NVARCHAR(MAX) /* We want to read the text file in. Microsoft makes it very hard to use the OpenRowset Bulk with a supplied parameter as a local variable but are we at all discouraged? No Sir, we do a bit of sp_execute.*/ SELECT @Command = 'SELECT @Filecontents = BulkColumn FROM OPENROWSET(BULK ''' + @NameAndPathOnServer + ''', SINGLE_BLOB) AS x' EXECUTE master..sp_executeSQL @Statment = @Command, @params = N'@FileContents VARCHAR(MAX) OUTPUT', @Filecontents = @LotsOfText OUTPUT; /* Read each word into a table (we make it a global temporary table as we don't want to keep it for ever!*/ IF EXISTS ( SELECT * FROM tempDB.sys.tables WHERE name LIKE N'##Word' ) DROP TABLE ##Word; /* This gives each word in order, and records the non-letter character that terminates the word. the identity of the two preceding words is added later as is the unique id of the word. */ CREATE TABLE ##Word (SequenceNumber INT NOT NULL PRIMARY KEY, Item VARCHAR(100) NOT NULL, Terminator CHAR(1) NOT NULL,--character Terminating the word Terminating CHAR(1) NOT NULL DEFAULT 'n',--is it Terminating a sentence? word_ID INT NULL, PreviousWord_ID INT NOT NULL DEFAULT 0, PluPreviousWord_ID INT NOT NULL DEFAULT 0); /* now we put all the words from the file, in order, into this table */ INSERT INTO ##Word (SequenceNumber, Item, Terminator) SELECT SequenceNumber, Item, Terminator FROM WordChop(@LotsOfText); -- clear out any previous work of literature IF EXISTS ( SELECT * FROM tempDB.sys.tables WHERE name LIKE N'##UniqueWords' ) DROP TABLE ##UniqueWords; --and recreate the table CREATE TABLE ##UniqueWords (Word_ID INT IDENTITY(1, 1) PRIMARY KEY, Word VARCHAR(100) NOT NULL, wordCount INT); --we will want the indexes of the two previous words to add to the table. INSERT INTO ##UniqueWords (Word, wordCount) SELECT Item, COUNT(*) FROM ##Word GROUP BY Item ORDER BY Item; --now we indicate whether the word terminates a sentence. UPDATE ##Word SET Word_ID = ##UniqueWords.Word_ID, Terminating = CASE WHEN Item IN ('Mrs', 'Ms', 'Mr') THEN 'n' WHEN ISNUMERIC(Item) = 1 THEN 'n' WHEN Terminator IN ('.', ':') THEN 't' ELSE 'n' END FROM ##Word INNER JOIN ##uniqueWords ON Word = Item; DECLARE @PreviousWord_ID INT, @PluPreviousWord_ID INT, @Word_ID INT, @Terminating CHAR(1) SELECT @PreviousWord_ID =0,@PluPreviousWord_ID =0, @Word_ID =0; --this is wicked, but it is fast and we have, after all, defined a primary key UPDATE ##word SET @PluPreviousWord_ID = PluPreviousWord_ID=CASE when @Terminating='t' THEN 0 ELSE @Previousword_ID END, @PreviousWord_ID = previousWord_ID=CASE when @Terminating='t' THEN 0 ELSE @word_ID END, @Word_ID=Word_ID, @Terminating=Terminating; CREATE INDEX WhatsGoneBefore ON ##Word(PluPreviousWord_ID, PreviousWord_ID); GO IF OBJECT_ID(N'CreateSentence') IS NOT NULL DROP PROCEDURE CreateSentence GO CREATE PROCEDURE CreateSentence @TheSentence VARCHAR(MAX) OUTPUT /** summary: > This Procedure creates a sentence from the ##words table. It does this by choosing a sentence at random from the bank, and takes the unitial word. Then it finds, randomly, a second word that follows that first word at the start of a sentence. After it has got a sentence start, it then repeatedly finds, randomly, a word that follows the two preceding ones until it reaches a word that is a sentence end. then it quits. Author: Phil Factor Revision: 1.0 date: 20 Dec 2010 example: - code: > DECLARE @Sentence VARCHAR(MAX) EXECUTE CreateSentence @Sentence OUTPUT SELECT @Sentence returns: > 0 if successful. Returns the sentence as an output variable **/ AS SET NOCOUNT on DECLARE @PreviousWord_ID INT, @PluPreviousWord_ID INT, @SentenceStart INT, @Word VARCHAR(100), @Iterator INT, @SentenceEnd CHAR(1), @Word_ID INT, @Terminator CHAR(1); DECLARE @SentenceOrder TABLE --used to store the sentence as it is assembled (sequence_ID INT IDENTITY(1, 1), word_ID INT NOT NULL, word VARCHAR(100), Terminator CHAR(1) NOT NULL); IF NOT EXISTS ( SELECT * FROM tempDB.sys.tables WHERE name LIKE N'##Word' ) BEGIN RAISERROR('I''m sorry but you will need to load a text bank to do this using the LoadBook procedure',16,1) RETURN 1 END SELECT @Iterator=20 ; WHILE @PreviousWord_ID IS NULL and @Iterator>0 BEGIN--eliminate one word sentences DELETE FROM @SentenceOrder; SELECT TOP 1 --choose a sentence-starting word at random @PluPreviousWord_ID = Word_ID, @Word = Item, @Terminator = Terminator FROM ##Word WHERE PreviousWord_ID = 0 AND PluPreviousWord_ID = 0 AND Terminating = 'n' ORDER BY NEWID(); INSERT INTO @SentenceOrder (word_ID, word, Terminator) SELECT @PluPreviousWord_ID, @word, @Terminator; SELECT TOP 1 --choose a word that follows the one we have found at the --start of a sentence. @previousWord_ID = Word_ID, @Word = Item, @Terminator = Terminator FROM ##Word WHERE PreviousWord_ID = @PluPreviousWord_ID AND PluPreviousWord_ID = 0 AND Terminating = 'n' ORDER BY NEWID(); SELECT @Iterator=@Iterator-1; --so we haver an exit in a sentence-less piece of literature END IF @Iterator=0 BEGIN RAISERROR ('Words table unprocessed, or wordbank without punctuation', 16,1) RETURN 1 ; END INSERT INTO @SentenceOrder (word_ID, Word, Terminator) SELECT @PreviousWord_ID, @Word, COALESCE(@Terminator, ' ') /* now keep building up words until we reach the end of a sentence */ SELECT @Iterator = 100, @SentenceEnd = 'n'; WHILE @SentenceEnd = 'n' AND @Iterator > 0 BEGIN SELECT TOP 1 @Word_ID = Word_ID, @Word = Item, @Terminator = Terminator, @SentenceEnd = Terminating FROM ##Word WHERE PreviousWord_ID = @PreviousWord_ID AND PluPreviousWord_ID = @PluPreviousWord_ID ORDER BY NEWID() ; IF @@Rowcount = 0 BEGIN RAISERROR ('this is impossible; but has happened with words: %o and %o', 16,1,@PreviousWord_ID,@PluPreviousWord_ID) BREAK ; END INSERT INTO @SentenceOrder (word_ID, Word, Terminator) SELECT @Word_ID, @Word, @Terminator; SET @PluPreviousWord_ID = @PreviousWord_ID; SET @PreviousWord_ID = @Word_ID; SET @Iterator = @Iterator - 1; END SELECT @TheSentence = ( SELECT CAST(word + CASE WHEN Terminator IN ('.', ':',';', '-', '"', '''') THEN Terminator + ' ' ELSE ' ' END AS VARCHAR(MAX)) FROM @SentenceOrder ORDER BY Sequence_ID FOR XML PATH('') ) GO IF OBJECT_ID(N'CreateSomeSentences') IS NOT NULL DROP PROCEDURE CreateSomeSentences GO CREATE PROCEDURE CreateSomeSentences @HowMany INT =null /** summary: > This Procedure creates a number of sentences from the ##words table. It calls the routine that does the real work 'CreateSentence' repeatedly until it has gotten the correct number of sentences. The default is 16. Author: Phil Factor Revision: 1.0 date: 20 Dec 2010 example: - code: Execute CreateSomeSentences - code: Execute CreateSomeSentences 24 returns: > 0 if successful. Returns the sentence as an output variable */ as DECLARE @Sentence VARCHAR(MAX) DECLARE @Text VARCHAR(MAX) DECLARE @Iterator INT SELECT @Iterator = COALESCE(@HowMany,16) IF NOT EXISTS ( SELECT * FROM tempDB.sys.tables WHERE name LIKE N'##Word' ) BEGIN RAISERROR('I''m sorry but you will need to load a text bank to do this using the LoadBook procedure',16,1) RETURN 1 END WHILE @Iterator > 0 BEGIN EXECUTE CreateSentence @Sentence OUTPUT; SELECT @Iterator = @Iterator - 1, @Text = COALESCE(@Text + ' ', '') + CASE WHEN RAND() < 0.3 THEN CHAR(13) + CHAR(10) ELSE '' END + @Sentence; END SELECT @Text; GO /* to use this application, you just create a database called 'parodist'. Then you load the book, or bank of sentences you want to parody like this: EXECUTE LoadBook 'MyDirectory\DraculaAndFrankenstein.txt' --Then you just get the parody text by calling EXECUTE CreateSomeSentences And here is the result of mixing two original wordbanks! 'Then we made a few hours before. I sent them over and read Sacred to the drawing room where we had all gathered round the room; he came out from under the eye can have departed forever--that the brightness of a servant, a condition precarious, and full today with a gesture placed us each in position. In accordance with instructions and keys left in pretty good time but when we closed the door was unlocked and now I think I put him in his sight. The spirits who assist my vengeance in his dressing gown and Mr. Morris Lord Godalming tells me her teeth are sharper and at a loss for words to describe--gigantic in stature yet uncouth and inarticulate sounds which broke from all littleness of feeling and delight than in public for there must be brave of heart. I opened another door in orthodox fashion putting the key in the long hours and felt somewhat safer in all. We can only trust in the firelight and they will soon think that of all these things yet he may purchase does not understand even in her. I visited him myself. Hear me! Let me tell you that the Honfoglalas was completed there And it is the second time was solely directed towards the south. But now he make in the room gently opened the door and seeing where she had not yet begun to take into this place or the study and I have confessed myself guilty of the fair moon for a moment on the floor. In fine if he go in lest I should have been thinking that tomorrow will see the tension of her head and bore all the way that I at length she formed her determination. The starry sky the sea it was it indeed such spiritual intensity that her imagination is beginning to increase my misery you will but take the glass. I had only the solid advantage of their miserable fare. I had visited her and whispers to me in he threw from time to save poor Lucy. Those are as fixed as fate and my loud unrestrained heartless laughter frightened and amazed me somewhat. He was sweetly courteous. ' */ |
If you need any more convincing, here is Shakespeare, put through the mangle
“Here’s ado to make my peace. I pray you tarry; longer I shall make it flame again; for more I prithee more. As by his small light of discretion that he should escape; For though she’s as fartuous a civil doctor which did put his cause being her uncle Rivers; Talk’d how I do not know DROMIO OF SYRACUSE. My lord Northumberland What says his Majesty bade me from the rebels.
In honest civil godly company for speedy aid; wherein our dearest friend Prejudicates the business is not a son being twain. These are they. Ay sir; a word I pray. Enough sweet Suffolk thou torment’st thyself; And I am glad I am not well; What has he that did haunt me still. Take that winter kill it.
Mine such as doth an inland man; one fire drives out one fire; one nail, one nail, one nail; Rights by me be brief my practice quit his pain. No leisure had he none but libertines delight in them is hereabout And cannot help him; young and though his false finger have profan’d the ring And look upon The Queen; come not by But stay here for defence For courage mounteth with occasion.
Love they flatter me; that can I fortune to the Capitol. Peter didst ever hear better. That will not be wink’d at how shall I be afeard. Pardon dear madam. But if he were dead I’ll speak with Coriolanus.“
And what about Brer Rabbit?
“Folks tell um fer me bittle. Whar I ain’t gwine ter stan’ inside en Brer Rabbit he tuck’n stan’ on one side. What are they going to Uncle Remus’s anger was all pretence but he wuz gwine long down de road. I done say I got you dis night en ef you’d a’ bin mighty smart man.
Dat’s so en Brer Rabbit he kotch holt er de flo’ un git all de folks on dis a-way you’ll des keep yo’ eye on de trees look like it mought work. I wonder who put um in de min’ Brer Wolf run fas’ Brer Rabbit en Mr.Man cut tree down. Skin swink skin stink skin swivel. Wid dat Brer Rabbit sezee w’ich ef he don’t year no complaints fum me.”
So I leave you to do your own experimentation, fuelled perhaps from the vast banks of text in Project Gutenberg, or the rich comedy of marketing papers. Make sure that it consists of pure sentences terminated by full-stops (periods). Take out all extraneous text. Try experiments such as mixing two different authors. Why not change the algorithm, or try another variation of the Markov algorithm. After all, SQL Server Developments can, and occasionally should, be fun.
Load comments