Probleme bei eigenem Makro

Redefinition von Makros, Definition eigener Befehle sowie neuer Umgebungen


sLy
Forum-Anfänger
Forum-Anfänger
Beiträge: 29
Registriert: Mi 30. Sep 2009, 17:41

Probleme bei eigenem Makro

Beitrag von sLy »

Hi,

ich habe ein eigenes Makro geschrieben, bei dem in meinem eigenen Befehl
\Lampe{B,C,L}
das "(B)(C)" durch den befehl \a mit
\def \a {(B)(C)} ---> \Lampe{L,\a} 
ersetzt werden soll.
Nur funktioniert das leider nicht.

Folgende Fehler:
! Paragraph ended before \pst@lamp was complete.
! LaTeX Error: \begin{pspicture} on input line 24 ended by \end{document}.
! Missing \endgroup inserted.
! Missing } inserted.

Hier das Minimalbeispiel (\LampeI soll so funktionieren wie \LampeII):
\documentclass{minimal}

\usepackage{etoolbox}
\usepackage{pst-circ}
\usepackage{multido}

\makeatletter

\def \a {(B)(C)}

\newcommand*{\LampeI}[1]{\L@mpeI#1\@nil} 
\newcommand*{\L@mpeI}{}
\def\L@mpeI#1,#2\@nil{%
  \lamp #2{$#1$}
}

\newcommand*{\LampeII}[1]{\L@mpeII#1\@nil} 
\newcommand*{\L@mpeII}{}
\def\L@mpeII#1,#2,#3\@nil{%
  \lamp (#1)(#2){$#3$}
}

\begin{document}

Das:\newline
\begin{pspicture}[showgrid=false](0,0)(8,5)
  \pnode(0.5,4.5){A}
  \pnode(0.5,1.75){B}
  \pnode(7.5,1.75){C}
  \pnode(7.5,4.5){D}
  \battery(D)(A){$U_0$}
  \wire(A)(B)
  \wire(C)(D)
%  \LampeI{L,\a}
\end{pspicture}

soll so aussehen:\newline
\begin{pspicture}[showgrid=false](0,0)(8,5)
  \pnode(0.5,4.5){A}
  \pnode(0.5,1.75){B}
  \pnode(7.5,1.75){C}
  \pnode(7.5,4.5){D}
  \battery(D)(A){$U_0$}
  \wire(A)(B)
  \wire(C)(D)
  \LampeII{B,C,L}
\end{pspicture}

\end{document}
Wenn ich was unklar erklärt haben sollte PN an mich.
Hoffe ihr könnt mir wiedermal helfen,
sLy

phi
Moderator
Moderator
Beiträge: 420
Registriert: Fr 6. Feb 2009, 21:28

Beitrag von phi »

Du kannst die Definition beispielsweise so ändern:
\newcommand*{\LampeI}[1]{%
  \protected@edef\@tempa{#1}%
  \expandafter\L@mpeI\@tempa\@nil
} 

sLy
Forum-Anfänger
Forum-Anfänger
Beiträge: 29
Registriert: Mi 30. Sep 2009, 17:41

Beitrag von sLy »

danke, funktioniert!

lässt sich der code auch so umschreiben, dass ich nur noch
\Lampe{L,a}
schreiben müsste?
also dass ich mir den backslash sparen könnte?

UND

Wo kann ich mich über die makros \@nil und \@tempa informieren??

mfg
sLy

phi
Moderator
Moderator
Beiträge: 420
Registriert: Fr 6. Feb 2009, 21:28

Beitrag von phi »

sLy hat geschrieben: lässt sich der code auch so umschreiben, dass ich nur noch
\Lampe{L,a}
schreiben müsste?
also dass ich mir den backslash sparen könnte?
Das würde eine Mehrdeutigkeit einbauen. Wie soll das Makro ermitteln, ob mit „a“ ein PSTricks-Punkt oder ein Makroname gemeint ist?
Wo kann ich mich über die makros \@nil und \@tempa informieren??
Die beiden Namen sind bedeutungslos, ich habe sie nur verwendet, weil sie in ähnlicher Bedeutung auch im LaTeX-Kernel verwendet werden.

sLy
Forum-Anfänger
Forum-Anfänger
Beiträge: 29
Registriert: Mi 30. Sep 2009, 17:41

Beitrag von sLy »

Naja ich dachte dabei an sowas:
\newcommand*{\@Lampe}{}
\def\@Lampe#1,#2\@nil{%
  \def\@lamp{\lamp}
  \appto \@lamp{ \}
  \appto \@lamp{#1} 
  \appto \@lamp{{$#2$}}
  \@lamp
}
Also der backslash wird erst innerhalb der Befehlsdefinition vor den ersten Parameter gesetzt. Ich gebe zwar \Lampe{a,L} ein, aber aus dem ersten Parameter wird \a, das ich ja als (B)(C) definiert habe.
Aber wenn du sagst, dass das nicht funktioniert hat sich die Sache erledigt.
phi hat geschrieben: Die beiden Namen sind bedeutungslos, ich habe sie nur verwendet, weil sie in ähnlicher Bedeutung auch im LaTeX-Kernel verwendet werden.
Wenn sie bedeutungslos sind, wozu hast du sie dann verwendet?
Heißt das ich könnte sie auch weglassen?
Welche bedeutung haben sie im LaTeX-Kernel?

mfg
sLy

phi
Moderator
Moderator
Beiträge: 420
Registriert: Fr 6. Feb 2009, 21:28

Beitrag von phi »

sLy hat geschrieben:Naja ich dachte dabei an sowas:
\newcommand*{\@Lampe}{}
\def\@Lampe#1,#2\@nil{%
  \def\@lamp{\lamp}
  \appto \@lamp{ \}
  \appto \@lamp{#1} 
  \appto \@lamp{{$#2$}}
  \@lamp
}
Also der backslash wird erst innerhalb der Befehlsdefinition vor den ersten Parameter gesetzt. Ich gebe zwar \Lampe{a,L} ein, aber aus dem ersten Parameter wird \a, das ich ja als (B)(C) definiert habe.
Aber wenn du sagst, dass das nicht funktioniert hat sich die Sache erledigt.
Das funktioniert schon, wenn der zweite Parameter immer einen Befehlsnamen bezeichnet:
% ungetestet
\def\L@mpeI#1,#2\@nil{%
  \ifcsname#2\endcsname
    \protected@edef\@tempb{%
      \protect\lamp\csname#2\endcsname{$#1$}%
    }%
    \@tempb
  \else
    \PackageError{xyz}{Command `\@backslashchar#2' not defined}{\@ehc}%
  \fi
}
phi hat geschrieben: Die beiden Namen sind bedeutungslos, ich habe sie nur verwendet, weil sie in ähnlicher Bedeutung auch im LaTeX-Kernel verwendet werden.
Wenn sie bedeutungslos sind, wozu hast du sie dann verwendet?
Heißt das ich könnte sie auch weglassen?
Welche bedeutung haben sie im LaTeX-Kernel?
[/quote]
Mit "bedeutungslos" meine ich, dass man auch andere einsetzen kann.
Wenn du das alles verstehen möchtest, musst du dich in die TeX-Programmierung einlesen. Fang mal mit TeX by Topic an, insbesondere den Kapiteln 11, 12 und 13. Danach kannst du im LaTeX-Kernel (source2e.pdf) nach Vorkommnissen von \@tempa und \@nil suchen. \@tempa wird einfach als temporäre Variable verwendet, \@nil als Begrenzer von Makroargumenten.[/code]

sLy
Forum-Anfänger
Forum-Anfänger
Beiträge: 29
Registriert: Mi 30. Sep 2009, 17:41

Beitrag von sLy »

danke funktioniert einwandfrei.
Ich werde mir TeX by Topic mal zu gemüte führen...

mfg
sLy


EDIT:

mein neues Problem: ich will die zugehörige Schaltung zu den Teilen bauen.
Hier mein Code
\newcommand*{\Schaltung}[1]{% die übliche Befehlsdefinition
  \protected@edef\@tempa{#1}%
  \expandafter\@Schaltung\@tempa\@nil
} 
\newcommand*{\@Schaltung}{}
\def\@Schaltung#1,#2,#3,#4,#5,#6,#7,#8,#9\@nil{% Parameter 1-3 sind Einheit in cm, max. X-Wert, max. Y-Wert; Parameter 4-9 stellen die Teile dar
  \psset {unit=#1cm}%
	\newcounter{x}% counter zur ermittlung von xmax und ymax
	\newcounter{y}
	\newcounter{xmax}
	\newcounter{ymax}
	\setcounter{x}{#2}
	\setcounter{y}{#3}
	\setcounter{xmax}{\value{x}}
	\setcounter{ymax}{\value{y}}
	\addtocounter{xmax}{1}
	\addtocounter{ymax}{1}
	\begin{pspicture}[showgrid=false](-1,-1)(\value{xmax},\value{ymax}) %das Standard-Schaltbild
		\def\Standard{%
			\pnode(0,\value{y}){A}%
			\pnode(0,0){B}%
			\pnode(\value{x},0){Y}%
			\pnode(\value{x},\value{y}){Z}%
			\pnode(3,0){C}%
			\pnode(6,0){D}%
			\pnode(9,0){E}%
			\pnode(12,0){F}%
			\pnode(15,0){G}%
			\pnode(18,0){H}%
			\battery (Z)(A){$U_0$}%
		 	\wire (A)(B)%
		 	\wire (Y)(Z)%
		 }%
		\def\bc{\noexpand#4} %hier sollen die Teile zur Schaltung hinzugefügt werden
		\def\cd{\noexpand#5}
		\def\de{\noexpand#6}
		\def\ef{\noexpand#7}
		\def\fg{\noexpand#8}
		\def\gh{\noexpand#9}
		\eappto\Standard{\bc}
		\eappto\Standard{\cd}	
		\eappto\Standard{\de}
		\eappto\Standard{\ef}
		\eappto\Standard{\fg}
		\eappto\Standard{\gh}
		\Standard%	
	\end{pspicture}%
}
Leider funktioniert das nicht so wie es soll.
Fehler:
! Argument of \@Lampe has an extra }.
! Paragraph ended before \@Lampe was complete.
! File ended while scanning use of \@Schaltung.
! Emergency stop.

Minimalbeispiel:
\documentclass{minimal}

\usepackage{etoolbox}
\usepackage{pst-circ}
\usepackage{multido}

\makeatletter

\def \PosA {(B)(C)}
\def \PosB {(C)(D)}
\def \PosC {(D)(E)}
\def \PosD {(E)(F)}
\def \PosE {(F)(G)}
\def \PosF {(G)(H)}

\newcommand*{\Lampe}[1]{%
  \protected@edef\@tempa{#1}%
  \expandafter\@Lampe\@tempa\@nil
} 
\newcommand*{\@Lampe}{}
\def\@Lampe#1,#2\@nil{%
  \ifcsname#1\endcsname
    \protected@edef\@tempb{%
      \protect\lamp\csname#1\endcsname{$#2$}%
    }%
    \@tempb
  \else
    \PackageError{xyz}{Command `\@backslashchar#1' not defined}{\@ehc}%
  \fi
} 
\newcommand*{\Schaltung}[1]{%
  \protected@edef\@tempa{#1}%
  \expandafter\@Schaltung\@tempa\@nil
} 
\newcommand*{\@Schaltung}{}
\def\@Schaltung#1,#2,#3,#4,#5,#6,#7,#8,#9\@nil{%
  \psset {unit=#1cm}%
	\newcounter{x}
	\newcounter{y}
	\newcounter{xmax}
	\newcounter{ymax}
	\setcounter{x}{#2}
	\setcounter{y}{#3}
	\setcounter{xmax}{\value{x}}
	\setcounter{ymax}{\value{y}}
	\addtocounter{xmax}{1}
	\addtocounter{ymax}{1}
	\begin{pspicture}[showgrid=false](-1,-1)(\value{xmax},\value{ymax})
		\def\Standard{%
			\pnode(0,\value{y}){A}%
			\pnode(0,0){B}%
			\pnode(\value{x},0){Y}%
			\pnode(\value{x},\value{y}){Z}%
			\pnode(2,0){C}%
			\pnode(4,0){D}%
			\pnode(6,0){E}%
			\pnode(8,0){F}%
			\pnode(10,0){G}%
			\pnode(12,0){H}%
			\battery (Z)(A){$U_0$}%
		 	\wire (A)(B)%
		 	\wire (Y)(Z)%
		 	\wire (H)(Y)%
		 }%
		\def\bc{\noexpand#4}
		\def\cd{\noexpand#5}
		\def\de{\noexpand#6}
		\def\ef{\noexpand#7}
		\def\fg{\noexpand#8}
		\def\gh{\noexpand#9}
		\eappto\Standard{\bc}
		\eappto\Standard{\cd}	
		\eappto\Standard{\de}
		\eappto\Standard{\ef}
		\eappto\Standard{\fg}
		\eappto\Standard{\gh}
		\Standard%	
	\end{pspicture}%
}
\makeatother
\begin{document}

\begin{center}
\Schaltung{1,13,4,%
		\Lampe{PosA,L_1},%
		\Lampe{PosB,L_2},%
		\Lampe{PosC,L_3},%
		\Lampe{PosD,L_4},%
		\Lampe{PosE,L_5},%
		\Lampe{PosF,L_6}%					
		}
\end{center}
\end{document}

Benutzeravatar
KOMA
TeX-Entwickler
TeX-Entwickler
Beiträge: 2958
Registriert: Fr 4. Jul 2008, 17:28
Kontaktdaten:

Beitrag von KOMA »

Das kann deshalb nicht funktionieren, weil \Lampe aus den Argumenten von \Schaltung bereits beim \protected@edef expandiert wird. Die Definition von Schaltung ist also zu ändern:
\newcommand*{\Schaltung}[1]{%
  \@Schaltung#1\@nil
}
Da ich nicht die gesamte Diskussion gelesen habe, stellt sich mir aber die Frage, warum bei nur 9 verwendeten Argumenten überhaupt mit einer Liste gearbeitet wird, statt einfach 9 Argumente zu verwenden. Außerdem erscheint mir die Definition von \Lampe ebenfalls unnötig umständlich. Wenn ich mir erlaube, das ganze einmal etwas zu vereinfachen:
\documentclass{minimal}

\usepackage{pst-circ}


\def \PosA {(B)(C)}
\def \PosB {(C)(D)}
\def \PosC {(D)(E)}
\def \PosD {(E)(F)}
\def \PosE {(F)(G)}
\def \PosF {(G)(H)}

\makeatletter
\newcommand*{\Lampe}[2]{%
  \expandafter\lamp#1{$L_#2$}%
}
\newcommand*{\Schaltung}[1]{%
  \@Schaltung#1\@nil
}
\newcommand*{\@Schaltung}{}
\def\@Schaltung#1,#2,#3,#4\@nil{%
  \psset {unit=#1cm}%
   \newcounter{x}
   \newcounter{y}
   \newcounter{xmax}
   \newcounter{ymax}
   \setcounter{x}{#2}
   \setcounter{y}{#3}
   \setcounter{xmax}{\value{x}}
   \setcounter{ymax}{\value{y}}
   \addtocounter{xmax}{1}
   \addtocounter{ymax}{1}
   \begin{pspicture}[showgrid=false](-1,-1)(\value{xmax},\value{ymax})
      \def\Standard{%
         \pnode(0,\value{y}){A}%
         \pnode(0,0){B}%
         \pnode(\value{x},0){Y}%
         \pnode(\value{x},\value{y}){Z}%
         \pnode(2,0){C}%
         \pnode(4,0){D}%
         \pnode(6,0){E}%
         \pnode(8,0){F}%
         \pnode(10,0){G}%
         \pnode(12,0){H}%
         \battery (Z)(A){$U_0$}%
          \wire (A)(B)%
          \wire (Y)(Z)%
          \wire (H)(Y)%
          #4
       }%
      \Standard%  
   \end{pspicture}%
}
\makeatother
\begin{document}

\begin{center}
  \Schaltung{1,13,4,%
    \Lampe\PosA1
    \Lampe\PosB2
    \Lampe\PosC3
    \Lampe\PosD4
    \Lampe\PosE5
    \Lampe\PosF{10}% Zur Verdeutlichung von mehrstelligen Nummern
  }
\end{center}
\end{document}
Das erscheint mit im Ergebnis deutlich einfacher. Die Fehlermeldung für nicht definierten \PosA etc. überlasse ich dabei einfach TeX. Man könnte jetzt natürlich \Schaltung auch einfach als Anweisung mit vier Argumenten definieren. Man spart sich dann auch gleich noch die Verwendung von \makeatletter und \makeatother. Dass man bei Schaltung alle Lampen mit einem Argument übergibt, sollte auch noch funktionieren, wenn man eure Definition von \Lampe und dann natürlich auch eure Schreibweise der Argumente von \Lampe verwendet.

sLy
Forum-Anfänger
Forum-Anfänger
Beiträge: 29
Registriert: Mi 30. Sep 2009, 17:41

Beitrag von sLy »

danke für eure Hifle soweit. Ich bin dank eurer Vorschläge ein ganzes stück weiter gekommen.

Leider habe ich ein neues Problem:

Ich möchte, dass sich der Benutzer mit Hilfe eines Befehls (\ElemDef) ein eigenes Element, das auf pst-circ aufbaut, definieren kann.
Ich bin bei der vorigen definition der Elemente geblieben und habe \Schaltung dafür verändert.
Hier noch mal die Definition eines Kondensators:
\newcommand*{\Kondensator}[1]{%
  \protected@edef\@tempa{#1}%
  \expandafter\@Kondensator\@tempa\@nil
} 

\newcommand*{\@Kondensator}{}
\def\@Kondensator#1,#2\@nil{%
  \ifcsname#1\endcsname
    \protected@edef\@tempb{%
      \protect\capacitor\csname#1\endcsname{$#2$}%
    }%
    \@tempb
  \else
    \protected@edef\@tempb{%
    	\protect\capacitor#1{$#2$}%
    }%
    \@tempb
  \fi
}
 
Ich habe mir vorgestellt, dass der Benutzer sich mit der Eingabe \ElemDef{Diode,diode} ein neuen Befehl \Diode definieren kann.
Der zweite Parameter stellt den pst-circ-befehl dar.

Hier mein Minimalbeispiel:
\documentclass{minimal}

\newcommand*{\ElemDef}[1]{\@ElemDef#1\@nil} 
\newcommand*{\@ElemDef}{}
\def\@ElemDef#1,#2\@nil{%
	\newcommand*{\csname#1\endcsname}[1]{%
  	\protected@edef\@tempa{##1}%
  	\expandafter\csname@#1\endcsname\@tempa\@nil
	} 
	\newcommand*{\csname@#1\endcsname}{}
		\def\csname{}@#1\endcsname##1,##2\@nil{%
  		\ifcsname##1\endcsname
    		\protected@edef\@tempb{%
      		\protect\csname#2\endcsname\csname##1\endcsname{$##2$}%
    		}%
    		\@tempb
  		\else
    		\protected@edef\@tempb{%
    		\protect\csname#2\endcsname##1{$##2$}%
    		}%
    		\@tempb
  		\fi
		} 
}
\begin{document}

\ElemDef{Diode,diode}

\begin{pspicture}[showgrid=true](-1,-1)(15,6)
  \pnode(0,5){Y}
  \pnode(0,0){A}
  \pnode(14,0){B}
  \pnode(14,5){Z}
  \battery(Y)(Z){$U_0$}
  \wire(Y)(A)
  \wire(B)(Z)
  \Diode{(A)(B),R_1}
\end{pspicture}

\end{document}

Benutzeravatar
KOMA
TeX-Entwickler
TeX-Entwickler
Beiträge: 2958
Registriert: Fr 4. Jul 2008, 17:28
Kontaktdaten:

Beitrag von KOMA »

Da funktioniert diverses nicht. Zum einen fehlen pst-circ, \makeatletter und \makeatother. Dann definierst Du hier:
\newcommand*{\csname#1\endcsname}[1]{%
\csname neu. Noch deutlicher wird das bei
\def\csname@#1
, wobei hier eben nicht \csname, sondern \csname@ neu definiert wird. Es fehlt also auch noch ein Leerzeichen nach \csname.

Bevor Du Dich an derart komplizierte Dinge wagst, solltest Du im TeXbook oder min. in TeX by Topic mal nachlesen, wie die Expansion in TeX generell und Dinge wie \expandafter und \csname...\endcsname funktionieren. Im Beispiel müsste man erst \csname #1\endcsname expandierten und dann \newcommand, also
\expandafter\newcommand\expandafter*\expandafter{\csname #1\endcsname}
Das ist dann aber noch nicht das Ende der Probleme. Du hast auch wieder Umwege über \protected@edef, wo diese sicher nicht erforderlich sind. Dafür sollte man überlegen, bei der \def\csname-Geschichte eher auf diesem Weg vorzugehen. Desweiteren macht wohl auch:
 \expandafter\csname @#1\endcsname\@tempa\@nil
nicht das, was Du willst. Denn hier wird lediglich die Expansion von \csname verzögert. Du willst aber vermutlich die Expansion des Makros der Expansion von \csname @#1\endcsname verzögern.

Ich glaube aber, dass es den Rahmen dieses Forums sprengt, Dir die low-level TeX-Grundlagen und die Feinheiten der Expansion beizubringen. Du begibst Dich da schon recht tief in den Teil von TeX, in dem man sich leicht verirren kann und auch ein Experte gut aufpassen muss.

Um es kurz zu machen: Neben anderen Fehlern funktioniert die komplette Expansionslogik Deines Codes nicht.

Antworten