| | | Gut zu wissen: Hilfreiche Tipps und Tricks aus der Praxis prägnant, und auf den Punkt gebracht für Autodesk Produkte | | | | PNY wird von NVIDIA zum Händler des Jahres gewählt – zum dritten Mal in Folge, eine Pressemitteilung
|
Autor
|
Thema: funktionsübergreifende funktionen (1310 mal gelesen)
|
benwisch Mitglied Bautechniker, CAD-Konstrukteur
Beiträge: 375 Registriert: 01.02.2001 Autocad 2005-2010 Microstation V8 Photoshop CS4 + Camera Raw Nikon Capture NX2 Nikon D90
|
erstellt am: 06. Mai. 2003 20:17 <-- editieren / zitieren --> Unities abgeben:
ich bin mir etwas unsicher, wie es wohl richtiger ist... bsp. code
Code:
(defun NAME () (setq N "Ben") ) ;_ end defun(defun ZAHL () (setq Z 385) ) ;_ end defun
funktionsübergreifend Code:
(defun MIX () (strcat (NAME) " ist " (itoa (ZAHL)) " Jahre alt") ) ;_ end defun(defun C:HP () (alert (strcat "Das ist ein Test\n-------------------\n" (MIX)) ) ;_ end alert ) ;_ end defun
oder besser so...
Code:
(defun MIX2 (N Z) (strcat N " ist " (itoa Z) " Jahre alt") ) ;_ end defun(defun C:HP2 () (alert (strcat "Das ist ein Test\n-------------------\n" (MIX2 (NAME) (ZAHL)) ) ;_ end strcat ) ;_ end alert ) ;_ end defun
ich tippe mal das 2., also werte der funktion immer übergeben für mich als anfänger wirkt es übersichtlicher aber ist es auch der richtige weg so zu programmieren ??
Eine Antwort auf diesen Beitrag verfassen (mit Zitat/Zitat des Beitrags) IP |
mapcar Mitglied CADmin
Beiträge: 1250 Registriert: 20.05.2002 Time flies like an arrow, fruit flies like a banana (Groucho Marx)
|
erstellt am: 07. Mai. 2003 10:12 <-- editieren / zitieren --> Unities abgeben: Nur für benwisch
Das Konzept ist mir noch nicht so ganz klar. Die Funktionen (name) und (zahl) setzen jeweils eine Variable N bzw. Z, aber es gibt keinen Hinweis auf den Namensraum. Sollen sie global sein, dann solltest du dich an die Konventionen halten und sie *N* bzw. *Z* nennen. Natürlich kann das Ganze auch weiter unten angesiedelt sein - aber auf Variablen aus übergeordneten Funktionen zuzugreifen, ist eine sehr unübersichtliche Sache. Schau dir dieses Beispiel an:
Code:
(defun tu-was( / p1 p2) (setq p1'(10 10 0)p2'(25 25 0)) (zeichne-was) )(defun zeichne-was( / p3) (setq p3(berechne-was)) (melde-was) ... ) (defun berechne-was( / ) (mapcar'+ p1 p2) ) (defun melde-was( / ) (princ "Aus ") (princ p1) (princ " und ") (princ p2) (princ " wurde ") (princ p3) (princ " berechnet.\n") )
p1 und p2 sind lokal zu tu-was, p3 ist lokal zu zeichne-was. Trotzdem kann aus den Funktionen berechne-was und melde-was darauf zugegriffen werden. AutoLisp-Variablen sind niemals lokal, auch wenn alle Handbücher dies behaupten. Sie sind zwar lokal zu der Funktion, in der sie in die Formalen Argumenteniste eingetragen sind, aber sie sind auch lokal zu allen anderen Funktionen, die daraus direkt oder indirekt aufgerufen werden. Stell dir das Beispiel ein wenig komplexer vor: Zig Dateien und Funktionen, und dir fällt die Funktion melde-was in die Hände. Du hast kaum eine Chance herauszufinden, wo diese Variablen p1, p2, p3 eigentlich herkommen, denn die Aufrufkette kannst du im Quellcode ja kaum rückwärts nachvollziehen. Fazit: Entweder man verzichtet weise auf die Verwendung subglobaler Variablen, oder man kennzeichnet sie namentlich so, dass sie rückverfolgbar sind: Code:
(defun tu-was( / tu-was*p1 tu-was*p2) (setq tu-was*p1'(10 10 0)tu-was*p2'(25 25 0)) (zeichne-was) )(defun zeichne-was( / zeichne-was*p3) (setq zeichne-was*p3(berechne-was)) (melde-was) ... ) (defun berechne-was( / ) (mapcar'+ tu-was*p1 tu-was*p2) ) (defun melde-was( / ) (princ "Aus ") (princ tu-was*p1) (princ " und ") (princ tu-was*p2) (princ " wurde ") (princ zeichne-was*p3) (princ " berechnet.\n") )
Die übersichtlichste und beste Lösung ist aber immer, alle Argumente durchzureichen, es sei denn, sie sind aus gutem Grund *global*. Dann spielt es auch keine Rolle, wie die Variablen in den einzelnen Funktionen heissen: Code:
(defun tu-was( / p1 p2) (setq p1'(10 10 0)p2'(25 25 0)) (zeichne-was p1 p2) )(defun zeichne-was(a b / p3) (setq p3(berechne-was a b)) (melde-was a b p3) ... ) (defun berechne-was(a1 a2 / ) (mapcar'+ a1 a2) ) (defun melde-was(v1 v2 v3 / ) (princ "Aus ") (princ v1) (princ " und ") (princ v2) (princ " wurde ") (princ v3) (princ " berechnet.\n") )
Nur diese Funktionen sind wirklich wiederverwertbar - die Funktionen aus den ersten beiden Beispielen laufen nur in dem gegebenen Kontext. Also: Deine Funktionen (name) und (zahl) erzeugen subglobale Variablen - darauf solltest du verzichten. Sie machen auch keinen Sinn, sie setzen immer wieder die selben Variablen auf den selben Wert - einmal reicht doch, wenn überhaupt. HTH, Axel ------------------ Meine AutoLisp-Seiten Meine private Homepage Mein Angriff auf dein Zwerchfell Mein Lexikon der Fotografie Mein gereimtes Gesülze Meine Überzeugung... Eine Antwort auf diesen Beitrag verfassen (mit Zitat/Zitat des Beitrags) IP |
benwisch Mitglied Bautechniker, CAD-Konstrukteur
Beiträge: 375 Registriert: 01.02.2001
|
erstellt am: 07. Mai. 2003 17:58 <-- editieren / zitieren --> Unities abgeben:
Zitat: Original erstellt von mapcar: Das Konzept ist mir noch nicht so ganz klar. Die Funktionen (name) und (zahl) setzen jeweils eine Variable N bzw. Z, aber es gibt keinen Hinweis auf den Namensraum. Sollen sie global sein, dann solltest du dich an die Konventionen halten und sie *N* bzw. *Z* nennen. Natürlich kann das Ganze auch weiter unten angesiedelt sein - aber auf Variablen aus übergeordneten Funktionen zuzugreifen, ist eine sehr unübersichtliche Sache.
hi axel, erstmal danke für deine antwort... um variablen ging es mir auch nicht, sondern mehr um einen ausdruck einer funktion... sorry, habe im bsp-code das deklarieren vergessen und mich nglücklich ausgedrückt... Zitat: Original erstellt von mapcar: Die übersichtlichste und beste Lösung ist aber immer, alle Argumente durchzureichen, es sei denn, sie sind aus gutem Grund *global*. Dann spielt es auch keine Rolle, wie die Variablen in den einzelnen Funktionen heissen:
konkretes beispiel an dem ich gerade "übe" diese funktion erzeugt in der registry einen neuen REG_SZ wert im "apploadbereich".
Code:
(defun BW_SETNEWSTARTUP (FILE) (vl-registry-write (strcat (BW_CURRENTAREA) "\\" (BW_GETPROFIL) "\\Dialogs\\Appload\\Startup") (strcat (itoa (1+ (BW_GETAPPCOUNTER))) "Startup") FILE ) ;_ end vl-registry-write (vl-registry-write (strcat (BW_CURRENTAREA) "\\" (BW_GETPROFIL) "\\Dialogs\\Appload\\Startup") "NumStartup" (itoa (1+ (BW_GETAPPCOUNTER))) ) ;_ end vl-registry-write ) ;_ end defun
wie man sieht, benötigt sie aber weitere funktionen/module um den richtigen REG-schlüssel zu finden. ich entschied mich für neue funktionen, weil ich mir dachte, da sind so viele zwischenschritte nötig, daß ich das in anderen funktionen/module am besten erledige, was ja auch schliesslich das ganze übersichtlicher macht. hier nun die anderen module
Code:
(defun BW_CURRENTAREA () (strcat "HKEY_CURRENT_USER\\Software\\Autodesk\\AutoCAD\\" (vl-registry-read "HKEY_LOCAL_MACHINE\\SOFTWARE\\Autodesk\\AutoCAD" "CurVer") "\\" (vl-registry-read (strcat "HKEY_LOCAL_MACHINE\\SOFTWARE\\Autodesk\\AutoCAD\\" (vl-registry-read "HKEY_LOCAL_MACHINE\\SOFTWARE\\Autodesk\\AutoCAD" "CurVer") ;_ end vl-registry-read ) ;_ end strcat "CurVer" ) ;_ end vl-registry-read "\\Profiles" ) ;_ end strcat ) ;_ end defun(defun BW_GETPROFIL () (vl-registry-read (BW_CURRENTAREA))) (defun BW_GETAPPCOUNTER () (atoi (vl-registry-read (strcat (BW_CURRENTAREA) "\\" (BW_GETPROFIL) "\\Dialogs\\Appload\\Startup") "NumStartup" ) ;_ end vl-registry-read ) ;_ end atoi )
auch hier greifen wieder funktionen auf andere zu, um werte zu erhalten und genau hier frage ich mich, ob es grundsätzlich besser ist, alle erforderlichen werte, die eine funktion benötigt, gleich zu übergeben. ich sehe doch so gleich, was die funktion an werten benötigt um einen ausdruck zu erhalten. beispiel
Code:
(defun BW_GETAPPCOUNTER (curver profil) (atoi (vl-registry-read (strcat curver "\\" profil "\\Dialogs\\Appload\\Startup") "NumStartup" ) ;_ end vl-registry-read ) ;_ end atoi ) ;_ end defun(defun BW_GETPROFIL (CURVER) (vl-registry-read CURVER) ) ;;; irgendwo der aufruf ... (BW_GETAPPCOUNTER (BW_CURRENTAREA)(BW_GETPROFIL (BW_CURRENTAREA))
zugegeben, der verschachtelte aufruf hier, sieht jetzt ein bisschen komisch aus. bw_getprofil benötigt aber ein wert der von bw_currentarea generiert wird (wollte wiederholungen von codezeilen vermeiden). es ist hier nur ein einfaches bsp., aber die frage stellte sich mir halt beim schreiben des codes. ich habe mal den code angehängt... bw_install = lsp/fas/vlx pfad+Startgruppe hinzufügen bw_uninstall = gegenteil von oben [Diese Nachricht wurde von benwisch am 07. Mai 2003 editiert.] Eine Antwort auf diesen Beitrag verfassen (mit Zitat/Zitat des Beitrags) IP |
mapcar Mitglied CADmin
Beiträge: 1250 Registriert: 20.05.2002 Time flies like an arrow, fruit flies like a banana (Groucho Marx)
|
erstellt am: 08. Mai. 2003 09:59 <-- editieren / zitieren --> Unities abgeben: Nur für benwisch
Ben, jetzt wird es klarer, worum es dir geht. Zunächst mal der 'Aufruf'(der abgesehen von den Grossbuchstaben keineswegs komisch aussieht - so sieht Lisp einfach aus): Werden an dieser Stelle (d.h. im Kontext des Aufrufs) diese beiden Sachen benötigt? 'Irgendwo' ist natürlich ein weiter Begriff, aber es sieht nicht so aus. Lediglich (bw_getappcounter) benötigt sie, also ist es auch die Aufgabe von (bw_getappcounter), diese zu besorgen. Man könnte diese Art des Aufrufs als Aufweichen des Funktionsprinzips auffassen - oder mit anderen Worten: Wenn sich vielleicht mal irgendwelche Details in der Registry ändern, musst du bei Version 1 nur (bw_getappcounter) modifizieren, bei Version 2 musst du aber den ganzen Quellcode durchsuchen und auch alle Aufrufe anpassen, was einen erheblich höheren Aufwand bedeutet. Dein Argument, dass man da 'gleich das Notwendige sieht', zieht nicht. Im Gegenteil - Funktionen sollen ja Black Boxes sein, d.h. Argumente rein, Wert raus, wie bei einem Kaugummi-Automaten. Wie das ganze funktioniert, soll dem Aufrufer verborgen bleiben. Bei einer Änderung der Implementation (z.B., dass der Münztest ausgeweitet wird) wird der Automat modifiziert - aber der Geldeinwurfschlitz und Kaugummi-Ausgabe müssen an der selben Stelle bleiben. Der 'Aufrufer' merkt nix - er kriegt sein Kaugummi wie gewohnt. Dem gegenüber steht aber das Prinzip der Effektivität: Vielleicht sollen ja nicht nur diese Änderungen in der Registry durchgeführt werden, sondern noch viele andere. Dann wäre es Blödsinn, wenn jeder Programmabschnitt immer wieder die CuVer ausliest. Es reicht ja, wenn man die einmal hat für das Gesamtprogramm. Da sind wir wieder beim Thema meines ersten Postings: Für eine globale Variable sehe ich keine Notwendigkeit, aber es wäre legitim. Also: subglobale Variable (aber ordentlich benannt) oder als Argument durchschleifen. Ein Problem sehe ich allerdings in (bw_currentarea) - ineffektiv! Da wird zweimal hintereinander das Selbe aus der Registry ausgelesen. Das ist genau der Fall, wo ein (setq) Sinn macht - ich predige hier ja immer nur gegen die vielen sinnlosen setqs. Und auch bei den String-Literals würde ich ein wenig sparen. Also: CurVer einmal aufgerufen, und dann verwenden. Auch in (bw_setnewstartup) wird doppelt gemoppelt - raus damit! Die doppelten Aufrufe von (bw_currentarea), (bw_getprofil) und (bw_appcounter) bringen nix! Wenn ich's jetzt auf die Schnelle richtig sehe, sollte die Struktur diese sein (ich habe aber nichts getestet oder so):
Code:
(defun bw_setnewstartup (file / curVer curArea profil appCounter) (setq curVer(vl-registry-read...)) (setq curArea(bw_curArea curVer)) (setq profil(vl-registry-read curArea)) (setq appCount(bw_getAppCounter curArea profil)) ... )(defun bw_curArea(curVer / ) ... ) (defun bw_getAppCounter(curArea profil / ) ... )
HTH . Den angehängten Code habe ich übrigens nicht angesehen - mit dem VLX-Compiler usw. habe ich wenig am Hut... Gruss, Axel
------------------ Meine AutoLisp-Seiten Meine private Homepage Mein Angriff auf dein Zwerchfell Mein Lexikon der Fotografie Mein gereimtes Gesülze Meine Überzeugung... Eine Antwort auf diesen Beitrag verfassen (mit Zitat/Zitat des Beitrags) IP |
benwisch Mitglied Bautechniker, CAD-Konstrukteur
Beiträge: 375 Registriert: 01.02.2001
|
erstellt am: 09. Mai. 2003 19:24 <-- editieren / zitieren --> Unities abgeben:
hi axel, ich glaube, ich bin dem prinzip der effektivität ein stückchen näher gekommen... ich habe nun nach deiner erklärung folg. getan : hauptparameter setzen --------------------- file, onlyPath, curArea profil ...im hauptprogramm, da sich die innerhalb eines durchlauf's nicht verändern und ich sie häufiger brauche. weiterhin werden die module bw_curArea und bw_onlyPath nur einmal aufgerufen, es muß also auch nur einmal gearbeitet werden. bei anderen funktionen, die diese werte benötigen, schleife ich die argumente durch. das modul bw_getappcounter habe ich ganz weggelassen, da dieser wert nur in zwei modulen gebraucht wird und pro hauptroutine nur auf jeweils ein modul zugegriffen wird... also dort definieren. eine funktion in eine if schleife einzubauen (für z.b. rückdialoge ö.ä.), ist doch auch effizient ? ich erschlage doch somit gleich 2 fliegen auf einmal, nämlich... a) funktion wird aufgerufen, mit einem "auge" checkt sie, ob die bedingung zum weiterarbeiten erfüllt ist. wenn ja, dann muß ihre siesta enden und den auftrag abarbeiten, womit dann auch die if bedingung erfüllt ist. b) wenn die bedingung zum arbeiten nicht erfüllt ist, dann darf sie weiter "schlafen" und gibt ein gähnen (nil) zurück. bei diesem kleinen code bsp. geht es mir eigentlich weniger um die geschwindigkeit, sondern eher um eine gewisse routine für eine planung nächster codes. ich glaube auch, daß durch die ausführlichkeit deiner antworten mir das alles wieder mal ein bisschen klarer wird und ich nun beim proggen versuche, funktionen in verbindung mit hauptroutinen, weitsichtiger zu planen. naja, ich merke schon.... üben, üben, üben ;-) danke Rolf hauptprogramme
Code:
(defun c:bw_install (/ curArea file i onlyPath profil quest) (setq file (getfiled "..:.. BW_INSTALL ..:.. Bitte eine Lisp, FAS oder VLX Datei auswählen" (getvar "DWGPREFIX") "lsp;fas;vlx" 4 ) ;_ end getfiled onlyPath (bw_onlyPath file) curArea (bw_curArea) profil (vl-registry-read curArea) ) ;_ end setq (if file (progn (if (bw_setSupportPath onlyPath) (princ (strcat "\nPfad: " onlyPath " erfolgreich hinzugefügt")) (princ (strcat "\nPfad: " onlyPath " bereits im Suchpfad vorhanden")) ) ;_ end if (setq i 0) (if (not (while (> (atoi (vl-registry-read (strcat curArea "\\" profil "\\Dialogs\\Appload\\Startup") "NumStartup" ) ;_ end vl-registry-read ) ;_ end atoi (setq i (1+ i)) ) ;_ end >= (= (strcase file) (strcase (bw_?StartupFile curArea profil i))) ) ;_ end while ) ;_ end not (progn (initget "J N") (setq quest (getkword "\nWollen Sie, daß das Programm automatisch mit Autocad geladen wird [J/N] ? <J>: " ) ;_ end getkword ) ;_ end setq (if (or (= quest "J") (not quest)) (progn (bw_SetNewStartup file curArea profil) (vl-load-all file) (princ (strcat "\nProgramm zur Startgruppe (Profil: " Profil ") hinzugefügt !!\nEmpfehlung Neustart !" ) ;_ end strcat ) ;_ end princ ) ;_ end progn ) ;_ end if ) ;_ end progn (princ (strcat "\nProgramm ist bereits in der Startgruppe (Profil: " Profil ")") ) ;_ end princ ) ;_ end if ) ;_ end progn ) ;_ end if (princ) ) ;_ end defun
die module
Code:
;;; ********* ;;; FUNCTIONS ;;; *********;;; f.e. (BW_STRINGREVERSE "Benwisch") = "hcsiwneB" (defun bw_StrRev (string / rev) (setq rev "") (repeat (strlen string) (progn (setq rev (strcat rev (substr string (strlen string) 1))) (setq string (substr string 1 (- (strlen string) 1))) ) ;_ end progn ) ;_ end repeat rev ) ;_ end defun ;;; ------------------------------------------------------------------------------------ (defun bw_setSupportPath (path) (if path (cond ((not (vl-string-search (strcase (strcat path ";")) (strcase (getenv "ACAD")))) (setenv "ACAD" (strcat (strcase (getenv "ACAD")) path ";")) ) ) ;_ end cond ) ;_ end if ) ;_ end defun ;;; ------------------------------------------------------------------------------------ (defun bw_removeSupportPath (path /) (if path (cond ((vl-string-search (strcase (strcat path ";")) (strcase (getenv "ACAD"))) (setenv "ACAD" (vl-string-subst "" (strcase (strcat path ";")) (strcase (getenv "ACAD"))) ) ;_ end setenv ) ) ;_ end cond ) ;_ end if ) ;_ end defun ;;; ------------------------------------------------------------------------------------ (defun bw_SetNewStartup (file curArea profil / appCounter) (setq appCounter (atoi (vl-registry-read (strcat curArea "\\" profil "\\Dialogs\\Appload\\Startup") "NumStartup" ) ;_ end vl-registry-read ) ;_ end atoi ) ;_ end setq (vl-registry-write (strcat curArea "\\" profil "\\Dialogs\\Appload\\Startup") (strcat (itoa (1+ appCounter)) "Startup") file ) ;_ end vl-registry-write (vl-registry-write (strcat curArea "\\" profil "\\Dialogs\\Appload\\Startup") "NumStartup" (itoa (1+ appCounter)) ) ;_ end vl-registry-write ) ;_ end defun ;;; ------------------------------------------------------------------------------------ (defun bw_RemoveFromStartup (file curArea profil / i appCounter) (setq appCounter (atoi (vl-registry-read (strcat curArea "\\" profil "\\Dialogs\\Appload\\Startup") "NumStartup" ) ;_ end vl-registry-read ) ;_ end atoi i 0 ) ;_ end setq (while (>= appCounter (setq i (1+ i))) (cond ((= (strcase file) (strcase (bw_?StartupFile curArea profil i))) (vl-registry-delete (strcat curArea "\\" profil "\\Dialogs\\Appload\\Startup") (strcat (itoa i) "Startup") ) ;_ end vl-registry-delete (vl-registry-write (strcat curArea "\\" profil "\\Dialogs\\Appload\\Startup") "NumStartup" (itoa (1- appCounter)) ) ;_ end vl-registry-write (princ) ) ) ;_ end cond ) ;_ end while ) ;_ end defun ;;; ------------------------------------------------------------------------------------ ;;; f.e (BW_onlyPath "C:\\test.lsp") = "c:" (defun bw_onlyPath (file / rev) (setq rev (bw_StrRev file)) (cond ((vl-string-search "\\" rev) (strcase (bw_StrRev (substr rev (+ 2 (vl-string-search "\\" rev))))) ) ) ;_ end cond ) ;_ end defun ;;; ------------------------------------------------------------------------------------ ;;; Returns the full path and name from a Startupfile (defun bw_?StartupFile (curArea profil num) (vl-registry-read (strcat curArea "\\" profil "\\Dialogs\\Appload\\Startup") (strcat (itoa num) "Startup") ) ;_ end vl-registry-read ) ;_ end defun ;;; ------------------------------------------------------------------------------------ ;;; Returns the current ACAD-Profiles-Path in Registry ;;; f.e. ;;; "HKEY_CURRENT_USER\\Software\\Autodesk\\AutoCAD\\R15.0\\ACAD-1001:407\\Profiles" (defun bw_curArea (/ curVer) (setq curVer (vl-registry-read "HKEY_LOCAL_MACHINE\\SOFTWARE\\Autodesk\\AutoCAD" "CurVer") ) ;_ end setq (strcat "HKEY_CURRENT_USER\\Software\\Autodesk\\AutoCAD\\" curVer "\\" (vl-registry-read (strcat "HKEY_LOCAL_MACHINE\\SOFTWARE\\Autodesk\\AutoCAD\\" curVer) "CurVer" ) ;_ end vl-registry-read "\\Profiles" ) ;_ end strcat ) ;_ end defun
Eine Antwort auf diesen Beitrag verfassen (mit Zitat/Zitat des Beitrags) IP |
| Anzeige.:
Anzeige: (Infos zum Werbeplatz >>)
|