23

I am using the algorithmic package to typeset an algorithm. I would like to have connecting lines between the beginning and end of a loop or conditional like in the algorithm2e package. Is there an easy way to accomplish this, or am I better off switching to algorithm2e?

Werner
  • 603,163

1 Answers1

23

Here is one way of doing it using algorithmicx - more advanced yet similar to and compatible with algorithmic. It involves expanding and contracting a token list \thelines that keeps track of the nested level. At every new level, an additional \theline is added to \thelines.

enter image description here

\documentclass{article}
\usepackage{algorithm}% http://ctan.org/pkg/algorithm
\usepackage{algpseudocode}% http://ctan.org/pkg/algorithmicx

\makeatletter
% This is the vertical rule that is inserted
\def\therule{\makebox[\algorithmicindent][l]{\hspace*{.5em}\vrule height .75\baselineskip depth .25\baselineskip}}%

\newtoks\therules% Contains rules
\therules={}% Start with empty token list
\def\appendto#1#2{\expandafter#1\expandafter{\the#1#2}}% Append to token list
\def\gobblefirst#1{% Remove (first) from token list
  #1\expandafter\expandafter\expandafter{\expandafter\@gobble\the#1}}%
\def\LState{\State\unskip\the\therules}% New line-state
\def\pushindent{\appendto\therules\therule}%
\def\popindent{\gobblefirst\therules}%
\def\printindent{\unskip\the\therules}%
\def\printandpush{\printindent\pushindent}%
\def\popandprint{\popindent\printindent}%

%      ***      DECLARED LOOPS      ***
% (from algpseudocode.sty)
\algdef{SE}[WHILE]{While}{EndWhile}[1]
  {\printandpush\algorithmicwhile\ #1\ \algorithmicdo}
  {\popandprint\algorithmicend\ \algorithmicwhile}%
\algdef{SE}[FOR]{For}{EndFor}[1]
  {\printandpush\algorithmicfor\ #1\ \algorithmicdo}
  {\popandprint\algorithmicend\ \algorithmicfor}%
\algdef{S}[FOR]{ForAll}[1]
  {\printindent\algorithmicforall\ #1\ \algorithmicdo}%
\algdef{SE}[LOOP]{Loop}{EndLoop}
  {\printandpush\algorithmicloop}
  {\popandprint\algorithmicend\ \algorithmicloop}%
\algdef{SE}[REPEAT]{Repeat}{Until}
  {\printandpush\algorithmicrepeat}[1]
  {\popandprint\algorithmicuntil\ #1}%
\algdef{SE}[IF]{If}{EndIf}[1]
  {\printandpush\algorithmicif\ #1\ \algorithmicthen}
  {\popandprint\algorithmicend\ \algorithmicif}%
\algdef{C}[IF]{IF}{ElsIf}[1]
  {\popandprint\pushindent\algorithmicelse\ \algorithmicif\ #1\ \algorithmicthen}%
\algdef{Ce}[ELSE]{IF}{Else}{EndIf}
  {\popandprint\pushindent\algorithmicelse}%
\algdef{SE}[PROCEDURE]{Procedure}{EndProcedure}[2]
   {\printandpush\algorithmicprocedure\ \textproc{#1}\ifthenelse{\equal{#2}{}}{}{(#2)}}%
   {\popandprint\algorithmicend\ \algorithmicprocedure}%
\algdef{SE}[FUNCTION]{Function}{EndFunction}[2]
   {\printandpush\algorithmicfunction\ \textproc{#1}\ifthenelse{\equal{#2}{}}{}{(#2)}}%
   {\popandprint\algorithmicend\ \algorithmicfunction}%
\makeatother

\begin{document}

\begin{algorithm}
  \caption{Euclid’s algorithm}\label{euclid}
  \begin{algorithmic}[1]
    \Procedure{Euclid}{$a,b$}\Comment{The g.c.d.\ of a and b}
      \LState $r\gets a\bmod b$
      \While{$r\not=0$}\Comment{We have the answer if r is 0}
        \LState $a\gets b$
        \LState $b\gets r$
        \LState $r\gets a\bmod b$
      \EndWhile\label{euclidendwhile}
      \LState \Return $b$\Comment{The gcd is b}
    \EndProcedure
  \end{algorithmic}
\end{algorithm}
\end{document}

Every single definition is redefined to now manage the indentation:

  • At the start of every block, you insert \printindent and append \pushindent. This removes the regular algorithmic indent \algorithmicindent and prints the vertical rule. At the end it adds another indent so that subsequent statements are properly indented; and
  • At the end of every block, you insert \popindent\printindent. This removes the regular algorithmic indent \algorithmicindent, then swallows one indent, and prints the remaining indents.

I've added some additional macros like \printandpush and \popandprint to combine the above \printindent, \pushindent and \popindent modifications.

Instead of using the traditional \State for procedural statements, use \LState. It performs a similar task to what is mentioned above.

This solution should work well for the standard environments defined about. However, other more complicated environments defined using \algdef may require more fiddling to obtain correct indentation.

Regarding the vertical rule. It is set at .5em from the left-most margin of the block, is .4pt wide and is defined by \therule:

\def\therule{%
  \makebox[\algorithmicindent][l]{%
    \hspace*{.5em}\vrule height .75\baselineskip depth .25\baselineskip}}%

Token list appending is described in How does one append material to a token list?, while token removal is discussed in How does one remove material from a token list?

Werner
  • 603,163
  • 2
    Nice solution!! – Gonzalo Medina Apr 21 '12 at 17:12
  • Hello, please i'd like to add lines between while and endwhile like you, this is my example \usepackage{algorithm} \usepackage{algorithmic} \begin{document} \begin{algorithm}[H] \caption{My algo} \begin{algorithmic}[1] \WHILE{condition} \STATE instruction 1 \STATE instruction 2 \ENDWHILE \end{algorithmic} \end{algorithm} \end{document} – researcher Oct 01 '12 at 15:47
  • should i add \usepackage{algpseudocode} ...... with \usepackage{algorithm} \usepackage{algorithmic} or should i delete \usepackage{algorithmic} ? – researcher Oct 01 '12 at 15:48
  • @researcher: The code in my answer was written for algpseudocode from algorithmicx. So you need \usepackage{algorithm}\usepackage{algpseudocode} and then change all the \STATE to \State, \WHILE to \While, \ENDWHILE to \EndWhile. I'll see what needs to be done when you want to use algcompatible, which seems to be the code you're using. Once I have that covered, I'll add it to my answer. – Werner Oct 01 '12 at 16:17
  • thank you for answer it works your solution, i ask please how can change while to "tant que "(in frensh), i try with i \renewcommand{\algorithmicwhile}{\textbf{Tant que}} i set it before your code but it didn't work! – researcher Oct 01 '12 at 17:16
  • @researcher: If you use my example code as-is, you just have to add \renewcommand{\algorithmicwhile}{\textbf{Tant que}} in the preamble after \usepackage{algpseudocode} for it to work. I also assume you're not using \usepackage{algcompatible}. – Werner Oct 01 '12 at 18:07
  • \renewcommand{\algorithmicwhile}{\textbf{Tant que}} works but \renewcommand{\algorithmicendwhile}{\textbf{Fin Tant que}} didn't work!! – researcher Oct 01 '12 at 18:16
  • @researcher: That's because there's no \algorithmicendwhile command. In fact, the end of a \While construct is made up of \algorithmicend\ \algorithmicwhile. So, you could either just do \renewcommand{\algorithmicend}{\textbf{Fin}}, or redefine the entire \While construct: \algdef{SE}[WHILE]{While}{EndWhile}[1] {\printandpush\textbf{Tant que}\ #1\ \textbf{faire}} {\popandprint\textbf{Fin Tant que}} I used Google Translate. – Werner Oct 01 '12 at 18:22
  • yes it works now, i'm sorry if i disturb you i have just another question: in my algoritm i'd like to add Début at the begining of the algoithm and FIN at the end and join them with a line, is that possible? – researcher Oct 01 '12 at 18:29
  • @researcher: Add \algdef{SE}[ALGOBEGEND]{AlgBegin}{AlgEnd}{\printandpush\textbf{D\'ebut}}{\popandprint\textbf{Fin}} and then you can use \AlgBegin and \AlgEnd just like you would \Procedure and \EndProcedure. – Werner Oct 02 '12 at 04:53
  • i test your solution, it works but the first instructios and 'Début' are on the same line ! and I have a lengthy instruction it intersects the line that connects 'Début' to 'Fin'!! Thank you – researcher Oct 02 '12 at 09:48
  • 2
    It does not seem to work properly with \ForAll. Would it be correct to use

    \algdef{SE}[FOR]{ForAll}{EndFor}[1] {\printandpush\algorithmicforall\ #1\ \algorithmicdo} {\popandprint\algorithmicend\ \algorithmicfor}%

    instead of

    \algdef{S}[FOR]{ForAll}[1] {\printindent\algorithmicforall\ #1\ \algorithmicdo}%

    ?

    – Magnar Myrtveit Oct 22 '14 at 22:35
  • nice solution! what if we want to use the noend option? – vefthym May 12 '17 at 08:37
  • @vefthym: Why don't you just switch to algorithm2e then? – Werner May 12 '17 at 17:09
  • @Werner I have already written a big algorithm and I was hoping there would be an easier solution than this, but I guess it is my last resort. Thanks again for the nice answer though (after a few years :). – vefthym May 12 '17 at 17:13
  • thanks for nice solution. One has to use LState instead of State, other than that, your solution seems to work very well so far. – Nasser Jun 12 '20 at 08:45