7

Suppose I have the string abc. By "every possible capitalization" I mean the following set of 8 different strings:

  • ABC
  • ABc
  • AbC
  • Abc
  • aBC
  • aBc
  • abC
  • abc

What I'd particularly like is the best way to generate a LaTeX3 seq which contains a list of these strings, but other options might work as well.

5
  • 3
    Trivial in lualatex.
    – JPi
    Mar 16, 2017 at 15:00
  • 1
    What do you mean by "fastest way" -- number of CPU cycles? time spent coding the program? something else? For a string of length n containing just alphabetic characters, there are 2^n capitalization schemes: How should they be output? A comma-separated list? An itemized list as in your screenshot? Please clarify what you're trying to achieve.
    – Mico
    Mar 16, 2017 at 15:08
  • Well, as I said in the post I would prefer a solution which would result in a LaTeX3 seq, but anything which can loop over the set would be fine. By fastest, I should say most efficient
    – junius
    Mar 16, 2017 at 15:16
  • @JPi taking less code than doing it just in tex? Mar 16, 2017 at 17:00
  • @DavidCarlisle: yes if the length of the string is unknown a priori.
    – JPi
    Mar 16, 2017 at 18:18

2 Answers 2

8

You can do it for “arbitrary” strings. Well, not so arbitrary, because a 32 character long string will produce 2^32 items.

Anyway, here is the code. The trick is to use the binary representation of numbers: a 1 means “uppercase”, a zero or nothing means “keep case”.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\generate}{O{default}m}
 {% #1 = name for a sequence, #2 = string
  \kevinkeith_generate:nn { #1 } { #2 }
 }

\NewDocumentCommand{\use}{O{default}+m}
 {
  \seq_map_inline:cn { l_kevinkeith_generate_#1_seq } { #2 }
 }

\cs_new_protected:Nn \kevinkeith_generate:nn
 {
  \seq_clear_new:c { l_kevinkeith_generate_#1_seq }
  \seq_clear:N \l__kevinkeith_generate_temp_seq
  \int_step_inline:nnnn { 0 } { 1 } { \fp_eval:n { 2^(\tl_count:n{#2})-1 } }
   {
    \__kevin_keith_generate_case:xn { \int_to_bin:n { ##1 } } { #2 }
   }
  \seq_set_eq:cN { l_kevinkeith_generate_#1_seq } \l__kevinkeith_generate_temp_seq
 }
\cs_new_protected:Nn \__kevin_keith_generate_case:nn
 {
  \tl_clear:N \l__kevinkeith_generate_case_tl
  \int_step_inline:nnnn { 1 } { 1 } { \tl_count:n { #2 } }
   {
    \str_if_eq_x:nnTF { \tl_item:nn { #1 } { -##1 } } { 1 }
     {
      \tl_put_right:Nx \l__kevinkeith_generate_case_tl
       {
        \tl_upper_case:n { \tl_item:nn { #2 } { ##1 } }
       }
     }
     {
      \tl_put_right:Nx \l__kevinkeith_generate_case_tl
       {
        \tl_item:nn { #2 } { ##1 }
       }
     }
   }
  \seq_put_right:NV \l__kevinkeith_generate_temp_seq \l__kevinkeith_generate_case_tl
 }
\cs_generate_variant:Nn \__kevin_keith_generate_case:nn { x }

\tl_new:N \l__kevinkeith_generate_case_tl
\seq_new:N \l__kevinkeith_generate_temp_seq

\ExplSyntaxOff


\begin{document}

\generate{abc}
\use{#1 }

\generate[four]{abcd}
\use[four]{#1 }

\end{document}

enter image description here

A much faster code that just prints one of the possible capitalization, given a number between 1 and 2 raised to the string length.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\capitalizestring}{mm}
 {% #1 = index of capitalized string, #2 = string
  % the index is between 1 and 2^{length of string}
  \kevinkeith_capitalize:nn { #1 } { #2 }
 }

\cs_new_protected:Nn \kevinkeith_capitalize:nn
 {
  \int_step_inline:nnnn { 1 } { 1 } { \tl_count:n { #2 } }
   {
    \str_if_eq_x:nnTF { \tl_item:fn { \int_to_bin:n { #1 - 1 } } { -##1 } } { 1 }
     {
      \tl_upper_case:n { \tl_item:nn { #2 } { ##1 } }
     }
     {
      \tl_item:nn { #2 } { ##1 }
     }
   }
 }
\cs_generate_variant:Nn \tl_item:nn { f }
\ExplSyntaxOff


\begin{document}

\capitalizestring{1}{abc}
\capitalizestring{2}{abc}
\capitalizestring{3}{abc}
\capitalizestring{4}{abc}
\capitalizestring{5}{abc}
\capitalizestring{6}{abc}
\capitalizestring{7}{abc}
\capitalizestring{8}{abc}

\capitalizestring{523}{abcdefghij}

\end{document}

enter image description here

2
  • It does produce three Abc, so it doesn't work entirely correct, right? By the way, what would be faster, the first one, or a \int_step_inline:nnnn {0} {1} { <2^{length of string}> } { \capitalizestring{#1}{abc} }?
    – Manuel
    Mar 16, 2017 at 21:43
  • @Manuel Thanks, the issue has been fixed. The two approach cannot really be compared, because the first saves all strings in a sequence, as requested.
    – egreg
    Mar 16, 2017 at 21:54
12

Note quite idiomatic L3 but

enter image description here

\documentclass{article}

\def\zz#1{\xzz{}#1\relax}

\def\xzz#1#2#3\relax{%
\ifx\relax#3\relax
\zzdo{#1#2}%
\zzdo{#1\uppercase{#2}}%
\else
\xzz{#1#2}#3\relax
\xzz{#1\uppercase{#2}}#3\relax
\fi}

% define to do whatever
\def\zzdo#1{\fbox{#1} }
\begin{document}

\zz{abc}
\end{document}
2
  • So verbose someone could confuse this with expl3.
    – Manuel
    Mar 16, 2017 at 22:33
  • @Manuel the likeness isn't that surprising, given the overlap in authorship:-) Mar 16, 2017 at 22:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .