・ ローカル変数とグローバル変数

ローカル変数

ローカル変数は、一つの関数内が有効範囲となり、特定の関数のためだけに用いられます。
そのため、他の関数の中で、同じ変数名を用いても、バッティングしません。
ローカル変数を使用するには
(defun test ( / a b)
のように、関数名のうしろの"( )"の中で、"/"の記号のうしろで、変数名を記述します。

グローバル変数

グローバル変数は、定義関数の内外が有効範囲であるため、関数間でその値を共用する場合に用います。
しかし、AutoLISPでは、原則として変数をグローバルとして扱うようになっているため、ローカル変数として定義し忘れると、意図した結果が、返ってこなかったり、他の関数に予期しない影響を与えることになるので注意が必要です。
グローバル変数を使用するには、特に必要な記述はなく、ローカル変数の定義をしていなければ、グローバル変数として扱われます。

引数

別の関数(プログラム)から、特定の値を持った変数を、関数内に取り込むために設定されています。
引数を定義するには
(defun test ( a b )
のように、関数名のうしろの"( )"の中に、スペースで区切りながら指定します。
この関数を呼び出すには
(test 1 2)
のように、引数に代入する値を指定してやる必要があります。
引数は、その関数の中だけで有効なため、特殊なローカル変数と考えられなくもありません。

さて、ここまでは前段として、実際の例をとって、記述の仕方によって、ローカルとして扱われるのか、グローバルとして扱われるのかを検証してみました。(ただの足し算です)

テスト1 (親ルーチンからローカル関数を引数指定で呼び出す)

(defun test1 ( / int_a int_b sub1)
; サブルーチン 
  (defun sub1 (int_a int_b)
    (setq int_c (+ int_a int_b))
    (princ (strcat "\nsub1 int_c=" (itoa int_c)))
    (setq int_b 0)
  )
; メインルーチン 
  (initget 1)
  (setq int_a(getint "\nint_a="))
  (setq int_b(getint "\nint_b="))
  (sub1 int_a int_b)
  (setq int_a 0)
  (setq int_c (+ int_a int_b))
  (princ (strcat "\ntest1 int_c=" (itoa int_c)))
  (princ)
  )

関数 test1 は int_a int_b をローカル変数、 sub1 をローカル関数として定義しています。
また、関数 sub1 は、関数 test1 のなかで定義され、引数 int_a int_b を必要としています。
そして、関数 test1 はメインルーチンの中で、sub1 を呼び出しています。
変数名と引数名が同じになっていることを除けば、ごく一般的なコーディングといえます。

コマンド:(setq int_a 100)    ; グローバル変数 int_a に 100 をセット
100
コマンド:(setq int_b 200)   
; グローバル変数 int_b に 200 をセット
200
コマンド:(test1)                  
; 関数 test1 の実行
int_a=1                                 
; ローカル変数 int_a に 1 をセット    
int_b=2                           
      ; ローカル変数 int_b に 2 をセット
sub1 int_c=3                         
; 実行結果 (1+2)
test1 int_c=2                        
; 実行結果 (0+2)
コマンド:!int_a                      
; グローバル変数 int_a の確認
100
コマンド:!int_b                      
; グローバル変数 int_b の確認
200

結果は、予想通り、test1 で定義したローカル変数 int_a int_b は、test1 だけが有効範囲です。
また、sub1 で定義された引数、int_a int_b は sub1 だけが有効範囲で、呼び出された後の test1 には影響を与えません。

テスト2 (親ルーチンからローカル関数を呼び出し、その中で同じ変数名を使用した場合)

(defun test2 ( / int_a int_b sub2)
; サブルーチン 
  (defun sub2 ()
    (setq int_c (+ int_a int_b))
    (princ (strcat "\nsub2 int_c=" (itoa int_c)))
    (setq int_b 0)
  )
; メインルーチン 
  (initget 1)
  (setq int_a(getint "\nint_a="))
  (setq int_b(getint "\nint_b="))
  (sub2)
  (setq int_a 0)
  (setq int_c (+ int_a int_b))
  (princ (strcat "\ntest2 int_c=" (itoa int_c)))
  (princ)
  )

関数 test2 も int_a int_b をローカル変数、sub2 をローカル関数として定義しています。
しかし、関数 sub2 は引数を必要としていないのが test1 と違います。
しかも、関数 sub2 は変数 int_a int_b をローカル変数としては定義していません。

コマンド:(setq int_a 100)    ; グローバル変数 int_a に 100 をセット
100
コマンド:(setq int_b 200)   
; グローバル変数 int_b に 200 をセット
200
コマンド:(test2)                
  ; 関数 test2 の実行
int_a=1                            
    ; ローカル変数 int_a に 1 をセット    
int_b=2                           
      ; ローカル変数 int_b に 2 をセット
sub2 int_c=3                       
; 実行結果 (1+2)
test2 int_c=0                    
   ; 実行結果 (0+0)
コマンド:!int_a                    
; グローバル変数 int_a の確認
100
コマンド:!int_b                    
; グローバル変数 int_b の確認
200

意外にも、関数 sub2 のなかでは、int_a と int_b はローカル変数として扱われています。
まあ、sub2 は test2 のローカル関数だから・・・・?
当然、sub2 のなかで変数の値をセットし直すと、親ルーチンにも影響を与えてしまいます。
特殊な条件では、使えるかもしれませんが、どこで変数の値が書き換わっているのかわかりにくくなるので、あまりよろしくないですね。

テスト3 (親ルーチンからグローバルな関数を呼び出し、その中で同じ変数名を使用した場合)

; サブルーチン
(defun sub3 ()
  (setq int_c (+ int_a int_b))
  (princ (strcat "\nsub3 int_c=" (itoa int_c)))
  (setq int_b 0)
  )
; メインルーチン
(defun test3 ( / int_a int_b ) 
  (initget 1)
  (setq int_a(getint "\nint_a="))
  (setq int_b(getint "\nint_b="))
  (sub3)
  (setq int_a 0)
  (setq int_c (+ int_a int_b))
  (princ (strcat "\ntest3 int_c=" (itoa int_c)))
  (princ)
  )

関数 test3 は、int_a int_b をローカル変数として定義していますが、sub3 はグローバルな関数(他の関数からも呼び出せる)です。
また、test2 同様 sub3 では、int_a int_b をローカル関数としては定義していません。

コマンド:(setq int_a 100)    ; グローバル変数 int_a に 100 をセット
100
コマンド:(setq int_b 200)   
; グローバル変数 int_b に 200 をセット
200
コマンド:(test3)                
  ; 関数 test3 の実行
int_a=1                            
    ; ローカル変数 int_a に 1 をセット    
int_b=2                           
      ; ローカル変数 int_b に 2 をセット
sub3 int_c=3                       
; 実行結果 (1+2)
test3 int_c=0                    
   ; 実行結果 (0+0)
コマンド:!int_a                    
; グローバル変数 int_a の確認
100
コマンド:!int_b                    
; グローバル変数 int_b の確認
200

結果は test2 と同じになりました。
ローカル関数でなくても、親ルーチンで、ローカル変数として定義していれば、同じ変数名を使った場合、ローカルとして扱われます。

私的考察
私の場合、BS2-CAD という特殊な環境で作業をしていますので、グローバルな変数や、関数は使えません。(BS2-CADの仕様を、すべて把握するのは、1ユーザーとしては不可能なため)
一般的には、システム変数や、エラー処理など、既定の環境をグローバル変数に格納して、関数実行終了時や、エラー終了時に、書きもどすのが、慣わしとなっています。
そのため、私のツールではエラー処理は行っていませんでしたが、ローカル変数に格納しておいても、記述の仕方によっては、書きもどすことは可能なようなので、今後対応を進めようと思います。