[Tex/LaTex] Use \newcommand in \newenvironment – content of environment is argument of \newcommand

environmentsmacros

I want to create an environment, that generates a new command. There are some similar examples on google, but nothing fits exactly.

The minimal (not) working example shows best, what I want:

\documentclass[12pt,a4paper]{article}
\usepackage[utf8]{inputenc}

\setlength{\parindent}{0pt}

%Thats my environment:
\newenvironment{LHZZ}[1]{
\newcommand{\csname LH#1\endcsname }[1]\bgroup
}{\egroup}

%Thats the use of it. It is very important, that I can use arguments:  
\begin{LHZZ}{XY}
write stuff here 

refer to #1
\end{LHZZ}

The environment above should (but doesnt) equal the following:

% not part of the code !!!!!
\newcommand{\LHXY}[1]{
write stuff here

refer to #1
}

The following is the call in the document.

\begin{document}
\LHXY{parameter} %the command \LHXY has been created indirectly by \begin{LHZZ}
\end{document}

The output should be:

write stuff here

refer to parameter

It doesnt work. It says \csname is already defined. But I dont want to define \csname. It is just the first command inside the argument. What do I have to change, to make this work?

Edit: There is a purpose for this, what seems to be code golfing. This should lead into a package which can be used by people who are not Tex-safe. The package is for making brochures for marriages, church service, etc. easily.

Edit 2:
I sticked to one of the given solutions as far as possible and far as I understood it. Unfortunately, I need one optional parameter. To understand, what is the meaning of this quirky code, have a look at the example songbook and the call of it. The almost final version as a still not working MWE:

%Business as usual
\documentclass[12pt,a4paper]{book}
\usepackage[utf8]{inputenc}
\usepackage[german]{babel}
\usepackage{environ}

%This sets a default value
\newcommand{\LHliederStandard}{n}

%This calls the environment(s)
\newcommand{\LHsong}[3][\LHliederStandard]{
\csname LH#2\endcsname[#1]#3 %indirect call of what is defined in Liederbuch environment
}

\makeatletter
%This is the Liederbuch environment (Liederbuch = songbook)
\NewEnviron{Liederbuch}[1]{
\xdef\LB@my@temp{
\noexpand\newcommand{\csname LH#1\endcsname}[2][\relax]{
\unexpanded\expandafter{\BODY}
}
}
\aftergroup\LB@my@temp
}
\makeatother

%This is the Lied environment (Lied = song)
\NewEnviron{Lied}[2]{
\ifnum\numexpr#2=\numexpr##3 %the double # refers to parameter of the level above, or doesnt it.
\ifnum\pdfstrcmp{#1}{##2}=0
\BODY
\fi
\fi
}

%example for a songbook
\begin{Liederbuch}{songbook}

\begin{Lied}{n}{1}
song 1 mode n
\end{Lied}

\begin{Lied}{nt}{2}
song 2 mode nt
\end{Lied}

\end{Liederbuch}

%call of the songbook
\begin{document}

\LHsong{songbook}{1}     %calls song 1 mode n
\LHsong[nt]{songbook}{2} %calls song 2 mode nt
%hypothetical other songbook
\LHsong[n]{childrensongs}{35}

\end{document}

Best Answer

The command form is easier:

\documentclass{article}

\newcommand{\LHZZ}[2]{%
  \expandafter\newcommand\csname LH#1\endcsname[1]{#2}}%
}

\begin{document}

\LHZZ{XY}{%
  write stuff here

  refer to #1%
}

\LHXY{parameter}

\end{document}

If you really insist in using an environment, use environ:

\documentclass{article}
\usepackage{environ}

\makeatletter
\NewEnviron{LHZZ}[1]{%
  \edef\maestroglanz@temp{%
    \unexpanded{\expandafter\gdef\csname LH#1\endcsname}####1{\unexpanded\expandafter{\BODY}}%
  }%
  \maestroglanz@temp
}
\makeatother

\begin{document}

\begin{LHZZ}{XY}
write stuff here

refer to #1
\end{LHZZ}

\LHXY{parameter}

\end{document}

enter image description here


An implementation using xparse and expl3; the largest part of the code is the definition of the error messages in case the environment is called twice with the same argument.

\documentclass{article}
\usepackage{environ,xparse}

\ExplSyntaxOn
\NewEnviron{LHZZ}[1]
 {
  \cs_set_protected:cV { LH#1:n } \BODY
  \cs_if_free:cTF { LH#1 }
   {
    \cs_gset_eq:cc { LH#1 }{ LH#1:n }
   }
   {
    \msg_error:nnn { maestroglanz/LH } { already-defined } { #1 }
   }
 }
\cs_generate_variant:Nn \cs_set_protected:Nn { cV }
\msg_new:nnnn { maestroglanz/LH } { already-defined }
 {
  LH#1~already~defined
 }
 {
  You~used~\token_to_str:N \begin{LHZZ}{#1}~before
 }
\ExplSyntaxOff

\begin{document}

\begin{LHZZ}{XY}
write stuff here 

refer to #1
\end{LHZZ}

\LHXY{parameter}

\begin{LHZZ}{XY}a\end{LHZZ}

\end{document}

Without the error check, the code part would look like

\ExplSyntaxOn
\NewEnviron{LHZZ}[1]
 {
  \cs_set_protected:cV { LH#1:n } \BODY
  \cs_gset_eq:cc { LH#1 }{ LH#1:n }
 }
\cs_generate_variant:Nn \cs_set_protected:Nn { cV }
\ExplSyntaxOff

Update

With xparse released 2019-05-03 the code can become

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentEnvironment{LHZZ}{m +b}
 {
  \cs_set_protected:cn { LH#1:n } { #2 }
  \cs_if_free:cTF { LH#1 }
   {
    \cs_gset_eq:cc { LH#1 }{ LH#1:n }
   }
   {
    \msg_error:nnn { maestroglanz/LH } { already-defined } { #1 }
   }
 }{}
\msg_new:nnnn { maestroglanz/LH } { already-defined }
 {
  LH#1~already~defined
 }
 {
  You~used~\token_to_str:N \begin{LHZZ}{#1}~before
 }
\ExplSyntaxOff

\begin{document}

\begin{LHZZ}{XY}
write stuff here 

refer to #1
\end{LHZZ}

\LHXY{parameter}

\begin{LHZZ}{XY}a\end{LHZZ}

\end{document}
Related Question