2019年10月20日日曜日

19: これまで語られなかったまたは不正確に語られたLibreOffice Basicの基本

<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>

LibreOffice/OpenOffice Basicについて私が抱く基本的な関心事のいくつかを満足いくように取り扱っているドキュメントが私には見つけられません。ここでは、そうした関心事に取り組みます。

話題


About: UNO (Universal Network Objects)
About: LibreOffice
About: Apache OpenOffice
About: LibreOffice Basic
About: Apache OpenOffice Basic

この記事の目次


開始コンテキスト



ターゲットコンテキスト



  • 読者は、これまで語られなかったまたは不正確に語られたLibreOfficeまたはApache OpenOffice Basicの基本のいくつかを知る。
ト書き
Hypothesizer 7、Objector 19A、Objector 19Bがコンピューターの前にいる。


オリエンテーション


Hypothesizer 7
本記事では、これまで語られなかったまたは不正確に語られたLibreOfficeまたはApache OpenOffice Basicの基本のいくつかを明確にするよう私が試みます。

Objector 19B
LibreOffice Basicについてのドキュメントってなんで、みんな、あんなに不満足なのかしらねえ...

Hypothesizer 7
マダム、私は全てのドキュメントを読んだわけではないので全てのドキュメントについて語ることはできませんが、少なくとも、LibreOfficeまたはApache OpenOffice Basicについて私が読んだ全てのドキュメントは、本質的であると私が考える事項のいくつかを取り扱っていません。

Objector 19B
だから、なんでだろうって思うわけ。

Hypothesizer 7
Basicはそんなに真剣に受け取られるように想定されていないからだと考えざるをえません。多分、真剣なドキュメントに対する需要があまりないのでしょう。

Objector 19B
それじゃ、Basicはジョークなのね...

Hypothesizer 7
...ジョークであろうがなかろうが、それでいくつかのプログラムを作る以上、正確な知識を、少なくとも、その本質的側面について、私は求めます。私のプログラムがいかに小さいといえども、必要な知識の欠如は、私を不可解につまずかせることになりますので。...まあ、理解していないことを盲目的に行なうということに我慢がならない性分だということもありますが。

Objector 19B
私の関心事はあなたのと一致するのかしら。

Hypothesizer 7
私がここで取り扱うのは、極めて基本的なものです。

例えば、各タイプの変数は何を格納できるのか?

Objector 19B
...私が読んだドキュメントはみんな、少なくとも、例えば、'Integer'変数は整数値を格納できると言ってると思うけど。

Hypothesizer 7
変数データのアドレスを格納できるのか、データ自体を格納できるのかを言っていないでしょう?

Objector 19B
あるドキュメントは、例えば、'Integer'タイプは値コピーされると言ってるけど。

Hypothesizer 7
それは、そうしたドキュメントが受け入れ可能レベルからいかに遠いかの例なんですよ。...そのような叙述は本質的にナンセンスです。'Integer'であろうが、他のものであろうが、ファンクション引数はデフォルトでリファレンスです(したがって、そうしたファンクション引数に'Integer'データを渡しても「値コピー」されません)。したがって、「'Integer'タイプは値コピーされる」とは言えないのですよ、「'Integer'変数に作用させる代入オペレーターはをコピーする」とは言えるかもしれませんが。...実のところ、「値コピー」であるというのは、変数タイプの属性ではなくて、例えば、代入オペレーターの属性なのです(ファンクションリターンの属性であるかもしれません)。

Objector 19B
ああ、それはそのとおりね。

Hypothesizer 7
それに、ポインターのはアドレスであって、それに作用させた代入オペレーターは(アドレス)をコピーします。したがって、「値コピー」であると言うのは、変数がポインターであるのか、通常変数であるのか、他のものであるのかについて、何も語っていないのです。

Objector 19B
それもそのとおりね。...でも、そういうドキュメントのほとんどの読者は、ポインターやアドレスが何であるか、知識も関心もないか、あると想定されていないかなのよ。

Hypothesizer 7
そうだと推測しますが、真剣なプログラマーにとっては、それは、ごく明確になっていなければならないポイントです。

Objector 19B
うーん...

Hypothesizer 7
例えば、ユーザー定義データタイプ変数がポインターであることをご存知でしょうか?

Objector 19B
はあ?値コピーされると、あるドキュメントは言ってるけど...

Hypothesizer 7
そのドキュメントは、アドレス(それがです)が代入オペレーターによってコピーされるという点において正確ですが、もしも、ユーザー定義データタイプ変数が通常の非ポインター変数であるとあなたが理解したとしたら、それは誤解です。

Objector 19B
誰だって値タイプだと理解するでしょう...

Hypothesizer 7
しかし、それはポインターです、以下のコードで容易に確認できるとおり(実のところ、「値タイプ」、「リファレンスタイプ」という実体とかけ離れた名称は捨て去られるべきです、私の意見では)。

@LibreOffice Basic ソースコード
Type UserDefinedTypeA
	i_stringA As String
End Type

Sub testUserDefinedDatumTypeVariable ()
	Dim l_userDefinedTypeAInstanceInUserDefinedTypeA0 As UserDefinedTypeA
	Dim l_userDefinedTypeAInstanceInUserDefinedTypeA1 As UserDefinedTypeA
	l_userDefinedTypeAInstanceInUserDefinedTypeA1 = l_userDefinedTypeAInstanceInUserDefinedTypeA0
	l_userDefinedTypeAInstanceInUserDefinedTypeA0.i_stringA = "AAA"
	MsgBox (l_userDefinedTypeAInstanceInUserDefinedTypeA1.i_stringA)
End Sub

ト書き
Hypothesizer 7は、'testUserDefinedDatumTypeVariable'サブルーチンを実行し、'AAA'のメッセージボックスが表示される。

Objector 19B
はあ?なんで、'AAA'が'l_userDefinedTypeAInstanceInUserDefinedTypeA1.i_stringA'に入ってるわけ?

Hypothesizer 7
なぜなら、'l_userDefinedTypeAInstanceInUserDefinedTypeA1'はポインターであり、'l_userDefinedTypeAInstanceInUserDefinedTypeA0'の'l_userDefinedTypeAInstanceInUserDefinedTypeA1'への代入は、'l_userDefinedTypeAInstanceInUserDefinedTypeA0'データのアドレスを'l_userDefinedTypeAInstanceInUserDefinedTypeA1'変数にコピーしたからです。...ユーザー定義タイプ変数を定義すると、対応するユーザー定義データタイプのインスタンスが自動的に作成されてそのアドレスがその変数にセットされるという事実によって騙されないでください。

Objector 19B
はあ?何が起きてるの?

Hypothesizer 7
'Dim l_userDefinedTypeAInstanceInUserDefinedTypeA0 As UserDefinedTypeA'というのは、Javaにおける'UserDefinedTypeA l_userDefinedTypeAInstanceInUserDefinedTypeA0 = new UserDefinedTypeA ()'に相当するのであって、だからといって、'UserDefinedTypeA'が、「値タイプ」と呼ばれている(実体とかけ離れて)ものになるわけはありません。

Objector 19B
...

Hypothesizer 7
実際、ユーザー定義データタイプ変数がポインターであることおよび'ポインター'とは何であるかを理解することなく、その変数の振る舞いは正確に理解できません、'ポインター'とは何であるかに読者が関心を持っていようがいまいが。...'ポインター'と'リファレンス'は別物であり、違った振る舞いをするのであって、ポインターはポインターとして理解されなければなりません、リファレンスとしてではなく。

もっと一般的に言うと、私が読んだLibreOfficeまたはApache OpenOfficeについてのドキュメントのすべては'データ'、'変数'、'表現'、'値'を適切に区別しておらず、それらの概念をごちゃ混ぜにすることが、それらのドキュメントを損なっています(実際には、その問題は、私がドキュメントを読んだことのある全てのプログラミング言語でとても広く見られます)。

Objector 19B
...

Objector 19A
あんたの'開始コンテキスト'は、 俺は「LibreOfficeまたはApache OpenOffice Basicの基本についての、大体において包括的であると想定されているドキュメントを読んだ」と想定されていると言ってるが...

Hypothesizer 7
サー、それが意味するのは、本記事はLibreOfficeまたはApache OpenOffice Basicの基本を包括的にカバーはしないということです。本記事は、無体にも語られなかったか不正確に語られた緖側面にのみ取り組みます、LibreOfficeまたはApache OpenOffice Basicの基本をかなり包括的に説明したつもりであると思われる平均的なドキュメントを読者は読んだという仮定に基づいて。

Objector 19A
...


本体


1: 変数タイプ群の各々は本当は何であるか


Hypothesizer 7
第1に、私たちは'データ'、'変数'、'値'を明確に区別しなければならいと、断らせていただきます。実際、私が'変数タイプ'について語るときは、私は断然、'変数タイプ'のことを意味しており、'データタイプ'のことをではありません。

第2に、'非引数変数'と'引数変数'を区別しましょう。任意の引数変数リファレンス変数(C++における意味において(Javaで「リファレンスタイプ変数」と呼ばれているものは実際にはポインターです))にも非リファレンス変数にもなれます。その一方、リファレンス非引数変数などというものはありません。

非リファレンス引数変数に何かを渡すのは、それを非引数変数に代入するのと同じ振る舞いをします。

そこで、これ以後は、'非引数変数'を論じましょう。

変数タイプ群は以下のカテゴリーに分けられます: Basicにビルトインされたタイプ群('String'のように)、UNO struct群、'Object'、'Variant'、ユーザー定義タイプ群。

Basicにビルトインされたタイプ変数は、通常変数(ポインターでもリファレンスでもないもの)です。

UNO struct変数は、通常変数です。

ユーザー定義タイプ変数は、ポインターです。

'Object'および'Variant'は、実のところ、そうしたカテゴリー分類を拒みます。

Objector 19A
「拒みます」ってどういうことだ?

Hypothesizer 7
'Object'変数は、UNO struct データ(そのアドレスではなく)、UNOオブジェクトのアドレス、ユーザー定義タイプデータのアドレスのいずれかを格納できます。

Objector 19A
それじゃあ、'Object'変数は、ある時は、データを格納し、ある時は、アドレスを格納するという...

Hypothesizer 7
ご注意いただきたいのですが、オフィシャルドキュメントは、'Object'変数をユーザー定義タイプデータを格納するためにのみ使用するように推奨していますが、そのオフィシャルドキュメント自身が'Object'変数を他のデータを格納するためにふんだんに使っております。...正直な所、そんな無責任な推奨は私は受け入れませんので、したがって、'Object'変数を他のデータを格納するために使用しても構わないというスタンスを私は取ります(実際、それが問題を起こす例を私は1つも見つけることができません)。

Objector 19B
...それが言ってることをおとなしく受け入れるべきです、代わりに'Variant'を使っても何の害もないんだから。

Hypothesizer 7
実のところ、不必要に包括的な変数を使うことに私は一定の害を見ます。

Objector 19B
あなた、厄介な人ね...

Hypothesizer 7
'Variant'変数は、Basicにビルトインされたタイプデータ(そのアドレスではなく)、UNO struct データ(そのアドレスではなく)、UNOオブジェクトのアドレス、ユーザー定義タイプデータのアドレスのいずれかを格納できます。

Objector 19A
ふーん...


2: ファンクションリターンはどれもリファレンスではない


Hypothesizer 7
私がこれまでに読んだドキュメントはどれも触れてないことなので、明確にしておきましょう: ファンクションリターンはどれもリファレンスではありません。

...当然のことですか?...えーと、引数変数はどれもデフォルトでリファレンスなので、少なくとも私は疑問を持ちます、「それでは、リターンはどうなのですか?」。

実際、ファンクションリターンはどれも、Basicにビルトインされたタイプデータ(そのアドレスではなく)、UNO struct データ(そのアドレスではなく)、UNOオブジェクトのアドレス、ユーザー定義タイプデータのアドレスのいずれかを返します。


3: 変数は、定義時に恣意的な値で初期化できないらしい


Hypothesizer 7
変数は、定義時に恣意的なで初期化できないようです。

Objector 19A
はあ?

Hypothesizer 7
つまり、'Global s_integerA As Integer = 2'や'Global s_integerA = 2'のようなことはできません。

確かに、ローカル変数については、'Dim l_integerA As Integer : l_integerA = 2'のようなことはできます。しかし、それは、グローバル変数についてはできません、':'はただ2つの行を接続しているだけなので(ファンクションやサブルーチンの外では2行目は無効)。

Objector 19A
...変数を恣意的な値で初期化することが本当にできないのか?そんなの、あらゆるプログラミング言語に普遍の機能だと思ったが。

Hypothesizer 7
確実には知りません。ただ、私が読んだドキュメントはどれもその機能に触れてもいません。

問題は、グローバル変数を望みの値で初期化する適切なタイミングがないことです: グローバル変数を望みの値で初期化するためには、それをファンクション(初期化実行ファンクション)内で行わなければならないことになりますが、複数のファンクションがあり、その内のどれもが最初に呼ばれる可能性がある場合、どのファンクションで初期化実行ファンクションを呼べばよいのでしょう?私が推測する限りでは、初期化実行ファンクションを他の全てのファンクション内で呼ぶ(初期化実行ファンクションは、その変数を1度のみ初期化するように制御をしなければならないでしょう)ようにするしかありませんが、それは、煩わしい解決策です。


4: ファンクションやサブルーチンのスコープ


Hypothesizer 7
私がこれまで読んだドキュメントはどれも触れていませんが、ファンクションやサブルーチンは、自動的かつ不可避にグローバルであるようです。

Objector 19A
「不可避に」?

Hypothesizer 7
ファンクションやサブルーチンはどれも、'Private'指定していても、何の指定もしていなくても、別のモジュールから呼ぶことができます。

Objector 19A
...Basicは、少なくとも、'Private'はそこでは使えないと指摘してくれるんだろ?

Hypothesizer 7
いいえ、実のところ、それはただ無視されます。

Objector 19A
最低だな。...同一名のファンクションが2つあったらどうなる?

Hypothesizer 7
最初に見つかったものが使われるようです。

Objector 19A
...どれが最初に見つかるんだ?

Hypothesizer 7
厳密なルールは知りません。...そのようなルールを知ろうとすることが有意義だとは思われません: それよりも、重複した名前を使わないようにするべきでしょう、Basicは親切に重複を警告するということをしてくれませんが...


5: ユーザー定義タイプのスコープ


Hypothesizer 7
ユーザー定義タイプの名前は、そのユーザー定義タイプが定義されたモジュールの内部でだけ有効であるようです。

Objector 19A
...それは厳しいハンディキャップだな。...定義しているモジュールでしかユーザー定義タイプが使えないとなると...

Hypothesizer 7
実際には、私は、ユーザー定義タイプは、それを定義しているモジュールでだけ有効だと言ったのではなく、その名前がそうだと言ったのです。

Objector 19A
はあ?何の違いもないだろう、そこには?

Hypothesizer 7
いえ、あります。定義モジュール内のあるファンクションがユーザー定義タイプのインスタンスをリターンする場合、そのファンクションを呼ぶ別のモジュールはそのインスタンスにアクセスできます、例えば、'Object'変数を通して。実際、以前の記事にて、私は、そういうコンストラクタの概念を導入しました。

Objector 19A
妙な仕様だな: 名前は使用できないが、データは使用できると...


6: ユーザー定義タイプインスタンスの生存期間?


Objector 19A
あんたのコンストラクタ、'ClassA_ClassA (a_argument0 As String)'内で、'l_this'はローカルインスタンスであって、コンストラクタはそのローカルインスタンスのアドレスをリターンしている(ファンクションリターンについてのあんたの説明によれば)が、それは問題ないのか?

Hypothesizer 7
既に申し上げたとおり'変数'と'データ'を区別しなければなりません。...実際には、'l_this'自体は「インスタンス」ではなく、ポインター変数です。確かに、その変数はローカルですが、その変数に指されているインスタンスがローカルか否かは別問題です。...その重要事項にこれまでに私が読んだドキュメントはどれも言及しないので、自分の推測を何らかのドキュメントで根拠付けられませんが、LibreOffice Basicの振る舞いから推論する限り、ユーザー定義タイプインスタンスはヒープ内にいるのであって、スタック内にあるのではなく、ファンクションがアドレスをリターンするのは問題ないようです。

Objector 19A
それじゃあ、インスタンスはいつ始末されるんだ?ガーベッジコレクトされるのか?

Hypothesizer 7
そう推測することしかできません、これまでに読んだドキュメントはどれもその重要な関心事に言及しないので。


7: 'Object'変数や'Variant'変数をリセットする方法


Hypothesizer 7
'Object'変数が意味上、何も指しも保持もしなくなるときは、その'Object'変数をリセットしたいと考えるのが自然です。

Objector 19B
実は、それが私の未解決の関心事の1つなのよね。'Null'を代入すれば変数がリセットされると自然に考えたんだけど、エラーになるのよねえ!

Hypothesizer 7
はい。Basicはそのように自然な構文をなぜ許さないのか、私は理解できませんが、'Object'変数をリセットする方法は、2つ(私が知る限り)あります。

方法1は、その変数を'Erase'ファンクションにかけることです。

Objector 19B
あら?...確かに、あるドキュメントはそのファンクションをオブジェクトメモリーを開放するファンクションとして挙げていたけど、それを、変数を'Null'にセットする方法として位置づけられなかったわ...

Hypothesizer 7
方法2は、'Nothing'をその変数に代入することです。

Objector 19B
はあ?'Nothing'って何?'Null'とどう違うの?

Hypothesizer 7
えーと、違いを説明するドキュメントを1つ見つけましたが、そこで主張されている違いは現実と一致していないようです。...実際、初期状態では、変数は'Nothing'かつ'Null'であり、'Nothing'が代入された後も、変数は'Nothing'かつ'Null'になります。どうすれば、'Object'変数が'Null'であるが'Nothing'ではない状態、またはその逆になり得るのか、私は知りません。もしも、そうなれないのであれば、違いはないものと断定せざるを得ないでしょう。

他方では、'Variant'変数は、'Erase'ファンクションにかけることでリセットできます。

そして、'Variant'変数は、'Nothing'を代入することで'Null'にセットできます。


8: 'VarType'というファンクションは変数タイプについてのものではない、大体において


Hypothesizer 7
'VarType'というファンクションがありますが、その名前と一部ドキュメントが主張するところに反して、そのファンクションは、変数タイプについてのものではありません、大体において。

Objector 19A
「大体において」?

Hypothesizer 7
例えば、'VarType (1)'は、'integerであること'を意味する'2'をリターンしますが、「それが変数タイプについてのもの」だと主張するのは無理無体です、なぜなら、そこには何の変数も関与していないからです: '1'は変数ではありません。

Objector 19A
それはデータタイプだよ、もちろん。

Hypothesizer 7
'Variant'タイプ変数がファンクション引数として指定されたときは、そのファンクションは、そのvariableにセットされたデータのタイプを報告するか、その変数が空であると報告します、'Variant' 変数変数タイプは'Variant'以外の何物でもありませんが。

Objector 19A
それじゃあ、そのファンクションは'変数タイプ'についてのものでは全然ないんだろう?

Hypothesizer 7
ところが、'Object'タイプ変数がファンクション引数として指定された場合のみ、そのファンクションは、ただ、その変数は'Object'タイプだと、報告します、たとえ、その変数が'Null'状態にあったとしても。

Objector 19A
妙な振る舞いだな。...'VarType (Null)'は何をリターンするんだ?

Hypothesizer 7
''Null'であること'を意味する'1'をリターンします。

Objector 19A
妙だ...


9: UNOオブジェクトの'Struct'プロパティを変更することについての一部の不十分な説明についての補足


Objector 19A
あるドキュメントが主張しているんだ、'struct'は値コピーされるので、'l_unoObjectA.structPropertyA.structMemberA = "A datum"'のようなコードは、そのUNOオブジェクトプロパティを変更せず、そのUNOオブジェクトプロパティのコピーを変更するのだと("l_unoObjectA"はUNOオブジェクト、"structPropertyA"は'struct'プロパティ、"structMemberA"はその'struct'のメンバー変数)。

そのプロパティがある変数に代入されれば、その変数はそのプロパティ値のコピーを保持することになるということは、完全に理解するが、上記コードはそのプロパティを何の変数にも代入してないじゃないか!直接アクセスしてるじゃないか!なぜコピーされるのだ?

Hypothesizer 7
ああ、その説明がそのドキュメント中に置かれているコンテキストを私は知りませんが、'BasicにおけるUNOオブジェクトプロパティアクセス'構文の性質をそのドキュメントが適切に説明していないとしたら、その説明はある不可欠なパーツを欠いている、と見なさなければならないでしょう。

Objector 19A
どういう意味だ?

Hypothesizer 7
「l_unoObjectA.structPropertyA.structMemberA」のような構文は、実は、getterメソッドをコールする'l_unoObjectA.getStructPropertyA ().structMemberA'に対するシンタックスシュガーなのです。その予備説明がなければ、あなたが引用した説明は意味不明です。

Objector 19A
えーと...

Hypothesizer 7
上記コードにおける「l_unoObjectA.structPropertyA」は、実際はファンクションリターンであって、ファンクションリターンの振る舞いにより、それは、プロパティのコピーです。

オフィシャルドキュメントは「オブジェクトはリファレンスとして扱われ、structは値として扱われる」と主張していますが、それは正確な言明ではありません: ファンクションリターンはどれもリファレンスではありません(ある前出セクションで説明されているとおり)。UNOオブジェクトリターンは、実際にはアドレスです(ポインター、アドレス、リファレンスを適切に区別しましょう)。

Objector 19A
えーと...

Hypothesizer 7
その一方、「l_unoObjectA.structPropertyA = l_structA」のような構文は、実は、setterメソッドを呼ぶ'l_unoObjectA.setStructPropertyA (l_structA)'に対するシンタックスシュガーです。

Objector 19A
ふーむ...


10: 結びとその先


Hypothesizer 7
以上が、明確にしなければならないと私が感じた最小限のことです、今のところ。

私はBasicを大規模に使う訳ではない(それは、私のお気に入りのプログラミング言語というわけではありません、その多くの側面がゆえに)ので、私が気付いていない不明瞭なことが他にもあることでしょう。そうした事項には、別の機会に取り組みましょう。


参考資料


<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>