17

I am using the algorithmicx package to write an algorithm. However I am using the noend option:

\usepackage[noend]{algpseudocode}

Is there anyway to get vertical lines for each block of code? This question has been answered here:

https://tex.stackexchange.com/a/52778/37056

However, it does not work when using the noend option.

EDIT: I'm looking for something like the algorithms here: http://www.isg.rhul.ac.uk/~kp/dtls.pdf

sazr
  • 291

4 Answers4

21

Here are some more natural (automatic and better-behaved) implementations of the vertical lines, where I have used Werner's answer to the linked question as a starting point.

Version 1: Draw a fixed-height rule for each line and each indent level

This version does not work well if the line height and depths differ; see below for attempted update to solve this.

This works for the noend case too, but there is a fudge factor of \addvspace{-3pt} in the printing code to prevent unwanted gaps in lines that don't get printed. This is not very robust but I can't do any better currently as I haven't identified the space (seems to come from \item[]).

Image of output from version 1 code

\documentclass{article}
\usepackage{algorithm}% http://ctan.org/pkg/algorithm
\PassOptionsToPackage{noend}{algpseudocode}% comment out if want end's to show
\usepackage{algpseudocode}% http://ctan.org/pkg/algorithmicx

\errorcontextlines\maxdimen

% begin vertical rule patch for algorithmicx (http://tex.stackexchange.com/questions/144840/vertical-loop-block-lines-in-algorithmicx-with-noend-option)
\makeatletter
% start with some helper code
% This is the vertical rule that is inserted
\newcommand*{\algrule}[1][\algorithmicindent]{\makebox[#1][l]{\hspace*{.5em}\vrule height .75\baselineskip depth .25\baselineskip}}%

\newcount\ALG@printindent@tempcnta
\def\ALG@printindent{%
    \ifnum \theALG@nested>0% is there anything to print
        \ifx\ALG@text\ALG@x@notext% is this an end group without any text?
            % do nothing
            \addvspace{-3pt}% FUDGE for cases where no text is shown, to make the rules line up
        \else
            \unskip
            % draw a rule for each indent level
            \ALG@printindent@tempcnta=1
            \loop
                \algrule[\csname ALG@ind@\the\ALG@printindent@tempcnta\endcsname]%
                \advance \ALG@printindent@tempcnta 1
            \ifnum \ALG@printindent@tempcnta<\numexpr\theALG@nested+1\relax% can't do <=, so add one to RHS and use < instead
            \repeat
        \fi
    \fi
    }%
\usepackage{etoolbox}
% the following line injects our new indent handling code in place of the default spacing
\patchcmd{\ALG@doentity}{\noindent\hskip\ALG@tlm}{\ALG@printindent}{}{\errmessage{failed to patch}}
\makeatother
% end vertical rule patch for algorithmicx


\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}
      \State $r\gets a\bmod b$
      \While{$r\not=0$}\Comment{We have the answer if r is 0}
        \State $a\gets b$
        \State $b\gets r$
        \State $r\gets a\bmod b$
      \EndWhile\label{euclidendwhile}
      \State \Return $b$\Comment{The gcd is b}
    \EndProcedure
  \end{algorithmic}
\end{algorithm}
\end{document}

Version 2: process boxes twice in attempt to measure heights

The following is a version that attempts to take better care of the line heights, by passing states to \myState wrapped in braces instead of \State directly, which measures the line heights before reprocessing the content. As it has a different interface, and may be even less robust (I am not yet sure), I am leaving the original answer above rather than replacing it.

Image of output from version 2 code

\documentclass{article}
\usepackage{algorithm}% http://ctan.org/pkg/algorithm
%\PassOptionsToPackage{noend}{algpseudocode}% comment out if want end's to show
\usepackage{algpseudocode}% http://ctan.org/pkg/algorithmicx

\errorcontextlines\maxdimen

% begin vertical rule patch for algorithmicx (http://tex.stackexchange.com/questions/144840/vertical-loop-block-lines-in-algorithmicx-with-noend-option)
\makeatletter
% start with some helper code
% This is the vertical rule that is inserted
    \newcommand*{\algrule}[1][\algorithmicindent]{\makebox[#1][l]{\hspace*{.5em}\thealgruleextra\vrule height \thealgruleheight depth \thealgruledepth}}%
% its height and depth need to be adjustable
\newcommand*{\thealgruleextra}{}
\newcommand*{\thealgruleheight}{.75\baselineskip}
\newcommand*{\thealgruledepth}{.25\baselineskip}

\newcount\ALG@printindent@tempcnta
\def\ALG@printindent{%
    \ifnum \theALG@nested>0% is there anything to print
        \ifx\ALG@text\ALG@x@notext% is this an end group without any text?
            % do nothing
        \else
            \unskip
            \addvspace{-1pt}% FUDGE to make the rules line up
            % draw a rule for each indent level
            \ALG@printindent@tempcnta=1
            \loop
                \algrule[\csname ALG@ind@\the\ALG@printindent@tempcnta\endcsname]%
                \advance \ALG@printindent@tempcnta 1
            \ifnum \ALG@printindent@tempcnta<\numexpr\theALG@nested+1\relax% can't do <=, so add one to RHS and use < instead
            \repeat
        \fi
    \fi
    }%
\usepackage{etoolbox}
% the following line injects our new indent handling code in place of the default spacing
\patchcmd{\ALG@doentity}{\noindent\hskip\ALG@tlm}{\ALG@printindent}{}{\errmessage{failed to patch}}
\makeatother

% the required height and depth are set by measuring the content to be shown
% this means that the content is processed twice
\newbox\statebox
\newcommand{\myState}[1]{%
    \setbox\statebox=\vbox{#1}%
    \edef\thealgruleheight{\dimexpr \the\ht\statebox+1pt\relax}%
    \edef\thealgruledepth{\dimexpr \the\dp\statebox+1pt\relax}%
    \ifdim\thealgruleheight<.75\baselineskip
        \def\thealgruleheight{\dimexpr .75\baselineskip+1pt\relax}%
    \fi
    \ifdim\thealgruledepth<.25\baselineskip
        \def\thealgruledepth{\dimexpr .25\baselineskip+1pt\relax}%
    \fi
    %\showboxdepth=100
    %\showboxbreadth=100
    %\showbox\statebox
    \State #1%
    %\State \usebox\statebox
    %\State \unvbox\statebox
    %reset in case the next command is not wrapped in \myState
    \def\thealgruleheight{\dimexpr .75\baselineskip+1pt\relax}%
    \def\thealgruledepth{\dimexpr .25\baselineskip+1pt\relax}%
}
% end vertical rule patch for algorithmicx

\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}
      \myState{$r\gets a\bmod b$}
      \While{$r\not=0$}\Comment{We have the answer if r is 0}
        \myState{$a\gets\displaystyle\sum_{i=1}^n x_i$}\Comment{Nonsense to show that tall lines might work}
        \myState{$a\gets b$}
        \myState{$b\gets r$}
        \myState{$r\gets a\bmod b$}
      \EndWhile\label{euclidendwhile}
      \myState{\Return $b$\Comment{The gcd is b}}
    \EndProcedure
  \end{algorithmic}
\end{algorithm}
\end{document}

Version 3: use tikzmark at start and end of each block

@desa pointed out that version 2 doesn't work when there are displayed equations in the algorithm. Here is a new implementation that attempts to fix that (I couldn't see how to correct the height guessing algorithm for this case).

This code uses tikz's remember picture feature so requires multiple runs to stabilise (the lines will not appear in the correct place in the first run, or in the first run following other changes on the page). Again, this may be disadvantageous in some cases (or for other reasons not yet identified), so I am also leaving the previous two approaches above.

Image of output from version 3 code

\documentclass{article}
\usepackage{algorithm}% http://ctan.org/pkg/algorithm
%\PassOptionsToPackage{noend}{algpseudocode}% comment/uncomment depending on whether want ends to show
\usepackage{algpseudocode}% http://ctan.org/pkg/algorithmicx

\usepackage{etoolbox}
\usepackage{tikz}
\usetikzlibrary{tikzmark}
\usetikzlibrary{calc}

\errorcontextlines\maxdimen

% begin vertical rule patch for algorithmicx (http://tex.stackexchange.com/questions/144840/vertical-loop-block-lines-in-algorithmicx-with-noend-option)
% note that some of the packages above are also needed
\newcommand{\ALGtikzmarkcolor}{black}% customise this, if you want
\newcommand{\ALGtikzmarkextraindent}{4pt}% customise this, if you want
\newcommand{\ALGtikzmarkverticaloffsetstart}{-.5ex}% customise this, if you want
\newcommand{\ALGtikzmarkverticaloffsetend}{-.5ex}% customise this, if you want
\makeatletter
\newcounter{ALG@tikzmark@tempcnta}

\newcommand\ALG@tikzmark@start{%
    \global\let\ALG@tikzmark@last\ALG@tikzmark@starttext%
    \expandafter\edef\csname ALG@tikzmark@\theALG@nested\endcsname{\theALG@tikzmark@tempcnta}%
    \tikzmark{ALG@tikzmark@start@\csname ALG@tikzmark@\theALG@nested\endcsname}%
    \addtocounter{ALG@tikzmark@tempcnta}{1}%
}

\def\ALG@tikzmark@starttext{start}
\newcommand\ALG@tikzmark@end{%
    \ifx\ALG@tikzmark@last\ALG@tikzmark@starttext
        % ignore this, the block was opened then closed directly without any other blocks in between (so just a \State basically)
        % don't draw a vertical line here
    \else
        \tikzmark{ALG@tikzmark@end@\csname ALG@tikzmark@\theALG@nested\endcsname}%
        \tikz[overlay,remember picture] \draw[\ALGtikzmarkcolor] let \p{S}=($(pic cs:ALG@tikzmark@start@\csname ALG@tikzmark@\theALG@nested\endcsname)+(\ALGtikzmarkextraindent,\ALGtikzmarkverticaloffsetstart)$), \p{E}=($(pic cs:ALG@tikzmark@end@\csname ALG@tikzmark@\theALG@nested\endcsname)+(\ALGtikzmarkextraindent,\ALGtikzmarkverticaloffsetend)$) in (\x{S},\y{S})--(\x{S},\y{E});%
    \fi
    \gdef\ALG@tikzmark@last{end}%
}



% the following line injects our new tikzmarking code
\apptocmd{\ALG@beginblock}{\ALG@tikzmark@start}{}{\errmessage{failed to patch}}
\pretocmd{\ALG@endblock}{\ALG@tikzmark@end}{}{\errmessage{failed to patch}}
\makeatother
% end vertical rule patch for algorithmicx


\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}%
      \State $r\gets a\bmod b$%
      \While{$r\not=0$}\Comment{We have the answer if r is 0}%
        \State $a\gets\displaystyle\sum_{i=1}^n x_i$\Comment{Nonsense to show that tall lines might work}%
        \State $a\gets b$%
        \State $b\gets r$%
        \State \begin{equation} y = mx + c \end{equation}%
        \State $r\gets a\bmod b$%
      \EndWhile\label{euclidendwhile}%
      \State \Return $b$\Comment{The gcd is b}%
    \EndProcedure%
  \end{algorithmic}
\end{algorithm}

\end{document}
  • I've copied your preamble to my .tex, but can't compile it. I'm getting this error:

    ! Use of \ALG doesn't match its definition.

    – sazr Feb 21 '14 at 15:52
  • @sazr: Sounds like it might be a \makeatletter problem? Can you confirm that this MWE works for you? Would probably need a bit more to go on otherwise. – cyberSingularity Feb 22 '14 at 19:07
  • jeez that was it... I can't even copy paste right x) thanks for putting up with my incompetence. it kinda works (compiled with no errors) but I don't think its working very well, I took a screenshot to show you: screenshot – sazr Feb 23 '14 at 01:33
  • @sazr: Yes, sorry, this solution does not work well in many cases. This is a due to both the \addvspace{-3pt} and the rule parameters height .75\baselineskip depth .25\baselineskip, which don't account for the actual height of the current line. Accounting for that is extremely difficult. I attempted to give a tikz/zref solution at Problems with vertical lines in algorithmicx but it is still not perfect and I'm unable to further improve it as I wouldn't know how. – cyberSingularity Feb 23 '14 at 09:37
  • When I use this, the super-lines are interrupted when and \EndFor is placed on a lower level... – willeM_ Van Onsem Jun 10 '14 at 14:21
  • @CommuSoft: Sorry, I am not quite sure what you have tried nor can I anticipate what might be the problem. Perhaps this would be worth a new question of its own? – cyberSingularity Jun 10 '14 at 20:31
  • @cyberSingularity: I did some research to the origin of the problem. When a line has a large v-space (for instance with \State $a\gets\displaystyle\sum_{i=1}^n x_i$), the length of the vertical line is insufficient. – willeM_ Van Onsem Jun 10 '14 at 20:35
  • @CommuSoft: I was always aware of that problem, sorry that I seemed to neglect to mention it in my answer. I have posted an update that is probably the best I can do in regards to trying to solve that particular issue. Hope it is of some use. – cyberSingularity Jun 11 '14 at 06:54
  • The solution does not work when there is an equation environment inside the algorithm. Do you have an idea why it happens? – desa Mar 28 '17 at 07:25
  • 1
    @desa: Thanks for alerting me to this. Sorry I couldn't work out why guessing the size of the equations didn't work in the previous code, but I have added an alternative approach to drawing the lines that hopefully solves the problem you identified. – cyberSingularity Mar 28 '17 at 12:00
  • @desa: Actually just noticed I already had a similar solution on this site, so you might want to try that as well. – cyberSingularity Mar 28 '17 at 12:05
  • @cyberSingularity Thank you a lot for your prompt reply! I can recompile your code, but I also tried your solution in my example and I got a very weird behaviour: the first time it compiles without any problem and adds the lines, the second time it does not compile and return an error: ERROR: Missing number, treated as zero. Investigating... – desa Mar 28 '17 at 17:35
  • 1
    @desa: Hmm, okay, I might need a more detailed MWE to resolve this. But first, can you check that all your latex packages are up to date? – cyberSingularity Mar 28 '17 at 21:13
  • Maybe it was the reason :) Now it works fine. Thank you a lot! – desa Mar 28 '17 at 21:55
  • Nice solutions, unfortunately the TikZ approach doesn't work with page breaks in the algorithm. – andz Mar 09 '18 at 02:47
  • Hopefully, someday I'll be able to understand the codes :-) – hola Jul 04 '18 at 22:20
  • Oops! There's a problem when run with a dummy state. I'll start a new question – hola Jul 05 '18 at 09:55
  • For the first two solutions, the vertical line I'm getting is broken. The tikz solution seems to work so far. :-) – hola Jun 06 '19 at 20:26
  • Is this tikz solution still working? I copied it and it does compile without error, but no lines are added. It just does nothing. – Lemonbonbon Jul 27 '20 at 14:22
  • @Lemonbonbon: sorry for the late reply. What was your compilation procedure? In particular, did you run it enough times? – cyberSingularity Aug 10 '20 at 21:21
  • @cyberSingularity: Version1 works for me. Tiks does not. But I get no errors, just no visible output with tiks. I am using pdfLatex (run multiple times). – Lemonbonbon Aug 11 '20 at 12:06
  • For the 3rd solution with Tikz, it seems the vertical line overshoots and touches the letters of the following line. – hola Sep 08 '20 at 04:36
2

The algpseudocodex (note the ending x) package has a noEnd option that works well.

  • Fine, but that's a bit short answer to my taste. Can you please provide an example, which matches the question? – MS-SPO Jun 29 '22 at 07:33
0

Based on cyberSingularity's answer, I tried to make a shorter version

\usepackage{tikz}
\usepackage[noend]{algpseudocode}
\makeatletter
\providetoggle{ALG@Ruler@empty}
\newcounter{ALG@Ruler@Id}
\apptocmd{\ALG@beginblock}{%
    \stepcounter{ALG@Ruler@Id}%
    \def\ALG@Ruler{ALG@Ruler@BeginPoint-\arabic{ALG@Ruler@Id}}%
    \expandafter\edef\csname ALG@Ruler@AtLevel@\theALG@nested\endcsname{\ALG@Ruler}%
    \tikz[remember picture, overlay] \coordinate (\ALG@Ruler);%
    \toggletrue{ALG@Ruler@empty}%
}{}{}
\pretocmd{\ALG@endblock}{%
    \iftoggle{ALG@Ruler@empty}{}{%
        \def\ALG@Ruler{\csname ALG@Ruler@AtLevel@\theALG@nested\endcsname}%
        \unskip\tikz[remember picture, overlay] \draw[black!25]
            ([xshift=0.5em, yshift=-0.5ex]\ALG@Ruler) |- ([xshift=1em]\ALG@Ruler|-0,0);%
    }%
    \togglefalse{ALG@Ruler@empty}%
}{}{}
\makeatother
Qiang
  • 184
-4

this is my solution for a one lined for each block

 \begin{algorithm}

\algnewcommand\algorithmiceach{\textbf{each}}
\algnewcommand\Each{\algorithmiceach}

\algnewcommand{\LineForEach}[2]{
    \State\algorithmicfor\ \Each\ {#1}\ \algorithmicdo \ {#2}\ }


\begin{algorithmic}[1]
\Procedure{my procedure}{}
\LineForEach{( condition)}{ what to do}%
\EndProcedure
\end{algorithmic}
\end{algorithm}

enter image description here