4

I attempted to write some code that would take the sex of my animal patients (neutered male, spayed female, intact male, intact female) that would apply that to usage of the correct form of he/she, him/her, his/hers to auto-populate throughout the document.

Currently, the code I have written is alternating outputs between the masculine and feminine forms regardless of the sex input and I can't quite figure out why. Any help/troubleshooting would be greatly appreciated.

\documentclass[]{article}
\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{graphicx}
\usepackage{verbatim}
\usepackage{lmodern}
\usepackage{xspace}
\usepackage{float}
\usepackage{tabularx}
\makeatletter 
\makeatother 

\newif\ifxspace\xspacetrue
\makeatletter
\newif\ifsex@he\sex@hefalse

% macro to get first character of a token
\def\sex@getfirst#1{\sex@fstx#1\empty\empty}
\def\sex@fstx#1#2\empty{%
 \edef\sex@fst{#1}}

\ProcessOptions
% toggle for changing the gender

%macros for comparison
\def\sex@balls{MI} %male - intact, with balls
\def\sex@neut{MC} %male - castrated, no balls
\def\sex@ovs{FI} %female - intact, ovaries
\def\sex@spay{FS} %female - spayed, no ovaries

% command to set gender (takes Male/M Female/F etc. or lowercase version of each)
% expands argument first to allow it to be a macro
\newcommand\setgender[1]{%
\edef\next{\noexpand\sex@setgender{#1}}\next}
\newcommand\sex@setgender[1]{%
\sex@getfirst{#1}
\ifx\sex@fst\sex@balls \global\sex@hefalse 
    \else
    \ifx\sex@fst\sex@neut \global\sex@hefalse
        \else
        \ifx\sex@fst\sex@ovs \global\sex@hetrue
            \else
            \ifx\sex@fst\sex@spay \global\sex@hetrue
                \else
                \global\sex@hetrue 
                %\PackageWarning{he-she}{Invalid value for gender. Using Masculine}
                \fi
            \fi
        \fi
    \fi
}

%SEX - he/she, his/hers, him/her (lowercase and capital)

%Sex toggle - he/she (lower case)
\newcommand*{\heshe}{%
  \leavevmode
  \ifsex@he
    he%
    \global\sex@hefalse%
  \else
    she%
    \global\sex@hetrue%
  \fi
  \ifxspace\xspace\fi
}

%Sex toggle - He/She (capitalized)
\newcommand*{\Heshe}{%
  \leavevmode
  \ifsex@he
    He%
    \global\sex@hefalse%
  \else
    She%
    \global\sex@hetrue%
  \fi
  \ifxspace\xspace\fi
}

%Sex toggle - him/her (lowercase)
\newcommand*{\himher}{%
  \leavevmode
  \ifsex@he
    him%
  %\global\sex@hefalse%
  \else
    her%
    \global\sex@hetrue%
  \fi
  \ifxspace\xspace\fi
}

%Sex toggle - Him/Her (capital)
\newcommand*{\Himher}{%
  \leavevmode
  \ifsex@he
    Him%
    \global\sex@hefalse%
  \else
    Her%
    \global\sex@hetrue%
  \fi
  \ifxspace\xspace\fi
}

%Sex toggle - his/her (lowercase)
\newcommand*{\hisher}{%
  \leavevmode
  \ifsex@he
    his%
    \global\sex@hefalse%
  \else
    her%
    \global\sex@hetrue%
  \fi
  \ifxspace\xspace\fi
}

%Sex toggle - His/Her (capitalized)
\newcommand*{\Hisher}{%
  \leavevmode
  \ifsex@he
    His%
    \global\sex@hefalse%
  \else
    Her%
    \global\sex@hetrue%
  \fi
  \ifxspace\xspace\fi
}


\begin{document}
\begin{tabularx}{\textwidth} { 
  | >{\raggedright\arraybackslash}X 
  | >{\raggedright\arraybackslash}X 
  | >{\raggedright\arraybackslash}X 
  | >{\raggedright\arraybackslash}X | }
 \hline
{\bf Patient name:}  &  ***    & {\bf Sex:}  & \sex@balls \\ %
\hline
\end{tabularx} \\

\heshe \Heshe \himher \Himher \hisher \Hisher

\end{document}
5
  • I get this output consistently. Are you saying that if you use \heshe~\heshe you get he she and it should be he he (or she she)?
    – Werner
    Commented Dec 30, 2020 at 22:10
  • The table does is essentially empty (and should not have \bf in latex) and following that is the text she He her Him her His what output did you expect? Commented Dec 30, 2020 at 22:13
  • 4
    +1 for a Question Title including the word "sex". Commented Dec 30, 2020 at 22:16
  • you say the code is alternating male/female but is that not the intention of all your tests such as \ifsex@he His \global\sex@hefalse which sets the flag false if it is currently true. Commented Dec 30, 2020 at 22:19
  • what do you mean by "regardless of the sex input"? I can not see anywhere that you specify a gender, the code just alternates. Commented Dec 30, 2020 at 22:22

2 Answers 2

2

Your question is rather unclear but my guess is that you do not want the alternating he/she behaviour (which is all the code you show does) and want the commands to produce a specified gender in each case, something like this

enter image description here

\documentclass[]{article}
\usepackage{xspace}

\makeatletter

\newif\ifxspace\xspacetrue

\newif\ifsex@he\sex@hefalse


% #1=MI,MC,FI,FS
\def\setgender#1{%
  \if F\@firstoftwo#1\sex@hefalse\else\sex@hetrue\fi}

%%????\ProcessOptions





%Sex toggle - he/she (lower case)
\newcommand*{\heshe}{%
  \ifsex@he\else s\fi
  he%
  \ifxspace\xspace\fi
}

%Sex toggle - He/She (capitalized)
\newcommand*{\Heshe}{%
  \ifsex@he H\else Sh\fi
  e%
  \ifxspace\xspace\fi
}

%Sex toggle - him/her (lowercase)
\newcommand*{\himher}{%
  h%
  \ifsex@he im\else er\fi
  \ifxspace\xspace\fi
}

%Sex toggle - Him/Her (capital)
\newcommand*{\Himher}{%
  H%
  \ifsex@he im\else er\fi
  \ifxspace\xspace\fi
}

%Sex toggle - his/her (lowercase)
\newcommand*{\hisher}{%
  h%
  \ifsex@he is\else er\fi
  \ifxspace\xspace\fi
}

%Sex toggle - His/Her (capitalized)
\newcommand*{\Hisher}{%
  H%
  \ifsex@he is\else er\fi
  \ifxspace\xspace\fi
}

\makeatother

\begin{document}

\setgender{MI} MI: \heshe \Heshe \himher \Himher \hisher \Hisher

\setgender{MC} MC: \heshe \Heshe \himher \Himher \hisher \Hisher

\setgender{FI} FI: \heshe \Heshe \himher \Himher \hisher \Hisher

\setgender{FC} FC: \heshe \Heshe \himher \Himher \hisher \Hisher

\end{document}
2
  • 1
    Sorry my question didn't make sense, but that's exactly what I wanted it to do. Thank you for your help!! Commented Dec 30, 2020 at 23:23
  • @egreg rather unnecessarily patronizing. Commented Dec 30, 2020 at 23:52
0

The macro \setgender as implemented in the code of your question in any case leads to setting \global\sex@hetrue:

\setgender does some \edef-expansion-trickery to the argument and then calls \sex@setgender.

\sex@setgender in turn via \sex@getfirst defines a macro \sex@fst from the first letter of the argument. The definition text of \sex@fst consists of a single letter.

Then a bunch of \ifx-comparisons of \sex@fst to macros take place whose definition texts consist of several letters.

Thus with each of those \ifx-comparisons meanings of compared macros always differ.

Thus each \ifx-comparison yields going the \else-route and things end up in the branch with

\global\sex@hetrue 
%\PackageWarning{he-she}{Invalid value for gender. Using Masculine}

.


I suggest not using \if/\ifx-comparison but delimited arguments for forking the gender:

\documentclass{article}

\usepackage{xspace}

\makeatletter

\newif\ifxspace\xspacetrue
\newif\ifsex@he\sex@hefalse

\@ifdefinable\gobbletoexclam{\long\def\gobbletoexclam#1!{}}%
\newcommand\setgender[1]{%
  \ifcat$\detokenize\expandafter{\gobbletoexclam#1!}$%
  \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  {%
    \setgenderfork
    !#1!MC!FI!FS!{\global\sex@hetrue}%
    !MI!#1!FI!FS!{\global\sex@hetrue}%
    !MI!MC!#1!FS!{\global\sex@hefalse}%
    !MI!MC!FI!#1!{\global\sex@hefalse}%
    !MI!MC!FI!FS!{\setgendererrordefault}!!!!%
  }{\setgendererrordefault}%
}%
\newcommand\setgendererrordefault{%
   \PackageError{he-she}{Invalid value for gender. Using Masculine}{Only values MI/MC/FI/FS are allowed!}%
   \global\sex@hetrue
}%
\@ifdefinable\setgenderfork{%
  \long\def\setgenderfork#1!MI!MC!FI!FS!#2#3!!!!{#2}%
}%

%Sex toggle - he/she (lower case)
\newcommand*{\heshe}{\ifsex@he\else s\fi he\ifxspace\xspace\fi}%
%Sex toggle - He/She (capitalized)
\newcommand*{\Heshe}{\ifsex@he H\else Sh\fi e\ifxspace\xspace\fi}%
%Sex toggle - him/her (lowercase)
\newcommand*{\himher}{h\ifsex@he im\else er\fi\ifxspace\xspace\fi}%
%Sex toggle - Him/Her (capital)
\newcommand*{\Himher}{H\ifsex@he im\else er\fi\ifxspace\xspace\fi}%
%Sex toggle - his/her (lowercase)
\newcommand*{\hisher}{h\ifsex@he is\else er\fi\ifxspace\xspace\fi}%
%Sex toggle - His/Her (capitalized)
\newcommand*{\Hisher}{H\ifsex@he is\else er\fi\ifxspace\xspace\fi}

\makeatother

\begin{document}

\setgender{MI} MI: \heshe \Heshe \himher \Himher \hisher \Hisher

\setgender{MC} MC: \heshe \Heshe \himher \Himher \hisher \Hisher

\setgender{FI} FI: \heshe \Heshe \himher \Himher \hisher \Hisher

\setgender{FS} FS: \heshe \Heshe \himher \Himher \hisher \Hisher

% Raising errors - Behavior in some cases differing from David Carlisle's code!

\setgender{FFS}

\setgender{MMI}

\setgender{}

\setgender{ }

\setgender{\fi}

\setgender{!MMI}

\end{document}

This variant, however, does not expand the argument of \setgender.

I.e., this variant does not let you set the gender from a macro.

I.e.,

\def\foobar{FI}%
\setgender{\foobar}

does not work out.

Triggering expansion is up to you:

\def\foobar{FI}%
\expandafter\setgender\expandafter{\foobar}

does work out.

I tend to avoid having user-input expanded/carried out wherever possible. User-input is not predictable and thus is to be considered arbitrary code. Expanding/carrying out arbitrary code may cause all kinds of trouble.

E.g., if an evil person does

\def\foobar{\foobar}
\setgender{\foobar\foobar}

with David Carlisle's code, then the compiler will silently be caught in a non-terminating loop.

(You can press <CTRL>+Z to cause LaTeX to stop.)

You must log in to answer this question.

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