月別アーカイブ: 2021年3月

4D 自力distinct

4D 自力distinctを作ってみたので紹介する。このコマンドはv17、v18で動作確認済み。

4DのDistinctコマンド

4DにはDistinctというコマンドがある。テーブルをクエリーしてセレクションができて、そのテーブルのフィールドの重複を排除したユニークな値の配列を取得できる便利なコマンドだ。

Distinctにはフィールドを一つしか与えることができない。たとえばユニークなコードの配列をください、そのコードに対応した名前の配列(ユニークでないかもしれない)を同時にください、というときは不便。Distinctを実行してもセレクションができるわけではないので、別のフィールドの値を取得するには、得られた配列のユニーク値をもとにしてクエリーし直さなくてはならない。

自力ディスティンクト

そこで「自力ディスティンクト」を作ってみた。ディスティンクトで得られるユニークなフィールドの配列を、データベース側のセットに実現することでセレクションを作る。このモジュールを呼び出すとIDがユニークな配列が返されると同時に、その配列の元になるカレントセレクションが用意される。呼び出し元ではユニークフィールド以外のフィールドの値はSelection to arrayで簡単に持ってこれる。セットを使っているので高速に動く。

このモジュールの特徴は、テーブル名を決め打ちしていることと、ユニークフィールドを整数に限定していること。別のテーブル用とかユニークフィールドが文字列以外の場合はこのモジュールを複製・修正して使う。MVCのモデルメソッドという位置付けである。

//JO_Distinct_byAC_ID
//20200821 wat
//自力Distinct //ジャーナルの勘定科目とカテゴリー
//セレクションは上位で作成しておく
//distinctキーとなるフィールドのポインタは整数、整数の結果配列を返す。

C_POINTER($1;$keyFldPtr)
$keyFldPtr:=$1
C_POINTER($2;$outIDAryPtr)
$outIDAryPtr:=$2 //結果の、IDの配列

C_LONGINT($ac_id)
C_LONGINT($i;$numOfRecs)
C_TEXT($setOrg;$setName)
$setOrg:=”set_org”
CREATE SET([JOURNAL];$setOrg) //最初は全件、評価したら除いていく
$setName:=”result_set”
CREATE EMPTY SET([JOURNAL];$setName) //最初は0件、まだないIDを追加していく

//元セットにレコードがなくなるまで繰り返す
USE SET($setOrg)
$numOfRecs:=Records in selection([JOURNAL])
FIRST RECORD([JOURNAL])
$ac_id:=$keyFldPtr->

While ($numOfRecs>0)
//結果セットを日付でクエリして、なければ追加、
USE SET($setName)
QUERY SELECTION([JOURNAL];$keyFldPtr->=$ac_id)
If (Records in selection([JOURNAL])=0)
//DB全体をクエリして最初のレコードを取得して、セットに追加
QUERY([JOURNAL];$keyFldPtr->=$ac_id)
FIRST RECORD([JOURNAL])
ADD TO SET([JOURNAL];$setName)

End if

//元のセットを日付でクエリして、元のセットからのぞく
USE SET($setOrg)
QUERY SELECTION([JOURNAL];$keyFldPtr->=$ac_id)
For ($i;1;Records in selection([JOURNAL]))
GOTO SELECTED RECORD([JOURNAL];$i)
REMOVE FROM SET([JOURNAL];$setOrg)

End for

//リデュースされた元セットの最初のレコードを取得
USE SET($setOrg)
$numOfRecs:=Records in selection([JOURNAL])
FIRST RECORD([JOURNAL])
$ac_id:=$keyFldPtr->

End while
CLEAR SET($setOrg)

//転送
USE SET($setName)
CLEAR SET($setName)


SELECTION TO ARRAY($keyFldPtr->;$outIDAryPtr->) //カレントセレクションがあるので、呼び出し元ではお好みのフィールドの値をSELECTION TO ARRAYで取得できる

使い方

呼び出し元では次のようにコーディング。この例では元となるセレクションは全件、このように元となるセレクションを作っておいて、そのセレクションの中でユニーク評価をして新たなセレクションを作る。呼び出し後に必要なフィールドの値を転送。

READ ONLY([JOURNAL])
ALL RECORDS([JOURNAL])<;br> JO_Distinct_byAC_ID (->[JOURNAL]JO_D_AC_ID;->$aryAC_ID)
SELECTION TO ARRAY([JOURNAL]JO_D_AC_CODE;$aryAC_CODE)
// SELECTION TO ARRAY(…

4D v18で変わった「Form event」コマンド

「4Dアプリ開発ガイド v18対応版」を制作していたらコマンドの仕様変更に気づいたので報告する。これまでに気づいているのは次の2つ、「FORM Event」と「PAGE SETUP」。

FORM Event

フォームイベントコマンドの仕様が変わった。以前は次のように記述していた。

//A02_frm
// オンロードメソッド
Case of
: (Form event=On Load)
A02_frmOnload
End case

このコードをv18にペーストして実行すると、次のようなエラーになる。

関数の結果が式と一致しません。定数のタイプが無効です。変数のタイプが異なるため比較できません。

ランゲージリファレンスを見ると、v18では戻り値がオブジェクト型になっていた。

https://doc.4d.com/4Dv18/4D/18.4/FORM-Event.301-5233147.ja.html

v15とかv17のプロジェクトをv18に変換すると、次のようなコードに変換される。

//A02_frm
// オンロードメソッド
Case of
: (Form event code=On Load)
A02_frmOnload
End case

「Form event」コマンドは「Form event code」に変更されて、オブジェクト型を返す「FORM Event」が新しく追加された、ということらしい。従来取得していた整数型のイベントコードは「code」というフィールドに格納されている。もともとコードしか返さなかったを、イベントのトリガーとなったオブジェクト名も返すようだ。いいかもしれない。これまでイベントハンドラはオブジェクトメソッドに書いていたが、フォームメソッドにまとめて書いて、「もしこのオブジェクト名だったら」という記述ができるようになったってことかな。

発生したcodeだけを必要とする場合はどちらのコマンドを使っても同じみたいだけど、オブジェクト型を使ってドット表記を参照する方が流行りっぽくてかっこいいかもしれない、とか色々考えて次のようにした。

//A02_frm
//オンロードメソッド
C_LONGINT($frmEvnt)
$frmEvnt:=FORM Event.code
Case of
: ($frmEvnt=On Load)
A02_frmOnLoad
End case

修正ついでに一旦ローカル変数に取得するようにした。これでデバッグしやすくなる。Case文に何回も書くと、その数だけFORM Eventが実行されてしまい、気づかれるほどではないにしても性能的によろしくないので。

PAGE SETUP

廃止になった。プロジェクトをv18に変換すると、次のようなコードに変換されている。

_O_PAGE SETUP

代わりのコマンドが用意されていて、SET PRINT OPTION/GET PRINT OPTION、Print settings to BLOB/BLOB to print settingsを使う、とランゲージリファレンスに書いてある。

https://doc.4d.com/4Dv18R5/4D/18-R5/o-PAGE-SETUP.301-5128159.ja.html

もともと「PAGE SETUP」は、印刷ダイアログを表示することなく、用紙とか印刷設定を前回と同様またはいつも決まった所定のセッティングで印刷したいときに使うコマンドであった。ユーザが毎回印刷設定ダイアログで設定してから印刷する場合は不要だが、印刷設定ダイアログを出さずに印刷したり、前の設定を覚えておいて欲しい場合に実装することになる。v18ではまだそのような仕様を使う状況に遭遇していないので、使うことになったらまた報告する。

夏みかん、今年の収穫は終了

庭に甘夏の木がある。3月、普通の甘夏はこれからが収穫期、だがうちのは終了した。

3月の様子

もう12月から黄色く色づいていた。今年は豊作と思っていたが。

12月の頃の写真

1月くらいからリスがかじり出して、ヒヨドリがつついて、メジロが食べたり、風で落ちたり、どんどんなくなっていく。昨年はこれらの影響で人間は一つも食べられなかった。

リスが食べているのならもう食べられるのでは?ということで、今年は1月から風で落ちたのを食べ始めた。酸っぱい。下手に食べるとむせて大変なことになる。砂糖をかけて食べるとむせないことに気づく。昔ババアがそうやって食べていたのを思い出す。若い頃は酸っぱいものを食べても、多少むせても平気だったがこの歳になるとむせかたが半端なくて辛い。なるほど砂糖をかけて食べるとむせる心配がない。これはこれでかなり美味しい。

2月に入ると樹熟が進んで甘くなってくる。別に八朔があって、こちらは今年初収穫。2月に14個取れた。八朔は美味しいな。

八朔も甘夏も皮は砂糖で煮てジャムにする。鍋に甘夏5個分くらいの皮を千切りにして入れて、水を張って煮る。沸騰したらお湯をこぼして、また水を張って煮る。これを3回繰り返す。4回目の沸騰の後、水が皮よりも少なくなったら砂糖を入れる。350gから400gがうちのお好みかな。これで水気が半分くらいになるまで煮詰める。程々で良い。冷めていく途中で水分が減るからね。

5個分の皮でこの容器の二倍くらいできる

3月14日、最後の4つを収穫。

間引きしていないせいか大きさがかなり違う
最初リスがかじって穴を開け、そこを鳥がつついてこうなる。何十個も食べられた

残りはリスと鳥にやられた残骸。今年は人間も食べられたので良かったです。