5

Disclaimer: This question is not intended to serve a general need. It is not even a question that serves my own need, or anyone else's "need". In Just a suggestion for a neat use and throw macro.

While working on template LaTeX files for my students and I, I thought it would be neat to write:

Write \Lipsum 300 sentences.

The macro \Lipsum expects a number and a terminating sequence. In the above, the number is integer 300. The terminating sequence is always the same: sentences., i.e., the tokens' sequence: "s", "e", ..., "e", "s", ".".

The macro will expand to lipsum text of the specified number of sentences. It would be nice if this text would not be always the same.

  • 1
    What's wrong with \lipsum[1-3] from package lipsum? Or is this question about how to defin a macro with a special syntax? – Tobi May 4 '16 at 6:07
  • this is related to random numbers without repetition. try to make a loop: for i=1->300 get a random number xi and write \lipsum[xi] or \kant[xi] – touhami May 4 '16 at 6:36
  • This looks very good. Another direction would be using Python to pull from this site any number of words, sentences or paragraphs:en.lipsum.com – Yossi Gil May 6 '16 at 12:25
  • @YossiGil: Forgot to mention, that my second implementation now has \AddToLipsumPool{ to add sentences, which you might use in combination with python … – Tobi May 6 '16 at 18:05
8

Here’s a possible solution using the power of PGF:

\documentclass{article}

\usepackage{pgffor}

\def\getRandomSentence{%
   \pgfmathparse{random(5)}%
   \ifcase\pgfmathresult\relax
      Sentence 1.
   \or
      Sentence 2.
   \or
      Sentence 3.
   \or
      Sentence 4.
   \or
      Sentence 5.
   \else
      Default Sentence.
   \fi
}

\def\Lipsum#1 sentences.{%
   \ifnum#1<1
      ERROR!
   \else
      \foreach \n in {1,...,#1} {%
%         (\n)
         \getRandomSentence
      }%
   \fi
   \ignorespaces
}

\begin{document}
Test A:
Write \Lipsum 1 sentences. Test

Test B:
Write \Lipsum 10 sentences. Test

Test C:
Write \Lipsum 0 sentences. Test
\end{document}

Just add an appropriate number of sentences and change the number in random(5) accordingly. \Lipsum test wether the number of sentences is bigger or equal to 1 and writes “ERROR” if not.

In \Lipsum 1 sentences. the space before the number is optional while the space following the number is mandatory like sentences..


However I’d suggest using a normal syntax like \LipsumSentences{300}.

  • 1
    It would be nice if the macro actually extracted random lipsum sentences from some canonical text. – Yossi Gil May 6 '16 at 5:19
  • 1
    You can just add the text in the definition … otherwise: how does the text look like, how are the sentences separated? Just by dots? – Tobi May 6 '16 at 7:05
10

I explained this implementation in more detail in my blog: http://tobiw.de/tbdm/blindtext (in German only)

Update:

The implementation now tries to prevent repeating a sentence (and punctuation) immediately. The maximum number of tries to get a different senses is set to 10, but can be changed in the code. Furthermore the debug info is a little better, and is now also written to the log file (terminal).


Here’s another implementation using LaTeX3 function. In this solution it is possible to set a pool of sentences with \SetLipsumPool and a pool of punctuation marks with \SetPunctPool.

To generate text use

\LipsumI <N> sentences.

or

\LipsumII{<N>}

I’d use the latter …

Code:

\documentclass{article}

\usepackage{pgffor,xparse}

\ExplSyntaxOn

% pool for sentences
\seq_new:N \g_yossi_lipsum_pool_seq
% count of sentences in pool
\int_new:N \g_yossi_lipsum_pool_int
% numbers for generating a random number
\int_new:N \g_yossi_current_int
\int_new:N \g_yossi_tries_int
\int_new:N \g_yossi_last_lipsum_int
\int_new:N \g_yossi_last_punct_int
% number of max tries to get a differnet random number than last on
\int_new:N \c_yossi_max_tries_int
\int_set:Nn \c_yossi_max_tries_int { 10 }
% pool for punctuation
\seq_new:N \g_yossi_punct_pool_seq
% count of punctuation marks in pool
\int_new:N \g_yossi_punct_pool_int
% boolean for debug mode
\bool_new:N \c_yossi_debug_bool
%\bool_set_true:N \c_yossi_debug_bool% debug mode on

\cs_new:Npn \yossi_add_to_lipsum_pool:n #1 {
   % set from argument by spliting at dots
   \seq_gset_split:Nnn \tmpa_seq { . } { #1 }
   \seq_gconcat:NNN \yossi_lipsum_pool_seq \yossi_lipsum_pool_seq \tmpa_seq
   % remove the duplicates
   \seq_gremove_duplicates:N \yossi_lipsum_pool_seq
   % remove emtpy sentences
   \seq_remove_all:Nn \yossi_lipsum_pool_seq { }
   % save number of sentences in pool
   \int_gset:Nn \g_yossi_lipsum_pool_int { \seq_count:N \yossi_lipsum_pool_seq }
}

% user macro to set up the pool
\NewDocumentCommand { \SetLipsumPool } { m } {
   \seq_gclear:N \yossi_lipsum_pool_seq
   \yossi_add_to_lipsum_pool:n { #1 }
}

% user macro to add sentences to the pool
\NewDocumentCommand{ \AddToLipsumPool }{ m }{
   \yossi_add_to_lipsum_pool:n { #1 }
}

% user macro to set up the pool
\NewDocumentCommand { \SetPunctPool } { m } {
   % set from argument
   \seq_gset_split:Nnn \g_yossi_punct_pool_seq { } { #1 }
   % save number of puctuation marks in pool
   \int_gset:Nn \g_yossi_punct_pool_int { \seq_count:N \g_yossi_punct_pool_seq }
}

\cs_new:Npn \yossi_get_random_number:NN #1#2 {
   \int_gzero:N \g_yossi_tries_int
   \bool_do_while:nn {
      \int_compare_p:n { #2 = \g_yossi_current_int }
      &&
      \int_compare_p:n { \g_yossi_tries_int < \c_yossi_max_tries_int }
   } {
      \pgfmathparse { random(1,#1) }
      \int_gset:Nn \g_yossi_current_int { \pgfmathresult }
      \int_gincr:N \g_yossi_tries_int
      \iow_term:x {
         [
            find ~ random ~ number: ~
            curr = \int_use:N \g_yossi_current_int \c_space_tl
            last = \int_use:N #2 \c_space_tl
            try = \int_use:N \g_yossi_tries_int
         ]
      }
   }
   % debug info
   \bool_if:NT \c_yossi_debug_bool {
      \newline (
      get ~ random ~ number: ~
      current\,=\,\int_use:N \g_yossi_current_int ; ~
      last\,=\,\int_use:N #2 ; ~
      tries\,=\,\int_use:N \g_yossi_tries_int )
      \newline
   }
   \int_gset_eq:NN #2 \g_yossi_current_int
}

% internal macro to get random sentecs
\cs_new_protected:Nn \yossi_get_random_sentence: {
   % get random sentence from pool
   \yossi_get_random_number:NN \g_yossi_lipsum_pool_int \g_yossi_last_lipsum_int
   \seq_item:Nn \yossi_lipsum_pool_seq { \g_yossi_current_int }
   % add random punctiation
   \yossi_get_random_number:NN \g_yossi_punct_pool_int \g_yossi_last_punct_int
   \seq_item:Nn \g_yossi_punct_pool_seq { \g_yossi_current_int }
   % add space
   \c_space_tl
}

% user macro to get #1 sentences
\def\LipsumI#1~sentences. {%
   \iow_term:x {
      ===================================== ^^J
      START LIPSUMS ^^J
      -------------------------------------
   }
   % test if ' is bigger than 1
   \int_compare:nTF { #1 < 1 } {% TRUE - #1 is smaller: Error
      ERROR!
   } {% FALSE - #1 is bigger: loop #1 times
      \foreach \n in {1,...,#1} {%
         % debug info
         \bool_if:NT \c_yossi_debug_bool {
            \par\bigskip
            \fbox{\textbf{n\,=\,\n}}
         }
         \iow_term:x {
            \n . ~ SENTENCE
         }
         % get random sentence (incl. punct and space)
         \yossi_get_random_sentence:
      }%
   }
   \iow_term:x {
      ------------------------------------- ^^J
      END LIPSUMS ^^J
      =====================================
   }
   % ignore spaces (on was added by \yossi_get_random_sentence: already)
   \ignorespaces
}

% alternative user macro with normal syntax
% (copy the implementation if you want to use this version)
\NewDocumentCommand{ \LipsumII } { m } {
   \LipsumI #1~sentences.
}


\ExplSyntaxOff

% preset pools
\SetLipsumPool{Lorem ipsum dolor sit amet, consectetur adipiscing elit.}
\SetPunctPool{.}

\begin{document}
% set pools
\SetLipsumPool{
   Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis lectus elit,
   tempus quis efficitur ut, consequat sed lorem. Cras convallis et nibh id
   accumsan.
}
\SetPunctPool{........:?!}% make dot more likely

Write \LipsumI 1 sentences. Test

Write \LipsumI 10 sentences. Test

Write \LipsumI 0 sentences. Test

\AddToLipsumPool{Another senseless sentence.}

Write \LipsumI 10 sentences. Test

\AddToLipsumPool{
   Pellentesque mollis, magna sed placerat mattis, quam nisl euismod dui, et
   sollicitudin orci lacus ac dolor. Phasellus pretium, purus ac lobortis
   mattis, leo libero lobortis ligula, ut rhoncus mauris purus at odio. Morbi
   vitae mollis ipsum. Morbi risus augue, feugiat quis posuere a, dapibus sed
   arcu. Fusce pharetra massa vitae tristique auctor. Sed sagittis placerat
   vestibulum. Curabitur a tempus arcu, eget ullamcorper urna. Maecenas
   malesuada placerat enim, sit amet commodo risus scelerisque vel. Sed vel
   feugiat est. Pellentesque habitant morbi tristique senectus et netus et
   malesuada fames ac turpis egestas. Praesent rhoncus facilisis quam in
   faucibus.
}

Write \LipsumI 10 sentences. Test

\SetLipsumPool{Only one sentence}

Write \LipsumI 10 sentences. Test

\bigskip
Write \LipsumII{5} Test
\end{document}

Note:
This implementation also includes a rudimentary debug mode.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.