CoCreate Modeling FAQ: Lisp programming in CoCreate Modeling
How to edit Lisp files?
There are a couple of editors which support syntax highlighting for Lisp.
Some of them also can highlight / find / jump to opening and closing brackets which
is one of the problems in Lisp for beginners (well not only for them
).
Here is a tiny list:
There are syntax highlighting files for ConTEXT (
http://context.cx/highlighters.php?filter=A-D),
UltraEdit and Notepad++. Notepad++ ships with CoCreate
Modeling 2007 and later.
Wie schreibt man am besten Lisp-Dateien?
Es gibt eine Reihe von Editoren (Text-Bearbeitungs-Programmen), die auch die Programmiersprache Lisp
unterstützen. Einige können auch das Auffinden von schliessenden und öffnenden Klammern erleichtern.
Eine kleine Liste von solchen Programmen findet sich hier:
Einige Editoren beherrschen automatische Syntaxhervorhebung, wie zum
Beispiel ConTEXT (
http://context.cx/highlighters.php?filter=A-D),
UltraEdit and Notepad++. Notepad++ wird mit CoCreate
Modeling 2007 und späteren Versionen ausgeliefert.
--
DerWolfgang, last updated 2007/04/05
How do I load a Lisp file into CoCreate Modeling?
There are several ways to do this:
- By using the Common Lisp load
command in the user input line:
(load "c:/there/is/no/place/like/foo.lsp").
- Via the file browser - click File/Open, change the file type to Lisp (
*.lsp
),
then navigate to the file and open it.
- By dragging the Lisp file from an Explorer window and dropping it over
CoCreate Modeling.
If you have some Lisp code which you want CoCreate Modeling to load automatically during startup,
you should place your Lisp code in one of the customization directories which CoCreate Modeling
visits during startup. The choice of customization directory depends on whether
you want the code to be loaded just for you, for your team, a site, or the whole
company. For more details, start with the
FAQ entry on automatic loading.
Wie lade ich eine Lisp-Datei in CoCreate Modeling?
Da gibt es viele Wege:
- Indem man in der CoCreate Modeling-Eingabezeile das Common-Lisp-Kommando
Lisp load benutzt:
(load "c:/wo/auch/immer/foo.lsp").
- Über den Dateibrowser - auf Datei/Öffnen klicken, den Dateityp auf
Lisp (
*.lsp
) ändern, die Datei suchen und auswählen.
- Indem man sich die Lisp-Datei in einem Explorer-Fenster greift und über
CoCreate Modeling fallen läßt (drag&drop).
Hat man ein Stückchen Lisp-Code, das bei jedem Start automatisch von CoCreate Modeling
geladen werden soll, kann man die Datei in einem der Anpassungsverzeichnisse
ablegen, die CoCreate Modeling beim Start durchsucht. Welches Anpassungsverzeichnis man
auswählt, hängt davon ab, ob der Code nur für einen einzelnen
Anwender, eine Arbeitsgruppe, eine Firmenniederlassung oder für die ganze
Firma geladen werden soll. Mehr dazu im
FAQ-Eintrag zum automatischen Laden.
--
ClausBrod - 19 Nov 2006
(setf foo (- 1 0.01)) - Lisp says that foo is 0.010000000000000009. Is subtraction in Lisp buggy?
I've written a macro which produces a profile, but sometimes, depending on the input, the profile is reported as not closed.
Chances are that you are using floating-point calculations in your profile
macro, and that you are running into inaccuracies which is
a problem that every programmer encounters when calculating
with floating-point values.
This kind of problem can be easily demonstrated by running the following two
Lisp commands:
(setf foo (-1 0.01))
(display foo)
foo
will displays its value as
0.010000000000000009
in the output box - but
shouldn't that read
0.01
? How can a simple subtraction like this fail?
In fact, this is not even a CoCreate Modeling or Lisp problem. A similar subtraction will
fail in
CoCreate Drafting as well:
LET FOO ( 1 - (VAL '0.99'))
DISPLAY (FOO > 0.01) {Result = 1}
So if both
CoCreate Modeling and
CoCreate Drafting fail in such
a simple subtraction, maybe it's a
CoCreate problem? Are their programmers too
dumb to even get this right?
No. Similar code fails in other languages as well. Here's an example using
VBscript (execute the script using
wscript
or
cscript
):
Foo = 1 - 0.99
If Foo > 0.01 Then
WScript.Echo("Foo is larger than 0.01")
Else
WScript.Echo("Foo is equal to or smaller than 0.01")
End if
The truth is that surprises like this can happen to you
in
any language and in
any application. The reason is that
the way in which CPUs represent floating-point
values is imprecise by definition - there are only a
limited number of bits available to store floating-point
values. A good backgrounder on this is the famous white
paper
What Every
Computer Scientist Should Know About Floating-Point Arithmetic.
Alternatively, check out the (German)
article at
http://www.aboutwebdesign.de/awd/content/1014916256.shtml
which is less formal and good enough as an introduction for
most people.
Bottom line of those articles: Calculations with floating-point values are
subject to inaccuracies because their representation in computer memory
has only a finite number of "digits", and sometimes you simply cannot
completely avoid floating-point errors. There are, however, techniques
which reduce and control the error:
- Work with tolerances: When comparing floating-point
values, allow a certain margin of error, i.e. consider
two floating-point values as equal if they differ by a
certain epsilon value at most. (This is why we call this
technique "Epsilontik", at least in German.)
- Work with precise data types: Most programming languages
provide two floating-point data types, i.e. single
precision (C/C++: float, Lisp: short float) and double
precision (C/C++: double). When in doubt, choose the more
precise data type.
- Avoid floating-point calculations wherever you can.
Sometimes it is possible to do part of the calculations
using integer arithmetic.
- Avoid combining floating-point values in the same term if
they are of completely different magnitudes. In a
sequence of calculations, first add/subtract/whatever
those floating-point pairs which are roughly in the same
magnitude range.
- Use algorithms which try to detect when two
floating-point values really should be 100% identical,
and which can "fix" the values on the fly.
- Avoid algorithms which accumulate inaccuracies. For
example, to rotate a transformation by 170 degrees, apply
one 170 degress rotation rather than iteratively applying
17 rotations of 10 degrees.
--
ClausBrod
How can I use double-quotes in a literal Lisp string?
Imagine you want to replace all double-quote characters in a Lisp string
using the Integration Kit function
sd-string-replace
. Seems we have a
problem here: Literal strings in Lisp start with a double-quote character,
and the next double-quote character in the string ends it. Hmmm...
The solution for this is to use so-called
escape characters. In Lisp
strings, backslashes are used as escape characters. (There are
similar mechanisms in many programming languages, and the backslash
character is used suspicously often for this purpose. Is there a
secret
backslash conspiracy going on here?
) Example:
(oli:sd-string-replace string "\"" "!")
This replaces all double-quote characters in
string
with exclamation
marks. Note that two double-quote characters follow after the backslash.
The special meaning of the first double-quote character ("end the current
literal string") is ignored by the Lisp reader in this case because it
is preceded by an escape character.
See also the section on the
Single Escape Character
in the Common Lisp
HyperSpec.
Wie gebe ich in einem Lisp-String doppelte Anführungszeichen an?
Nehmen wir mal an, wir wollten in einem Lisp-String alle doppelten Anführungszeichen
durch ein anderes Zeichen ersetzen, und zwar mit der Funktion
sd-string-replace
aus dem Integration Kit. Das könnte problematisch werden, denn ein
Zeichenkettenliteral in Lisp beginnt mit einem doppelten Anführungszeichen,
und das nächste Anführungszeichen beendet es. Hmmm...
Die Lösung sind die sogenannten
ESCAPE- oder Abdeckzeichen. In Lisp-Zeichenketten
wird der Rückstrich (Backslash) als solches Umschaltzeichen verwendet.
(In anderen Programmiersprachen gibt es ähnliche Mechanismen, und das
Backslash-Zeichen wird verdächtig oft für diesen Zweck eingesetzt -
eine Backslash-Verschwörung?
) Beispiel:
(oli:sd-string-replace string "\"" "!")
Das Kommando ersetzt alle doppelten Anführungszeichen durch
Ausrufezeichen. Man beachte, daß nach dem Backslash-Zeichen
zwei doppelte Anführungszeichen folgen. Die besondere Bedeutung
des ersten Anführungszeichen ("beende die aktuelle literale
Zeichenkette") wird von Lisp in diesem Fall ignoriert, weil ein
Backslash-Zeichen vorangeht.
Siehe auch den Abschnitt
Single Escape Character
in der
HyperSpec-Dokumentation zu Common Lisp.
--
ClausBrod
How do I search for a backslash character in a string?
See the explanations
above on escape characters to understand
why the following would work:
(oli:sd-string-match-pattern-p "*\\\\*" somestring)
Hint: Four backslashes are required in the pattern string because the pattern
uses a special regular expression syntax in which the backslash has a special
meaning, just like in Lisp. So you need to "escape" once because of the
pattern language, and then another time because of Lisp.
Wie suche ich einen Rückstrich in einer Zeichenkette?
Siehe die Erläuterungen zum Thema Escape-Zeichen
weiter oben;
damit versteht man (hoffentlich), warum das folgende funktioniert:
(oli:sd-string-match-pattern-p "*\\\\*" somestring)
Hinweis: Vier Rückstriche braucht man deswegen im Muster-String, weil
das Muster in einer speziellen Syntax für reguläre Ausdrücke
angegeben wird, in der der Rückstrich ebenso wie in Lisp eine spezielle
Bedeutung hat. Man muß also einmal wegen der regulären Ausdrücke
und dann noch einmal wegen Lisp die erforderlichen Rückstriche
hinzufügen.
--
ClausBrod - 13 Jan 2005
What's the difference between setf
and setq
?
setf
is a macro which builds on
setq
, but also allows to do funky stuff like this:
(setf (fifth somelist) 42)
Note that the first argument isn't really an ordinary variable, but rather the
description (as a Lisp form) of a place where the value is to be written to.
http://www.supelec.fr/docs/cltl/clm/node80.html lists which kind of forms
can be used with
setf
.
In the majority of cases, this special evaluation isn't needed, so you're
probably slightly better off (in terms of performance) by using
setq
.
If you get it wrong, Lisp will kindly remind you with an error message, and then
you can then still use
setf
instead when required.
Historically,
setf
is the abbreviation for
setfq
, which,
according to
Evolution of Lisp,
stood for "quote the function and evaluate everything else".
Was ist der Unterschied zwischen setf
und setq
?
setf
ist ein Makro, das auf
setq
aufbaut, zusätzlich aber ulkige Dinge wie dies hier
erlaubt:
(setf (fifth somelist) 42)
Das erste Argument von
setf
ist keine normale Variable, sondern eher die Beschreibung
(als Lisp-Form) eines Platzes, an den der Wert geschrieben werden soll.
http://www.supelec.fr/docs/cltl/clm/node80.html listet auf, welche Arten von Lisp-Formen
bei
setf
erlaubt sind.
In der Mehrzahl der Fälle braucht man solche speziellen Erweiterungen nicht,
fährt also mit
setq
etwas besser (zumindest in Sachen Geschwindigkeit).
Macht man's falsch, erinnert Lisp einen ohnehin freundlicherweise in Form von
Fehlermeldungen daran, und dann kann man immer noch auf
setf
umsteigen.
Aus historischer Sicht ist
setf
eine Abkürzung für
setfq
, was
laut
Evolution of Lisp
für "quote the function and evaluate everything else" steht.
--
ClausBrod
When I load my Lisp code, I get an "Unexpected end of input stream" error.
When the Lisp interpreter loads a file, it parses and checks it for syntax problems. One
such syntax problem is when you open parentheses, but forget to close then. Example:
(display "This will not work"
When saving this to a file called
foo.lsp
and loading it into CoCreate Modeling,
you'll get an error message which reads like:
LISP error: Unexpected end of #<input stream "foo.lsp">.
What the Lisp interpreter is trying to say is that it arrived at the end of the file
before all forms were closed properly, i.e. the number of opening parentheses does
not match the number of closing parentheses. You can also run into this issue if you
do not properly terminate a literal string, i.e. you forget the closing double-quote.
So when you get this error message, open the Lisp file in an editor and look for
missing parentheses and quotes.
A
good Lisp editor will highlight matching parentheses, and also
indent your code automatically so that any imbalances become readily apparent.
This error message also used to occur if the Lisp file contains
a comment in the last line, and that last line does not end with a newline character.
This has been fixed in CoCreate Modeling 2007.
Wenn ich meinen Lisp-Code lade, bekomme ich eine Fehlermeldung "Unexpected end of input stream"
Wenn der Lisp-Interpreter eine Datei lädt, prüft er sie auch auf Syntaxfehler.
Ein solcher Syntaxfehler ist es, wenn man eine Klammer öffnet, aber sie zu schließen vergißt. Beispiel:
(display "Das klappt nicht"
Speichert man das in eine Datei namens
foo.lsp
und lädt diese dann in CoCreate Modeling,
bekommt man eine Fehlermeldung wie diese:
LISP error: Unexpected end of #<input stream "foo.lsp">.
Der Lisp-Interpreter versucht hier zu sagen, daß er am Ende der Datei angekommen ist, bevor
alle Lisp-Formen ordentlich geschlossen wurden, daß also die Anzahl der öffnenden Klammern
nicht der Anzahl der schließenden Klammern entspricht. Das Problem kann auch auftreten,
wenn man eine literale Zeichenkette nicht ordentlich abschließt, also die
Gänsefüßchen am Ende vergißt. Wenn man diese Meldung bekommt,
öffnet man also einfach die betreffende Datei in einem Editor und sucht nach fehlenden
Klammern oder Gänsefüßchen.
Ein
guter Lisp-Editor hebt automatisch zugehörige Klammernpaare hervor
und rückt auch automatisch den Code ein, so daß solche Strukturfehler sofort auffallen.
Die Fehlermeldung wurde bisher auch dann angezeigt, wenn die Lisp-Datei
einen Kommentar in der letzten Zeile beinhaltet und diese letzte Zeile nicht mit einem
Zeilenvorschub abgeschlossen ist. Dieses Problem ist in CoCreate Modeling 2007
behoben.
--
ClausBrod - 20 April 2005, last updated 21 December 2005
When I try to call an Integration Kit function SD-XXX
, I get an error message: "The function SD-XXX is undefined"
First, check the spelling.
Second, all Integration Kit APIs are in the
OLI
LISP package. A Lisp package is
the equivalent of a
namespace in other languages. To specify the package, preprend
the function name with
OLI:
, or add a
use-package :oli
statement at the
beginning of your Lisp file.
(SD-XXX param1 param2) ;; package not specified, call might fail
(OLI:SD-XXX param1 param2) ;; MUCH better!
(use-package :OLI) ;; alternative method
(SD-XXX param1 param2)
See the
section on packages
in the Lisp
HyperSpec for more details. The
introduction on packages
in David Lamkins'
Successful Lisp is also a good start
to learn more about this topic.
Actually, in older versions of CoCreate Modeling it was sometimes to use IKIT APIs without
specifying the
OLI
package. This was an undocumented side effect of our
internal code structure; it just so happened that some code
executed the required
use-package
call, or at least most of the time.
Recent versions of CoCreate Modeling are less forgiving; to reduce the likelihood of
name clashes, we now enforce proper specification
of the
OLI
package.
Wenn ich die Integration-Kit-Funktion SD-XXX
aufrufe, bekomme ich eine Fehlermeldung "The function SD-XXX is undefined"
Zuallererst: Korrekte Schreibweise des Kommandos überprüfen.
Zum zweiten: Alle APIs des Integration Kit befinden sich im Lisp-Package
OLI
. Ein
Lisp-Package ist die Entsprechung zu
Namensräumen in anderen Programmiersprachen.
Um das Package anzugeben, hängt man vor den aufzurufenden Funktionsnamen einfach
OLI:
, oder aber man benutzt das Kommando
use-package :oli
am Anfang der Lisp-Datei.
(SD-XXX param1 param2) ;; Package nicht angegeben; das wird schiefgehen
(OLI:SD-XXX param1 param2) ;; Viel besser!
(use-package :OLI) ;; Alternative Methode
(SD-XXX param1 param2)
Siehe auch den
Abschnitt zu Packages
in der Lisp-HyperSpec für detailliertere Erläuterungen. Die
Einführung zu Packages im Buch
Successful Lisp von David Lamkins ist auch
ein guter Startpunkt, um mehr zu diesem Thema zu lernen.
In älteren Versionen von CoCreate Modeling konnte man übrigens manchmal straflos
IKIT-APIs ohne Angabe des Packagenamens (
OLI
) aufrufen. Das war ein undokumentierter
Seiteneffekt der internen Codestruktur; zufälligerweise benutzte interner Code
den passenden Aufruf von
use-package
und nahm diese Anmeldung nicht zurück.
Neuere Versionen von CoCreate Modeling sind weniger nachsichtig in diesem Punkt. Um die
Wahrscheinlichkeit von Namenskollisionen zu reduzieren, erzwingt CoCreate Modeling seit einiger
Zeit die korrekte Angabe des Packagenamens.
--
ClausBrod
When running my macro X, I get an error message reporting that "XXX cannot be coerced to YYY".
In the example displayed here, a function is called which expects a parameter of type
double
, but instead is passed a structure of type
GPNTDOCU
. So this type of error
message usually indicates a programming error (incorrect parameter passing)
in the Lisp code which has been executed.
As you can see from the screenshot, the error message itself is slightly buggy, too .-)
Wenn ich das Makro X ausführe, bekomme ich eine Fehlermeldung "XXX cannot be coerced to YYY".
Im Beispiel wird eine Funktion aufgerufen, die einen Parameter vom Typ
double
erwartet,
stattdessen aber vom Aufrufer eine Struktur vom Typ
GPNTDOCU
erhält. Diese Art von
Fehlermeldung deutet also auf einen Programmierfehler (falsche Parameterübergabe)
im Lisp-Code hin, der gerade ausgeführt wurde.
Wie man dem Bild entnehmen kann, ist allerdings die Fehlermeldung selbst auch nicht ganz fehlerfrei .-)
--
ClausBrod
How do I port customization code from HP-UX to Windows?
If you only used the official APIs in the Integration Kit, there should rarely
be a need to port anything. Almost all of them are cross-platform.
However, there are exceptions. One such exception is when you use
sd-sys-background-job
or
sd-sys-exec
to execute external commands.
Such code will only continue to work if the external commands are
available on the Windows platform as well.
In some cases, the HP-UX code will try to execute UNIX shell commands, such as
grep
,
awk
,
sort
,
ls
etc etc. For many external commands, you will actually find
equivalent Integration Kit APIs or Lisp commands, which is the clearly preferable
implementation. For instance, rather than using
cp
as an external command, use
the
sd-copy-file
API; and to create a directory,
sd-make-directory
is more
portable than
mkdir
. Consult the "Filing and Operating System" section of
the Integration Kit documentation for details. Lisp also has a number of
system-level commands, such as to access files, so the Common Lisp
HyperSpec is
also a good place to start your search.
Sometimes, however, you will still have to use external commands. The good news
is that the DOS batch language (which can be used in
sd-sys-exec
commands
on Windows) is actually quite flexible and powerful, even though the syntax differs
significantly from UNIX systems. For example,
grep
can almost always be replaced
by
findstr
,
rm
is the same as
del
,
dir
matches
ls
most of the time etc.
A good book on the subject is
Windows NT Shell Scripting by Timothy Hill. Also, there are many sources in the Internet which discuss batch file programming, such as
Rob van der Woude's Scripting Pages.
Yet another alternative is to use one of the UNIX-lookalike tool collections
and shell environments out there. These allow to continue to use
grep
,
ksh
and friends, so instead of porting your macros, you simply install a UNIX
shell environment on your systems.
There are both commercial and free implementations. Examples:
Wie portiert man Anpassungen von HP-UX nach Windows?
Solange man nur die offiziellen APIs im Integration Kit verwendet, sollte es selten
überhaupt einen Bedarf geben, irgendetwas zu portieren. Fast alle dieser APIs
sind für alle Plattformen gültig.
Es gibt aber Ausnahmen. Eine solche Ausnahme ist es, wenn man
sd-sys-background-job
oder
sd-sys-exec
zum Ausführen externer Kommandos verwendet. Solcher Programmcode
wird nur dann auf der neuen Plattform (in unserem Beispiel Windows) weiter funktionieren,
wenn die externen Kommandos dort ebenfalls verfügbar sind.
In einigen Fällen versuchen HP-UX-Makros, UNIX-Kommandos auszuführen,
beispielsweise
grep
,
awk
,
sort
oder
ls
. Für viele solcher externen Kommandos
kann man übrigens Entsprechungen in Lisp selbst oder im Integration Kit finden -
ganz klar ist das einer plattformabhängigen Programmierung vorzuziehen.
Beispielsweise kann man statt
cp
als externem Kommando einfach
sd-copy-file
verwenden; zum Anlegen eines Verzeichnisses ist
sd-make-directory
portabler
als
mkdir
. Der Abschnitt "Filing and Operating System" in der Dokumentation
zum Integration Kit ist hier besonders interessant. Lisp stellt ebenfalls
eine Reihe von Kommandos zur Verfügung, die von der verwendeten Plattform
abstrahieren, beispielsweise zum Zugriff auf Dateien - die
HyperSpec-Dokumentation
zu Common Lisp ist also auch hier ein guter Anlaufpunkt.
Manchmal muß man aber doch auf externe Kommandos ausweichen.
Die gute Nachricht ist hier, daß die Batch-Sprache von DOS (die
man für mit
sd-sys-exec
ausgeführte Kommandos einsetzen kann)
recht flexibel und vielseitig ist, auch wenn die Syntax doch oft erheblich
von UNIX-Systemen abweicht. Beispielsweise kann man
grep
fast immer durch
findstr
ersetzen;
rm
ist fast identisch mit
del
, und
dir
entspricht meistens
ls
. Ein gutes Buch zu diesem Thema ist
Windows NT Shell Scripting
von Timothy Hill. Es gibt auch viele weitere Quellen im Internet, die Batch-Programmierung
behandeln, beispielsweise
die Scripting-Seiten von Rob van der Woude.
Eine weitere Alternative sind UNIX-ähnliche Werkzeugsammlungen
und Shell-Umgebungen, die es erlauben, wie gewohnt
grep
,
ksh
und Kumpane zu verwenden. Anstatt also die Makros zu portieren, installiert
man einfach eine solche Shell-Umgebung auf den Zielsystemen.
Es gibt sowohl kommerzielle als auch freie Implementierungen. Beispiele:
--
ClausBrod, last update 2007-04-03
How can I run CoCreate Drafting (ME10) macros in Annotator?
The IKIT function
sd-execute-annotator-command
is a convenient way to do
this. For example, if you have an OSDD macro which takes two parameters,
you can call it like this:
(oli:sd-execute-annotator-command
:cmd (format nil "my_osdd_macro ~A ~A" param1 param2))
Note how
format
is used to build the full macro string before passing
it to Annotator.
Wie kann ich Makros für CoCreate Drafting (ME10) in Annotation laufen lassen?
Das geht recht bequem über die IKIT-Funktion
sd-execute-annotator-command
.
Wenn man zum Beispiel ein OSDD-Makro hat, das zwei Parameter erwartet, kann man
es wie folgt rufen:
(oli:sd-execute-annotator-command
:cmd (format nil "my_osdd_macro ~A ~A" param1 param2))
Man beachte, wie hier
format
benutzt wird, um den Makroaufruf
aufzubauen, bevor man ihn an Annotator schickt.
--
ClausBrod - 11 Jun 2005
to top