<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HTML>
<HEAD>
  <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=shift_jis">
  <META NAME="Author" CONTENT="ktkawabe">
  <META NAME="GENERATOR" CONTENT="ktkawabe's hand with JMemo, note or ME on EPOC, and Emacs on Linux or win32">
  <TITLE>OPLにありがちな。</TITLE>
</HEAD>
<BODY>
<hr>
<center>
[<a href="http://www.hi-ho.ne.jp/~ktkawabe/">表紙</a>]
[<a href="../densha.html">ソフトウェア配布</a>]
[<em><a href="http://www.hi-ho.ne.jp/~ktkawabe/prog.html">プログラミング</a></em>]
[<a href="../hardware">ハードウェア</a>]
[<a href="../others.html">その他</a>]
[<a href="http://a.hatena.ne.jp/ktkawabe/">アンテナ</a>]
</center>
<hr>
<H1>
OPLのナニな点についての覚書の様なもの
</H1>
<p>
OPLというのは思い立ったらすぐにプログラムが書き始められて
なかなか良いものです。
が、OPLにも色々あれなところもあって、苦労する事も無いではありません。
私などそういう時は
「2度と忘れるまい」等と思うのですが、
大体いつも知らないうちに忘れています。
数箇月毎に同じ事で苦いというのもあれなので、
このページに OPLのナニなところを書きとめておく事にしました。</p>
<h2>内容</h2>
<ul>
      <li>第八回 (予告): 引数の型チェック</li>
      <li><a href="#7">第七回</a>: Series 5では動きません (その2: ビットマップ)
          <font size="-1">2003/05/03 (05/04修正)</font></li>
      <li><a href="#6">第六回</a>: 配列の初期化
          <font size="-1">2002/04/20</font></li>
      <li><a href="#5">第五回</a>: 定数なのに何故
          <font size="-1">2002/01/20</font></li>
      <li><a href="#4">第四回</a>: Series 5では動きません (その1: dbmsと SQLを使ったソート)
          <font size="-1">2002/01/06</font></li>
      <li><a href="#3">第三回</a>: トランスレート出来無い
          <font size="-1">2001/12/31</font></li>
      <li><a href="#2">第二回</a>: Revoでは動きません (入れ子の深さ)
          <font size="-1">2001/12/31</font></li>
      <li><a href="#1">第一回</a>: 論理式は何が何でも全部評価
          <font size="-1">2001/12/31</font></li>
    </ul>

<hr>
<h2><a name="7">第七回</a>: Series 5では動きません (その2: ビットマップ)</h2>
<P>
5mxや Revoでは完璧に動いたプログラムが、Series 5 (Classic)上では
"Not Supported"というエラーが出て動かないという経験は
ありませんか。
ソースコードを見ても完璧で、全く見当がつかない、という場合には
ビットマップを疑ってみると良いかもしれません。</p>
<p>
5mxや Revoと違い、Series 5では (少なくとも OPL中から)
カラーデータを扱う事が出来ません。
ボタンやら何やらの画像をカラーで作ってしまった方は、
白黒かグレースケールに変換しないと上記のエラーが出るという事に
なります。
しかし、一見すると全ての画像がグレースケールなのに、やっぱり
上記のエラーが出る事もあります。
これは、それらの画像データが「カラー形式だが、使用されている
色が たまたまグレーか白黒だけ」だからです。
これもちゃんとグレースケール形式に変換しなければなりません。
</p>
<p>画像データの作成に、グレースケールのMBM
(というEPOCネイティブのビットマップ形式)を
直接出力できるソフトウェアを使っていれば何も問題は無いのですが、
Sketchを使っている場合には注意が必要です。
netBookや WINSエミュレータ等、実際にカラーを出力出来る
ハードウェア上では、ER5の Sketchは全てのファイルを
カラー形式でセーブしてしまう様なのです。
使われている色が白黒の二つだけであろうがお構い無しです。</p>
<P><em>(注@2003/05/04：
この文書を作成した当初は、ER5の Sketchが全てこの様な動作を
すると記憶していたのですが、確認してみたところ Series 5mxの
Sketchで作成したデータは Series 5で正常にロード出来ましたので
記述を変更しました。)</em></p>
<p>
これを回避する根本的な方法はありません。
netBook, Series 7や WINSで ER3でも使える画像を作成するには、
MBM形式でセーブし、
それを別の適当なソフト(例えば
EPOC上の MBM Viewや Windows/Linux上の XnView等)で
グレースケールに変換する必要があります。
</p>

<hr>
<h2><a name="6">第六回</a>: 配列の初期化</h2>
<p>OPLには配列変数というものがあります。一次元配列しか許されないのですが、
まあそれはそれで有用なものです(<a href="#6_note">注</a>)。例えば
</p>
<pre>LOCAL table%(256)</pre>
<p>というのは、要素を256ヶ持つ2バイト整数配列を表しています。
しかし、配列変数はあるのにもかかわらず、
配列型のデータ構造を表す記法が存在しません。
これは、配列の初期化の際(例えば内容にあまり規則性の無いテーブルを
配列に代入したい場合等)等に相当痛いです。
以下の例を見ていただければ、言わんとしているところが
御判りいただけるでしょうか:</p>
<pre>LOCAL b%(255)
b%(1) = 21
b%(2) = 43
b%(3) = 261

(中略)

b%(254) = 2
b%(255) = 2</pre>
<p>
C言語では 配列の初期化の際
</p>
<pre>int b[]={21, 43, 261, ...(中略)..., 2, 2}</pre>
<p>という記法が許されたのにと恨めしく思う方もいらっしゃる事かと
思いますが、OPLにおいては恐らくこれは避けられない苦痛ですので
おとなしくあきらめましょう。
</p>
<p><strong><a name="6_note">(注)</a></strong>
neuon &lt;<a href="http://www.neuon.com">http://www.neuon.com</a>&gt;
の nOPLPlusを購入すれば、OPLで多次元配列や構造体を扱ったり、C的な
プリプロセッサを使う等、様々の便利な機能を利用出来る様になります。</p>

<hr>
<h2><a name="5">第五回</a>: 定数なのに何故</h2>
<p>他のポピュラーな言語と同様、OPLでも定数を宣言する事が出来ます。
例えばこんな具合に:</p>
<pre>CONST KArraySize&=&1000</pre>
<p>(Kというのは OPLで定数の前につける記号で、つけなくてもエラーが
出たりはしませんが、習慣上つける事になっているのだそうです。)
定数を定義出来る事自体は結構便利なものです。
さてこの定数、全てのPROCの外で宣言しなければならない事になっており、
更にスコープはGLOBALであるという事が OPL User Guide (又は OPLSDKの
ドキュメント) に記載されております。
となれば、以下の様な用法が許されると期待するのが
人情というものでしょう:</p>
<pre>CONST KArraySize&=20

PROC TEST:
    LOCAL iArray&(KArraySize&), i&
    WHILE i& &lt; KArraySize&
        i& = i&+1
        iArray&(i&) = i&
    ENDWH
ENDP</pre>
<p>
しかし、上記のコードをトランスレートしようとしますと、
<em>iArray&(KArraySize&)</em> の宣言のところで
"Bad Array Size" エラーが出てトランスレート出来ません
(これぐらい許してくれと思うのですが)。
実は、トランスレート済みのバイナリの中では
CONSTによって宣言された定数は全て数値に置換されているので
(もちろん適宜キャストされています!)、
それを考えると更に理不尽感がツノります。
</p>
<hr>
<h2><a name="4">第四回</a>: Series 5 では動きません
(その1: dbmsと SQLを使ったソート)</h2>
<p>
Series 5 と言っても、5mx とか 5mx Pro の事ではありません。
EPOCを搭載した最初の機種である、初代 Series 5 の事です。
Series 5mx は "Series 5 のハードウェアを速くしただけのもの"
という様な印象を持たれがちですが、実は OS もアプリケーションも
相当色々な面で改良がなされています。
さて、その改良の一つに OPLにおけるデータベースの扱いがあります。
早速以下の例を見てみましょう。</p>
<pre>PROC dbTest:
    LOCAL file$(255), i&
        file$="c:\test"
        IF EXIST(file$)
            DELETE file$
        ENDIF

        REM データベースファイルを作成し、ファイル内にテーブルを一つ作成する。
        CREATE file$+" FIELDS number1, number2 TO testTable", A, num1&, num2&
        BEGINTRANS

        REM テーブル内に4つのフィールドを登録。
        REM 0から3までの整数を number1には昇順に、number2には降順に代入
        WHILE i&<4
            INSERT
            A.num1&=i&
            A.num2&=3-i&
            PUT
            i&=i&+1
        ENDWH
        COMMITTRANS
        CLOSE :REM 閉じる。

        REM SQL機能を利用し、先ほどのファイルの中から
        REM 「number2だけが昇順で開かれたビュー」を構築。
        OPEN file$+" SELECT number2 FROM testTable ORDER BY number2 ASC", A, num2&
        i&=0
        WHILE i&<4
            PRINT A.num2&
            i&=i&+1
            NEXT
        ENDWH
        CLOSE
        GET
ENDP
</pre>
<p>
何も考えずにこの例題を実行すると、5mxやそれ以後の機種では
無事に</p>
<pre>0
1
2
3
</pre>
<p>という結果が出力されるのですが、
Series 5では OPEN文のところで以下の様なエラーが出て異常終了してしまいます:
</p>
<pre>Information
Error in DBTEST\DBTEST
Not supported</pre>
<p>
何故かというと、5mxの OPLでは データベースのソートについての機能が
強化されているからです。
具体的にどこがどう違うのか見るため、上記のコードの内容をちょびっと
読んでみましょう。</p>
<p>この例では、C:\testという名前のデータベースファイルを作成し、
その中に <em>testTable</em> という名前のテーブルを構築しています。
このテーブルは、<em>number1</em>と<em>number2</em>という名前の
二つの整数フィールドだけから出来ています。
最初に 0から 3までの整数を <em>number1</em>と<em>number2</em>に
それぞれ昇順、降順で代入していますので、
最終的にデータベースファイルの構造は
以下の様になります:</p>
<table border="2" cellpadding="5">
      <caption>ファイル (C:\test)</caption>
      <tr>
	<td><table border="1" cellpadding="3">
	    <caption>テーブル (testTable)</caption>
	    <tr>
	      <th><br></th>
	      <th><em>number1</em></th>
	      <th><em>number2</em></th>
	    </tr>
            <tr>
	      <th><em>Record 1</em></th>
	      <td>0</td>
	      <td>3</td>
	    </tr>
	    <tr>
	      <th><em>Record 2</em></th>
	      <td>1</td>
	      <td>2</td>
	    </tr>
	    <tr>
	      <th><em>Record 3</em></th>
	      <td>2</td>
	      <td>1</td>
	    </tr>
	    <tr>
	      <th><em>Record 4</em></th>
	      <td>3</td>
	      <td>0</td>
	    </tr>
	  </table></td>
      </tr>
    </table>
	
<p>その後のOPEN文で、上記ファイルから <em>number2</em>だけを抽出し、
昇り順に並べ替えたビューを構築します。この結果、
上記のプログラムを Series 5mxで実行してみると、number2の内容が
(最初は 3210という降順だったのに) 0123と昇順で表示されたのです。
</p>
<p>
しかし、Series 5では このOPEN文でエラーが出ました。
これは 5mxとただの 5の OPLの機能の差なのです。
それでは、初代 Series 5では データのソートは出来ないのかというと、
決してそうではありません。
Series 5では、もう一手間かけてやらないといけない (というよりも、Series 5mx
やそれ以後の機種では「もう一手間かける必要が無くなった」) のです。
Series 5で同じ事をするには、database OPXを使い、
ファイル内に <strong>インデックス</strong> を書き込みます。
インデックス とは、あるテーブルの中のあるデータ
(例えば testTableの中の number2)
をソートするための情報を書き込んだものだと思えばよいでしょう。
実際には インデックス 作成は2段構えになっており、インデックス の対象となる
フィールド名を記録した <strong>キー</strong> を作成し、
その キー をもとにしてデータベースファイルに
インデックス を書き込むという段取りで作業を進めます。
では早速やってみましょう。
以下のコードは Series 5でもそれ以外の機種でも問題無く動きます。
</p>
<pre>REM 改良版
INCLUDE "dbase.oxh"
PROC dbTest2:
    LOCAL file$(255), i&, key&
        file$="c:\test"
        IF EXIST(file$)
            DELETE file$
        ENDIF

        REM データベースファイルを作成し、ファイル内にテーブルを一つ作成する。
        CREATE file$+" FIELDS number1, number2 TO testTable", A, num1&, num2&
        BEGINTRANS

        REM テーブル内に4つのフィールドを登録。
        REM 0から3までの整数を number1には昇順に、number2には降順に代入
        WHILE i&<4
            INSERT
            A.num1&=i&
            A.num2&=3-i&
            PUT
            i&=i&+1
        ENDWH
        COMMITTRANS
        CLOSE :REM 閉じる。

        <strong>REM キーの作成と定義
        key&=DBNEWKEY&: :REM 新しいキーを作成する。
        DBADDFIELD:( key&, "number2", 1)
        REM キーにインデックス対象のフィールド名とソート順 (1: ASC, 2: DESC) を登録
        REM これでキーの準備終了

        REM 先ほど作成したキーを、file$ の中の testTableというテーブルに
        REM "newIndex" という名前をつけて書き込む。
        DBCREATEINDEX:("newindex", key&, file$, "testTable")
        
        REM キーはもう不要なので、削除する。
        DBDELETEKEY:(key&)</strong>

        REM SQL機能を利用し、先ほどのファイルの中から
        REM 「number2だけが昇順で開かれたビュー」を構築。
        OPEN file$+" SELECT number2 FROM testTable ORDER BY number2 ASC", A, num2&
        i&=0
        WHILE i&<4
            PRINT A.num2&
            i&=i&+1
            NEXT
        ENDWH
        CLOSE
        GET
ENDP
</pre>
<p>
インデックスの作成は5mx以後の機種でももちろんサポートされています。
という事で、データベースにおけるインデックスの作成は
過去との互換性を保つ意味で大事です、というハナシでした。
</p>
<p>
<font size="-1">注) 
上記のコードで作成されたデータベースファイルは、Series 5では
number2しかソートすることが出来ません。
DBADDFIELD:をもう一度行って、<em>key&</em>に <em>number2</em>だけでなく
<em>number1</em>
も登録してから DBCREATEINDEXすれば、
どちらのフィールド名でソートする事も可能になります。
</font></p>
<hr>
<h2><a name="3">第三回</a>: トランスレート出来ない</h2>
<p>
「あなたの配布している OPLソースをいざ自分の Series 5にコピーして、
トランスレートしようとすると
"Illegal Character" と言われてトランスレート
出来ませんでした。」
私は他人様から時々こう言われる事があります。
自分でも自分のプログラムでたまにやります。
この場合、プログラムエディタ内からそのソースをテキストにExportし、
再度Importし直してからトランスレートすればよいのです。</p>
<p>
この原因は、ソースの中に含まれる non-breaking spaceという
キャラクタであると考えてほぼ間違いありません。
non-breaking spaceが存在すると、Series 5や Series 5mx等では
トランスレートに失敗しますが、
WINSではこのキャラクタを普通の空白と同様に扱い、
トランスレートに成功してしまうのです。
プログラムエディタ (に限らず、普通のEPOCのプログラム全般) では
Shift+SPACE でこのキャラクタが入力される様になっていますから、
WINS上でソースを書いた場合にはついつい気付かずにそのまま配布して
しまうという事になります。
テキストにエキスポートする事で、non-breaking spaceは普通の空白に
変換されるので、それを再度インポートすればトランスレートできるように
なるのです。</p>

<hr>
<h2><a name="2">第二回</a>: Revoでは動きません (入れ子の深さ)</h2>
<p>1997年に執筆されたという OPL User Guideによれば、
IF...ENDIF, WHILE...ENDWH, DO...UNTIL の制御構造は最大 8階層まで
入れ子に出来る事になっています。
それ以上深い入れ子にすると、トランスレート時に "too complex"エラーが
出てトランスレートできないのです。
ところが、初代Revoの初期ロットなのかどうか判りませんが、8階層の
入れ子構造を持つOPLアプリケーションでエラーが発生するという報告を
受けた事がありました。Series 5や 5mxだとちゃんと実行出来るのですが、
Revoだと駄目だというのです。
全く原因がわからず苦労したのですが、結局</p>
<pre>IF 1
    IF 1
        IF 1
            IF 1
                IF 1
                    IF 1
                        IF 1
                            IF 1
                            ENDIF
                        ENDIF
                    ENDIF
                ENDIF
            ENDIF
        ENDIF
     ENDIF
ENDIF
</pre>
<p>という
何もしないプログラムでも同じエラーが出る事が判り、
入れ子のレベルを一つ下げる事で解決したのでした。
Revo Plusや Makoだとどうなんでしょうね。</p>

<hr>
<h2><a name="1">第一回</a>: 論理式は何が何でも全部評価</h2>
<p>
論理式の真偽を評価する場合、OPLインタープリタは無駄に頑張ります。
どんな場合だろうが、構成要素であるそれぞれの論理値を
必ず最後まで評価してくださるのです、無駄なのに。
一応次のテストプログラムを見てみましょう。
</p>
<pre>
PROC test:
    PRINT "Start"
    IF 1=2 AND test2&:=3 : REM ここの条件判断が今回のお題です。
        PRINT "Impossible to come here."
    ENDIF
    PRINT "END. Press any key."
    GET
ENDP

PROC test2&:
    PRINT "test2&: was executed."
    RETURN 4
ENDP
</pre>
<p>
このテストプログラムを実行すると、結果は
</p>
<pre>
Start
test2&: was executed.
END. Press any key.
</pre>
<p>となりました。
<em>PROC test2&:</em> が実行されていますね。
これは、<br>
<em>IF 1=2 AND test2&:=3</em><br>
という条件文の中から呼び出されたのです。
この論理式の最初の要素である<br>
<em>1=2</em><br>
というのは明らかに偽なのですから、
2番目の論理値である<br>
<em>test2&:=3</em><br>
の結果に関わらず、
論理式の値<br>
<em>1=2 AND test2&:=3</em><br>
は偽であるに決まっています。
つまりよく考えてみれば、1番目の論理値を評価した時点で
2番目の論理値を調べる必要は全く無い事が解かるはずなのです。
賢いコンパイラなりインタープリタなりを使い慣れた方ですと、
この様な場合に 2番目の論理値は評価されない
という事を期待してしまいがちだと思いますが、
OPLでそれを仮定してコードを書いてしまうと
予期しない動作の原因となる可能性がありますので注意が必要です。</p>
<p>
私の場合、「AでかつBの時だけCという処理をしたいのだが、
Aで無い場合にはBを評価されては困る」という場合に、アサハカにも
</p>
<pre>IF A AND B
    C
ENDIF
</pre>
<p>と書いてしまうことがままあります。
例えば、name$という文字列の 20文字目から 5文字分が "From:" である場合にだけ
"From:" と画面に書きたいとしましょう。
何も考えずに <em>IF MID$(name$, 20, 5)="From:"</em> 等とやってしまうと、
<em>name$</em>の長さが 20より短い場合に <em>MID$</em>のところで
Invalid arguments というエラーが出て異常終了する事があります。
そこで、<em>name$</em> の長さが24文字以上(20文字目から5文字分なので)
であるかどうかを事前にチェックする事にしましょう。
以下に示すのは、私の書きがちなアサハカなコードです:
</p>
<pre>IF LEN(name$)>=24 AND MID$(name$, 20, 5)="From:"
    PRINT "From:"
ENDIF</pre>
<p>
実はこれでは <em>name$</em> の長さによらず
<em>MID$</em> が評価されてしまうので、
長さチェックを入れた意味が全くありません
(<em>name$="a"</em> 等として試してみましょう)。
面倒ですが
</p>
<pre>IF LEN(name$)>=24
    IF MID$(name$, 20, 5)="From:"
        PRINT "From:"
    ENDIF
ENDIF</pre>
<p>としておけば、この様なトラブルを避ける事が出来ます。</p>
    
<HR>
本ページ(http://www.hi-ho.ne.jp/ktkawabe/OPLTips/index.html)
へのリンクは御自由にどうぞ。
なお、嘘を書こうという気は毛頭無いにせよ、
ktkawabeはこのページに書いてある内容の正確性をいかなる意味でも
保証する事は出来ません。
<br>
ktkawabe&nbsp;@&nbsp;hi-ho.ne.jp
</body>
</html>


