4 ========================
6 ========================
8 Ring のスコープ規則と変数の検出方法、および名前衝突の解説、さらに解決方法と回避方法を学びます。
10 この情報は Ring で大規模アプリケーションの開発をはじめるときに重要です。
12 アプリケーションには下記が使用されています。
14 * グローバル変数 (使用を避けることに努めてください)
18 * 括弧 { } によるオブジェクトのアクセス
25 pair: スコープ規則; 三種類のスコープ
32 (1) パブリックスコープとグローバルスコープ - 各変数はステートメント部で定義されます (関数とクラスの前)
34 (2) オブジェクトスコープ - オブジェクトの内側にあるとき (クラスのメソッドの内側または { } の使用によるオブジェクトへのアクセス)
36 (3) ローカルスコープ - 関数とメソッドに関連付けられています
39 pair: スコープ規則; 変数の定義と変数へのアクセス
42 ============================
44 (1) Ring はレキシカルスコープを使用しています。つまり、変数のスコープは変数を定義した場所により決定します。
46 (2) { } 括弧内でのオブジェクトのアクセス時、現在有効なオブジェクトのスコープを対象となるオブジェクトのスコープへ変更します。今まで通り、グローバルスコープとローカルスコープへアクセスできます。
48 (3) 'Class' キーワードとクラス名末尾に、変数名の定義を記述したとしても、今まで通りグローバルスコープへアクセスできます。
50 この範囲 (クラスの範囲 - クラス名の後、およびメソッドの前) にあるものは、
52 * グローバルスコープ ----> グローバルスコープ
53 * オブジェクトスコープ ----> オブジェクトスコープ
54 * ローカルスコープ ----> オブジェクトスコープ
56 .. note:: クラスの範囲にあるローカルスコープからでも、この範囲のオブジェクトスコープを指すため、入れ子の括弧は使用可能でありローカルスコープからクラスのオブジェクトスコープへのアクセスができます。
58 .. tip :: この範囲での定義によりウィンドウとコントロールの属性を作成できます。
60 .. tip :: クラスの範囲にてオブジェクトの作成、および括弧 { } でオブジェクトへアクセスした後に、括弧の内側で Self.属性 を使うとクラスを使えます (アクセスしようとしているオブジェクトではありません)。この理由はローカルスコープからクラスを呼び出すことができるからです。
62 (4) 関数の仮引数は、自動的にローカルスコープへ定義されます。
65 pair: スコープ規則; Ring による変数の検出方法
68 =========================
80 * 見つからない ----> ランタイムエラー
82 * 見つかった ----> 次回の検索を回避するために、最適化できるかどうか確認します (性能改善のためのポインタとキャッシュ)。
85 pair: スコープ規則; オブジェクト.属性の用法
88 ===========================
90 オブジェクト.属性 を使うときはオブジェクト属性に限り検索を行います。
92 つまり、ローカルスコープまたはグローバルスコープのオブジェクト属性では検索は行いません。
94 .. note:: Self.属性を使うときは属性を検索する前に Self の検索を最初に行います。
98 pair: スコープ規則; Self オブジェクト
103 Selft オブジェクトは現在のオブジェクトを参照するためにクラスのメソッドから使えます!
105 クラスのメソッドの内側であり Self を使うときは、このクラスから作成されるオブジェクトを意味します。
107 クラスのメソッドの内側で括弧 { } を使うと、現在のオブジェクトのスコープを変更します。
108 また、 Self でオブジェクトへアクセスするときに、括弧の内側にある参照を変更します。
110 クラスの範囲 (クラス名の後、およびメソッドの前) の内側はオブジェクトスコープ、
111 およびローカルスコープからオブジェクトへのアクセスすることもできます。
112 この範囲で Self を使うと、常にクラスのオブジェクトへ参照されます。
113 オブジェクトのスコープの変更で括弧を使用後に、括弧の内側で Self を使用した場合でも、
114 クラスの範囲内にあるため、 Self はクラスオブジェクトへの参照になります (既に括弧でアクセスしているオブジェクトではありません)。
116 * グローバルスコープ ---> グローバルスコープ
117 * オブジェクトスコープ ---> オブジェクトスコープ
118 * ローカルスコープ ---> オブジェクトスコープ
120 括弧を使うとオブジェクトのスコープのみ変更されます。
121 Ring は変数を検索するときに、ローカルスコープを最初に検索するためクラスの内側にある Self を検出します。
124 pair: スコープ規則; Ring における変数と属性の定義方法
128 =================================
130 Ring は代入操作で変数名を使用します。
134 2 - 見つからない ---> ランタイムエラーの回避と現在のスコープへ変数を定義します。
136 3 - 見つかった ---> 変数を使用しますが現在のスコープへ変数を一切定義しません。
138 * グローバルの範囲 (関数またはクラスの前) において現在のスコープはグローバルスコープになります。
140 * クラスの範囲 (クラス名の後、およびメソッドの前) において現在のスコープはオブジェクトの属性になります。
142 * 関数とメソッドにおける現在のスコープはローカルスコープになります。
145 pair: スコープ規則; クラス属性とグローバル変数の間での名前衝突
148 クラス属性とグローバル変数の間での名前衝突
149 ==========================================
164 前述の用例ではグローバル変数 ‘name’ が
167 Ring は‘name’ 変数を使用するにとき検索処理の開始、
170 見つかった ---> 見つかったものを使用
174 しかし、変数名はグローバル変数であるため、検出後に使用されます!
176 属性名が存在しない! オブジェクトへ追加します。
192 解決方法② - $ などのグローバル変数名に特殊記号を使用
205 解決方法③ - AddAttribute() メソッドの使用
214 AddAttribute(self,"name")
218 解決方法④ - 属性名の先頭に Self を使用
236 2 - オプション扱い : グローバル変数の回避する、または Main 関数の使用
242 * 属性名の先頭に Self を使います。または AddAttribute() を使用します。
245 pair: スコープ規則; クラス属性とローカル変数の間での名前衝突
248 ========================================
250 この名前衝突は、括弧でオブジェクトにアクセスするときに発生します。
258 o1 = new person {name="mahmoud" address="Egypt" phone = 000 }
268 この変数の値は、オブジェクトの属性ではなく“mahmoud”が設定されます。
276 o1 = new person {self.name="mahmoud" address="Egypt" phone = 000 }
290 o1 = new person {name="mahmoud" address="Egypt" phone = 000 }
298 解決方法③ : 括弧の変更およびドット演算子を使用
317 pair: スコープ規則; 括弧によるクラスのメソッドの内側にあるオブジェクトへのアクセス方法
319 括弧によるクラスのメソッドの内側にあるオブジェクトへのアクセス方法
320 ======================================================================
322 クラスのメソッドの内側では三種類のスコープ (ローカルスコープ、オブジェクトスコープとグローバルスコープ) があることを思い出してください。
323 これはオブジェクト属性とメソッドへアクセス可能であると期待しており、
324 括弧でオブジェクトの属性とメソッドへアクセスするまでは本当です。
325 この場合は、オブジェクトのスコープが別のオブジェクトへ切り替えられるためです。
334 see x + nl + y + nl # 正常に動作します。
335 myobj = new otherclass {
337 see x + nl + y + nl # エラー!
351 Line 8 Error (R24) : Using uninitialized variable : x
352 In method test() in file methodbraceerror.ring
353 called from line 5 in file methodbraceerror.ring
357 解決方法 (1) : 括弧の外側にあるクラスの属性へアクセスするためのコードを記述する。
366 see x + nl + y + nl # 正常に動作します。
367 myobj = new otherclass {
370 see x + nl + y + nl # 括弧の外側 - 正常に動作します。
398 myobj = new otherclass
406 解決方法③ : Self オブジェクトのコピー
408 この解決方法は、括弧でクラス属性へアクセスしたいときに使用します (読み取り)。
419 myobj = new otherclass {
421 see oself.x + nl + oself.y + nl
443 前行の問題はオブジェクトの新しいコピーあることです。
444 この理由は Ring の代入演算子はリストと値によるオブジェクト (参照ではない) はコピーしないからです。
446 新しいオブジェクトへアクセスする場合は問題はありません (読み取り)。
448 オブジェクトの属性を変更した場合は問題があります (そのためコピーを変更します!)
450 .. note:: 括弧はコピーを行うときに再度使えます。
461 myobj = new otherclass {
471 GUI アプリケーションでは、様々なメソッドからコントロールへアクセス可能な属性として、
472 Window オブジェクトがあるクラスを作成します。
473 括弧でメソッドの内側にあるオブジェクトへアクセスするときは、前述の情報を思い出してください。
474 この場合は、オブジェクトとの属性へアクセスできないため Self オブジェクトをコピーした場合は、コピー先での作業となります。
475 新しいコントロールの作成は、コピーと関連付けられていますがアクセスはできません。
479 pair: スコープ規則; クラスのメソッド内にある括弧からクラスの属性にアクセスするには
482 クラスのメソッド内にある括弧からクラスの属性にアクセスするには
483 ==============================================================
485 クラスのメソッドからクラスの属性へ直接アクセスするために、属性やメソッド名の先頭に Self による参照を選択する方法があります。
486 クラスのメソッド内で括弧 { } を使うと、有効なオブジェクトのスコープを変更することになり、
487 クラスの属性へ直接アクセスするのを防ぎます。また Self の使用は、括弧でアクセスするオブジェクトを変更できるため Self による参照は効果がありません。
489 この場合、属性を読み取りたい場合は、括弧の使用前に
490 Self オブジェクトを必ずコピーしてください。
491 また、括弧の後に、オブジェクトの属性からローカル変数へコピーしたい場合は、必ず属性を修正してください。
493 この場合は、括弧の内側にある属性の読み取り、または変更をしたいときに発生します。
506 oCon = addDatabase("QSQLITE") {
507 setDatabaseName("weighthistory.db")
514 前述の用例で connection オブジェクトを作成して oCon 属性の内側で保存したいとします。
516 このオブジェクトは QSQLDatabase() オブジェクトのアクセス後に使用する addDatabase() メソッドからの出力です。
518 括弧の内側では MyApp クラスから作成したオブジェクトによる Self 参照の使用はできません。
519 ここで Self 参照を使用することは、括弧でオブジェクトをアクセスすることになるからです。
521 ローカル変数 oCon を作成してから、括弧の後に oCon 属性を変数へコピーすることにより、
536 oCon = new QSqlDatabase()
537 oCon = oCon.addDatabase("QSQLITE") {
538 setDatabaseName("weighthistory.db")
557 this.oCon = addDatabase("QSQLITE") {
558 setDatabaseName("weighthistory.db")
564 .. note:: 括弧の内側でクラスの属性 (oCon) へアクセスするために This.属性 を使用しています。
568 pair: スコープ規則; GUI アプリケーションのウィンドウごとにクラスを作成するには
570 GUI アプリケーションのウィンドウごとにクラスを作成するには
571 ==========================================================
573 ウィンドウ用のクラスを作成するための優れた方法は、クラス名の直後にウィンドウを定義することです。
575 ウィンドウをコントロールの定義に関する問題は発生せずに、入れ子の括弧を使えます。
576 そして、属性はメソッドからアクセスできます。
586 $ObjectName = "oFirstWindow"
587 oFirstWindow = new FirstWindow
589 $ObjectName = "oSecondWindow"
590 oSecondWindow = new SecondWindow
597 win = new qWidget() {
598 setgeometry(0,50,300,200)
599 setWindowTitle("First Window")
600 label1 = new qLabel(win)
602 setgeometry(10,10,300,30)
605 btn1 = new qPushButton(win)
609 setClickEvent($ObjectName+".increment()")
616 setText( "" + ( 0 + text() + 1 ) )
622 win = new qWidget() {
623 setgeometry(400,50,300,200)
624 setWindowTitle("Second Window")
625 label1 = new qLabel(win)
627 setgeometry(10,10,300,30)
630 btn1 = new qPushButton(win)
634 setClickEvent($ObjectName+".decrement()")
641 setText( "" + ( 0 + text() - 1 ) )
645 pair: スコープ規則; クラス範囲にある括弧内の Self と Self 間との名前衝突
647 クラス範囲にある括弧内の Self と Self 間との名前衝突
648 ====================================================
650 クラスの領域 (クラス名の後、そしてメソッドの前) で属性を定義します。
652 この領域では、グローバルスコープへアクセスできます。そして、ローカルスコープはオブジェクトスコープを指します。
656 * グローバルスコープ ---> グローバルスコープ
657 * オブジェクトスコープ ---> オブジェクトスコープ
658 * ローカルスコープ ---> オブジェクトスコープ
671 aFriends + new Friend {
674 aFriends + new Friend {
688 前述の用例における問題は account クラスには “name” 属性があり、
689 Friend クラスにも同名属性 “name” があります。
691 括弧内で self.name を使うと前述の用例と同じ結果になります!
702 aFriends + new Friend {
705 aFriends + new Friend {
712 この名前衝突に関して、括弧の内側で self.name でも解決しない理由は?
716 * グローバルスコープ ---> グローバルスコープ
717 * オブジェクトスコープ ---> オブジェクトスコープ (Account クラス)
718 * ローカルスコープ ---> ローカルスコープ (Account クラス)
720 括弧を使うとき、オブジェクトのスコープは変更されるため、このようになります。
722 * グローバルスコープ ---> グローバルスコープ
723 * オブジェクトスコープ ---> オブジェクトスコープ (Friend クラス)
724 * ローカルスコープ ---> ローカルスコープ (Account クラス)
726 Ring では、ローカルスコープを最初に検索するため、 self.name を使うときは Account クラスを使用します。
730 解決方法① : リストによるオブジェクトへのアクセス
741 aFriends + new Friend
742 aFriends[len(aFriends)] {
743 aFriends[len(aFriends)].name = "Gal"
745 aFriends + new Friend
746 aFriends[len(aFriends)] {
747 aFriends[len(aFriends)].name = "Bert"
753 解決方法② : name 属性を設定するために friend クラスでメソッドを作成
764 aFriends + new Friend {
767 aFriends + new Friend {
776 解決方法③ : 属性を設定するために account クラスでメソッドを作成
791 aFriends + new Friend {
818 aFriends + new Friend
819 return aFriends[len(aFriends)]
832 pair: スコープ規則; 括弧による現在のオブジェクトスコープの除外方法
835 括弧による現在のオブジェクトスコープの除外方法
836 ==================================================
838 括弧から現在のオブジェクトスコープを別のオブジェクトスコープへ変更します。
839 クラス属性の変更、および同名変数の使わずに処理できます。
843 new point {x=10 y=20 z=30 start() }
846 see self # x y z の値を表示 (10,20,30)
852 see self # x y z の値を表示 (10,20,30)
856 Self { # 利点なし - ローカルスコープの最初の検索で完了します。
861 see self.x + nl # 10 の表示
862 see self.y + nl # 20 の表示
863 see self.z + nl # 30 の表示
890 pair: スコープ規則; For ループでローカルスコープを使用
893 ==================================
895 Ring 1.8 より、 For ループでの新規識別子 (変数) を定義するときは、ローカルスコープで定義します。
917 ? x # NULL の表示 ("For In" ループでは、ループ完了後に参照を破棄します)
932 pair: スコープ規則; スコープ規則のまとめ
936 ======================
940 1 - 各種プログラミング言語には言語の目的に基づいたスコープ規則があります。
942 2 - 小規模プログラミングと大規模プログラミングは異なります。
944 3 - あるプログラミング言語は小規模プログラミング用に設計されていますが、それ以外は大規模プログラミング用に設計されています。
946 4 - プログラミングで、スコープへのアクセスが複数ある場合があります。 - 正確に管理されていない場合は問題になります。
948 5 - 可視スコープの個数を削減することで、さらなる安全性を保ち続けることができます。
950 6 - あるプログラミング言語では、ある方法でスコープの管理を強制しており、それ以外の方法はありません!
954 1 - まず柔軟性、そして次に安全性を求めて設計された特別かつ「非常に単純明快」なスコープ規則があります
956 2 - Ring は小規模プログラミングと大規模プログラミングに対応するように設計されています。
958 3 - Ring にはプロジェクトの規模に基づいて選択可能な各種プログラミング・パラダイムがあります。ターゲットとなるプロジェクトで悪いパラダイムを選択した、または間違っているか一般的ではない方法によりパラダイムを使用するとエラーになります。
960 4 - Ring には選択肢があります。グローバル変数の使用、または使用を避けることができます。特殊記号 $ の指定、または除去できます。オブジェクト指向を使用する、または手続き型の使用を継続できます。クラス範囲 (クラスの範囲 - クラス名の後、およびメソッドの前) で属性の使用、またはコードで属性を使えます。
962 5 - このスコープ規則をご確認になり、記載されていることについて考えた後に好きな方法を使用してください。
966 1 - プログラムのコードのすべての場所において最大三種類のスコープだけあります (ローカルスコープ、オブジェクトのスコープとグローバルスコープ)。
968 2 - Ring が変数を検索するとき、最初にローカルスコープを、次にオブジェクトのスコープを、そしてグローバルスコープを検索します。
970 3 - 手続き、またはメソッドは、括弧 { } でオブジェクトへのアクセス、および現在のオブジェクトのスコープをいつでも変更できます。
972 4 - クラスの範囲 (クラス名の後、およびメソッドの前) において、これはオブジェクトスコープでオブジェクトのスコープとローカルスコープの両方を指す特別な範囲です。すなわち、この範囲で定義する各変数は属性になるため、ローカル変数ではありません。
974 5 - 変数 (スコープとクラスの範囲) の定義前に検索処理で変数が検出された場合は検出された変数を使用します。
976 6 - 関数とメソッドの仮引数は関数、またはメソッドへのローカル変数として自動的に定義されます。
978 7 - オブジェクト.属性 を使うとオブジェクト属性のみ検索します。
980 8 - Self.属性 を使うと先頭にある Self を最初に検索してから Self の属性を検索します。
982 9 - クラスの範囲 (クラス名の後、そしてメソッドの前) の内側にある Self 参照はクラスから作成されたオブジェクトのスコープを指します。
984 10 -メソッドの内側にある Self 参照は括弧による参照でオブジェクトへアクセスするときに変更されます。
986 11 - クラスの範囲 (クラス名の後、そしてメソッドの前) に変数名を直接記述すると、それらの使用または定義の意味となります。
988 12 - クラスの範囲で Self.属性 を使うと検索対象となるオブジェクトのスコープの個数を削減します (グローバルスコープとの名前衝突を回避します)。
990 これらの規則から名前衝突が何故発生するのか、そして回避するにはどうしたら良いかを理解できます。
992 名前衝突を避けるための簡単な助言として、スコープ規則を使用することが最良の方法です。
994 1 - グローバル変数の回避をしてください。
996 2 - Main 関数の使用 - これはグローバル変数の回避に有効です。
998 3 - グローバル変数を多用する場合は変数名の先頭に $ を使用してください。
1000 4 - クラスの範囲で三番目 ($ の使用) の助言を考慮しない場合は、属性を定義するときに self.属性 を使用します。
1002 5 - オブジェクトのスコープを変更したくない場合は、オブジェクト { 属性 } と オブジェクト { メソッド() } の代わりに オブジェクト.属性 と オブジェクト.メソッド() を使用します。
1004 6 - 入れ子の括弧をクラスで使用する場合 - この範囲で { } + によりクラス属性へアクセスするために、オブジェクトのアクセス権がある場合はクラス範囲の使用に関して可能ならば考慮してください。
1006 7 - クラスのメソッドの内側、および入れ子の括弧が使用されている場合は、括弧ごとにオブジェクトのスコープは変更されるため、直ちにクラスの属性へのアクセス権を失いますが、括弧 { } でローカルスコープの前後へアクセスできます。括弧からクラスの属性を読み取り、 または修正する場合は This.属性 を使用してください。この理由として ‘This’ を使用することは「このクラスからオブジェクトが作成される」ことを意味するのとは違い ‘Self’ (現在のスコープにあるオブジェクト) を意味します。
1008 前述の要点を全て理解したならば、本章を会得したことになります。