2020年5月31日日曜日

モデル駆動によるクラウド・アプリケーション開発

「クラウド」というとすでにややレトロな響きも出てきたかもしれません。しかし、Kubernetes/Dockerといった足回りやAWS Lambdaのような要素技術は登場していますが、クラウド・アプリケーション全体を構築するための開発手法の整備にはまだ手がついていない状況だと思います。

当ブログではModegrammingをキーワードに、モデリングとプログラミングを融合したモデル駆動開発を提唱しており、このModegrammingのクラウド・アプリケーションへの適用を中心的なテーマとしています。

Modegrammingを成立させるためには、開発手法を下支えする開発ツールや実行プラットフォームの存在が不可欠です。この目的で現在、以下の5つのプロダクトを開発しています。

SmartDox
文書処理系
SimpleModeler
モデルコンパイラ
Kaleidox
アクション言語
Arcadia
Webフレームワーク
Prefer Cloud Platform
クラウド・アプリケーション・プラットフォーム

2008年からScalaを使って少しずつ開発を進めてきましたが、かなり整備が進んできたので現在の状況についてまとめてみました。

SmartDox

SmartDoxはSmartDocの後継となる文書処理系です。OSSとして開発しています。

SmartDocは文書形式としてXMLを採用していましたが、SmartDoxではMarkdownとEmacs orgを融合させた独自のプレインテキストを文書形式として使用します。

本ブログもSmartDox形式で記述したものを、SmartDoxでBlogger形式を生成したものをBloggerに貼り付けています。

文書処理系とモデル駆動開発との関係は一見なさそうにもみえますが、Modegrammingの中でSmartDoxを重視しているのは、Literate Programming、Literate Modeling、ひいてはLiterate Modegrammingの基盤となる文書処理基盤を提供するためです。具体的には、後述するSimpleModelerとKaleidoxがSmartDox基盤を使用してLiterate Modeling, Literate Programmingを実現しています。

SmartDoxは当面はSimpleModeler、Kaleidox、Arcadiaの中でLiterate Modegramming(Literate Modeling + Literate Programming)を実現するための要素技術として使用していく予定です。

将来的にはSmartDocと同様に、独立した文書処理系として公開するかもしれません。

SimpleModeler

SimpleModelerはRelaxerの後継となるモデルコンパイラです。OSSとして開発しています。

RelaxerはXMLスキーマ言語(RELAX)からJavaなどのプログラムを生成するスキーマコンパイラです。

RelaxerはXMLを扱うアプリケーション開発に非常に有益だったと思いますが、スコープがXMLに閉じているため応用範囲が限られました。ただ、プログラムの自動生成の可能性については十分認識することができました。

次の段階としてオブジェクトモデルからのプログラムの自動生成を行うために開発を始めたのがSimpleModelerです。SimpleModelerはオブジェクトモデルからJavaやScalaなどのプログラムを生成するモデルコンパイラです。

2008年から開発を始め、一応の実用化レベルの機能は提供できています。後述のPCP開発にも使用しています。

Scalaの内部DSLを主となるDSLとして採用したのですが、オブジェクト・モデルの記述にはあまり適さないことが分かってきたのでSmartDoxをベースとした外部DSLを主DSLとするように切り替える開発を進めていました。

同時に、オブジェクト・モデルのメタモデルの拡張をクラウド・アプリケーション向けに拡張する作業を進めています。

Kaleidox

モデル駆動開発をターゲットにしたアクション言語です。OSSとして開発しています。

モデル駆動開発のアキレス腱となっているのが、振る舞いの記述です。オブジェクト・モデルでは、静的構造は自動生成可能な精度のモデル記述が可能ですが、動的振る舞いについては状態機械モデルまでが自動生成可能な精度のモデル記述が可能なものの、それ以上の振る舞いについてはスコープ外になっています。

このため、モデル・コンパイラでプログラムの自動生成をする場合でもプログラミングとの併用が必須となり、ちらにしてもプログラミングが必要となるのであればプログラミングを主軸にし、モデリングは概念レイヤーのメモ書きレベルの使用に限るようなバランスが妥当ともいえます。

この「振る舞い」に関する問題がモデル駆動開発のアキレス腱となっていました。

この問題は、モデル駆動開発用のアクション言語によって解決できるのではないか、というアイデアのもと開発したのがKaleidoxです。

Kaleidoxについては本ブログで紹介してきました。スクリプト言語としては実用ゾーンに入って来たと思いますが、引き続き本命のアクション言語として活用できるように、SimpleModelerとの繋ぎ込みを進めていく予定です。

Arcadia

Arcadiaはモデル駆動開発をターゲットにしたWebフレームワークです。OSSとして開発しています。

モデルコンパイラでプログラムの自動生成を行う際に問題となるのが、自動生成したプログラムの実行基盤です。

この問題に対応するために、ArcadiaはWeb UIを構築するWebアプリケーションの自動生成を用意にするための以下のような仕組みを導入しています。

  • プログラムではなくメタデータ設定でWebアプリケーション開発を可能にする。
  • バックエンドのクラウド・プラットフォームとシームレスに連携できるようにする。
  • シナリオによるWeb入力機構を搭載している。
  • アクション言語(kaleidox)を使って処理の記述が可能になっている。

バックエンドのクラウド・プラットフォームとして後述するPrefer Cloud Platformも使用できますが、アダプタを作れば任意のクラウド・プラットフォームを使用することが可能です。

アクション言語であるkaleidoxとの統合は、モデル駆動開発との連携を指向したものです。

Arcadiaは基本部は開発済みです。KaleidoxとSimpleModelerが一段落した後に公開したいと考えています。

Prefer Cloud Platform

Prefer Cloud Platform(以下PCP)はボクがCTOをやっているEverforth社が提供する商用のクラウド・アプリケーション・プラットフォームです。

PCPを用いることでクラウド・アプリケーションを高速、低コストで開発することができます。

PCPは、モデル駆動開発の実行環境としても使用できるように設計されており、現時点でもSimpleModelerによる自動生成を活用して開発を行っています。

しかし、現時点ではPCP上で動作するクラウド・アプリケーションを自動生成するところまでは到達できていません。SimpleModeler、Kaleidox、Arcaidaが整備されることで、オブジェクト・モデルからのクラウド・アプリケーションの自動生成が可能になることを目指しています。

関連書籍

モデル駆動開発を行うためにはオブジェクト・モデルのメタモデルの整備も必要です。

この整備はwakhok時代から進めており、2008年当時の成果を以下の書籍にまとめています。いずれもwakhok時代に教科書として使うことを目的に書いたものです。

しかし、2008年当時とはクラウド環境をめぐる状況が大きく変わっており、それらの要素を取り込んだメタモデルの拡張が必要です。

上記の書籍の内容をベースに、SimpleModelerの開発を進めながらメタモデルの拡張も同時に行っています。

その成果もいずれ文書としてまとめていきたいと思います。

まとめ

本ブログが提唱しているModegrammingを支える開発ツール、実行プラットフォームの開発の近況について説明しました。

2008年から少しずつ進めてきた開発ですが、すでに商用化されているPCPに加えて、いくつかのOSSも実用ゾーンに入りつつあります。

OSSが実用ゾーンに入ってきたことで、クラウド・アプリケーション向けのオブジェクト・モデルのメタモデルを、実用化を前提として具体的に検討をすすめることが可能になってきました。メタモデルの拡張はSimpleModelerの開発と並行して行っています。

まだまだ開発途上ですが、本ブログで継続して開発の成果を報告していく予定です。

2020年4月30日木曜日

Kaleidox/スタック

アクション言語は複雑なアルゴリズムを記述するというより、入力データと状態の組み合わせに対してデータ変換を行い変換後のデータを後続のシグナルに乗せて発信するという処理が中心となります。

このような処理の記述にはパイプラインのセマンティクスが適しています。

パイプラインを記述するためのプログラミング後の構文としてはUNIXシェルの以下の形式が有力です。

cat file.txt | grep foo | sort

この記述方式は直感的で強力ですが、一般的なプログラミング言語の文法とセマンティクスが異なるため、汎用言語の一要素として併存させることは苦労が伴います。パイプラインで記述できない処理が出てきた時に、パイプライン以外の方式で記述した上で、それをパイプラインにどのように簡潔な形で統合していくのかという点が問題です。煩雑な記述方式で統合すると、"直感的で強力"な文法が失われてしまいます。

この問題への対応としてKaleidoxではスタックのメカニズムを用意しています。スタックを用いることで、パイプラインのセマンティクスに近い形で通常のプログラミング言語との"直感的で強力"な統合を目指しています。

スタック

式の評価結果はスタックにプッシュされます。

たとえばREPLから1、2、3と順に入力するとそれぞれの式の評価結果である数値がスタックにプッシュされていきます。

kaleidox> 1
1
kaleidox> 2
2
kaleidox> 3
3

スタックの様子はstackコマンドで表示することができます。先程入力した数値がスタック上に逆順で積まれていることが確認できました。

kaleidox> #stack
 1: 3
 2: 2
 3: 1
...略...

関数は必須引数数が定義されており、引数の数がこれに満たない場合は不足分をスタックから持ってくるようになっています。

前述のスタックの状態の元で関数+を引数なしで投入してみます。+関数の必須引数の数は2なので、スタックから2つの値をポップし、これを引数として利用します。

kaleidox> +
6

+関数実行後のスタックは以下になります。

kaleidox> #stack
 1: 1
...略...

より実用的な例としてCSVデータをチャートで表示する処理を考えてみます。

以下のCSVデータをcity.csvとして用意します。

都市,緯度,経度,平均気温,降水量
札幌,43.055248,141.345505,8.0,1158
仙台,38.254162,140.891403,11.9,1219
東京,35.680909,139.767372,15.3,1460
名古屋,35.154919,136.920593,14.9,1575
大阪,34.702509,135.496505,16.2,1400
広島,34.377560,132.444794,15.0,1603
福岡,33.579788,130.402405,16.0,1690
那覇,26.204830,127.692398,22.4,2128

table-load関数でCSVデータをtableオブジェクトとして読み込みます。

kaleidox> table-load file:city.csv
Table[5x8]
kaleidox> :show:print
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━┓
┃都市  │緯度     │経度      │平均気温│降水量┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━┿━━━━━━┫
┃札幌  │43.055248│141.345505│8.0     │1158  ┃
┃仙台  │38.254162│140.891403│11.9    │1219  ┃
┃東京  │35.680909│139.767372│15.3    │1460  ┃
┃名古屋│35.154919│136.920593│14.9    │1575  ┃
┃大阪  │34.702509│135.496505│16.2    │1400  ┃
┃広島  │34.377560│132.444794│15.0    │1603  ┃
┃福岡  │33.579788│130.402405│16.0    │1690  ┃
┃那覇  │26.204830│127.692398│22.4    │2128  ┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━┛

続けて、table-select関数を使って読み込んだ表の必要部分だけ切り取ります。ここでは第0、1、3カラムを残し、都市、緯度、平均気温の表にしています。

kaleidox> table-select 0,1,3
Table[3x8]
kaleidox> :show:print
Table[3x8]
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━┓
┃都市  │緯度     │平均気温┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━┫
┃札幌  │43.055248│8.0     ┃
┃仙台  │38.254162│11.9    ┃
┃東京  │35.680909│15.3    ┃
┃名古屋│35.154919│14.9    ┃
┃大阪  │34.702509│16.2    ┃
┃広島  │34.377560│15.0    ┃
┃福岡  │33.579788│16.0    ┃
┃那覇  │26.204830│22.4    ┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━┛

table-select関数は第1引数に選択するカラムを示す範囲を第2引数に対象のtableオブジェクトを取ります。第1引数のみ指定されてるので、第2引数はスタックよりtable-load関数の評価結果であるcity.csvを読み込んだtableオブジェクトが指定されます。

続けてtable-chart関数を使ってチャートの表示を行います。

kaleidox> table-chart :analyzes 'simple-regression

table-chart関数ではキーワードanalyzesにsimple-regressionを指定しているので、散布図の上に単回帰分析の結果が表示されます。キーワードの後ろに指定する引数ではtableオブジェクトを取ります。ここが省略されているので、スタックよりtable-select関数の評価結果であるカラム射影後のtableオブジェクトが指定されます。

この結果、以下のチャートが表示されます。

関数

ここまでREPLで試してきた処理では以下の3つの関数を順に実行しています。

  • table-load
  • table-select
  • table-chart

これは前出のUNIXシェルのパイプラインの記述方式を使うと以下のようなイメージです。

table-load file:csv | table-select 0,1,3 | table-chart :analyzes 'simple-regression

この処理を通常のLisp文法で関数化したものが以下のshowchart関数です。

(defun showchart (uri)
  (setq a (table-load uri))
  (setq b (table-select 0,1,3) a)
  (table-chart :analyzes 'simple-regression b))

前段の処理結果を後段に受け渡していくパイプライン的な処理ですが、この例でも分かるように手続き型的な記述方式では変数を使ってデータを受け渡していく形になります。

この記述方式でも実用的には問題ありませんが、UNIXシェルの記述方式に比べると煩雑であることは否めません。

KaleidoxではUNIXシェルのような記述はできませんが、関数呼び出しをシーケンシャルに並べてパイプライン的な処理を簡潔に記述することができます。

この記述を用いて関数化したものが以下のshowchart関数です。

(defun showchart (uri)
  (table-load uri)
  (table-select 0,1,3)
  (table-chart :analyzes 'simple-regression))

table-load, table-select, table-chartのそれぞれの評価結果をスタックを用いて後続の関数に受け渡しています。

スタックを用いてこのような記述方式を可能にすることで、パイプライン的な処理を簡潔に記述することが可能になっています。

まとめ

アクション言語は、汎用言語としての記述力も重要ですが、処理の中心はデータ変換のパイプライン処理になるので、パイプライン処理を簡潔に記述する記述能力も重要になります。

Kaleidoxではスタックを用いてパイプライン処理を簡潔に記述することを可能にしているわけです。

関数型言語ではFunctor(Scalaではmap関数)やMonad(ScalaではflatMap関数)を用いてパイプライン処理を記述することが可能になっています。この関数型言語方式もパイプラインの記述方式としては有力、というか汎用言語としては本命と思いますが、アクション言語にはやや重いのではないかというのがボクの見立てで、スタックによる記述方式を開発しました。

関数型言語方式とスタック方式の比較検討はいずれ行いたいと思います。

諸元

  • Kaleidox : 0.1.11

2020年3月31日火曜日

Kaleidox: モデル駆動開発

Kaleidoxはモデル駆動開発のアクション言語を指向しています。実用言語の観点から、データ操作の枠組みとしてRecordとTableを中心とした機能群を用意しています。

一方、モデル駆動開発に適用するためにはモデル側との連携も必要になります。

今回はモデル駆動開発のためのモデル定義とアクション言語の連携について、現時点で実装している簡単な機能を例に探っていきます。

初期設定

以下のinit.kldを用意します。

* env

db.default.driver="org.h2.Driver"
db.default.url="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"

* voucher

** City

#+caption: 特性一覧
| 特性 | 名前                | 型     | 多重度 | ラベル       |
|------+---------------------+--------+--------+--------------|
| 属性 | name                | int    |      1 | 都市 |
| 属性 | latitude            | string |      1 | 緯度         |
| 属性 | longitude           | int    |      1 | 経度         |
| 属性 | average_temperature | int    |      1 | 平均気温     |
| 属性 | precipitation       | int    |      1 | 降水量       |

* prologue

store-create 'City 'city

この初期設定により以下の設定が行われます。

  • メモリDBの初期化
  • モデルの定義
  • モデルに従ったデータベースのテーブルの作成
メモリDBの初期化

envセクションで組み込みRDBのH2をメモリモードで起動するように記述しています。

モデルの定義

voucherセクションでモデルの定義を行っています。

モデルに従ったデータベースのテーブルの作成

prologueセクションでテーブルを作成する記述を行っています。

モデル

voucherセクションで以下のモデルを記述しています。

ボクは2008年ごろから帳票指向アーキテクチャをターゲットにしたモデリングの手法SimpleModelingを整備しています。

SimpleModelingではSimpleModelerというモデルコンパイラでプログラムの自動生成を行うことが可能です。

voucherはSimpleModelingのアーキタイプで「伝票」を記述するためのモデルです。一般のOOADではValue ObjectやDTO(Data Transfer Object)という方向性のモデルです。

SimpleModelingでは、voucher以外にも多数の種類のモデルを定義していますが、現時点ではKaleidoxではvoucherのみサポートしています。

現在SimpleModelerの新版を開発中で、これをKaleidoxに組み込めば各種モデルの操作をKaleidoxで行うことができるようになる予定ですが、現在は暫定的にvoucherのみ扱い可能にしています。

voucherセクションで定義しているvoucherは都市の緯度・経度、平均気温、降水量を定義した以下のものです。

* voucher

** City

#+caption: 特性一覧
| 特性 | 名前                | 型     | 多重度 | ラベル       |
|------+---------------------+--------+--------+--------------|
| 属性 | name                | int    |      1 | 都市 |
| 属性 | latitude            | string |      1 | 緯度         |
| 属性 | longitude           | int    |      1 | 経度         |
| 属性 | average_temperature | int    |      1 | 平均気温     |
| 属性 | precipitation       | int    |      1 | 降水量       |

この表の定義によってvoucher Cityが定義されます。

テーブルの作成

前述のinit.kldのprologueセクションは以下になっています。

* prologue

store-create 'City

store-create関数はテーブルの作成を行います。第1引数にモデル名Cityを指定しています。

このためKaleidox起動時にprologueセクションでこのstore-create関数が実行され、バウチャーCityのモデル定義に従ったテーブルCityがメモリDB内に作成されます。

今回はテスト目的でメモリDBを使用していますが、MySQLなどのRDBを使う場合にすでにテーブルが作成されている場合はこの処理は不要です。

データ格納

init.kldの設定によりKaleidox起動後はテーブルcityが作成されています。このテーブルにデータの格納を行ってみます。

都市,緯度,経度,平均気温,降水量
札幌,43.055248,141.345505,8.0,1158
仙台,38.254162,140.891403,11.9,1219
東京,35.680909,139.767372,15.3,1460
名古屋,35.154919,136.920593,14.9,1575
大阪,34.702509,135.496505,16.2,1400
広島,34.377560,132.444794,15.0,1603
福岡,33.579788,130.402405,16.0,1690
那覇,26.204830,127.692398,22.4,2128

「file:city.csv」によりcity.csvを読み込みます。 city.csvはTableオブジェクトとして読み込まれます。

kaleidox> file:city.csv
Table[5x8]
kaleidox> :show:print
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━┓
┃都市  │緯度     │経度      │平均気温│降水量┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━┿━━━━━━┫
┃札幌  │43.055248│141.345505│8.0     │1158  ┃
┃仙台  │38.254162│140.891403│11.9    │1219  ┃
┃東京  │35.680909│139.767372│15.3    │1460  ┃
┃名古屋│35.154919│136.920593│14.9    │1575  ┃
┃大阪  │34.702509│135.496505│16.2    │1400  ┃
┃広島  │34.377560│132.444794│15.0    │1603  ┃
┃福岡  │33.579788│130.402405│16.0    │1690  ┃
┃那覇  │26.204830│127.692398│22.4    │2128  ┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━┛

読み込んだTableオブジェクトをテーブルCityに書き込みます。

Tableのヘッダにあるカラム名をキーにしてテーブルに書き込みます。このカラム名はVoucherモデルの名前だけでなくラベルとも照合されます。

たとえばVoucherモデルの定義ではCityオブジェクトの都市名を格納する属性はnameですが、ラベルが「都市」になっています。このためTableオブジェクトのカラム名nameだけでなくカラム名「都市」も格納対象となります。他のカラムも同様に、Cityオブジェクトの各属性とも対応付けられます。そして、Cityオブジェクトを格納するCityテーブルの該当するカラムとの対応付けも行われます。

テーブルへの追加格納はstore-insert関数で行います。

第1引数にテーブル名、第2引数にTableオブジェクトを指定します。ここではTableオブジェクトの読み込み直後で、Tableオブジェクトがスタックの先頭に積まれているので第2引数を省略してスタック上のTableオブジェクトを指定しています。

kaleidox> store-insert 'City
(0 0 0 0 0 0 0 0)

store-insert関数の返却値は新規作成したレコードのIDのリストです。CityテーブルはIDを持たないテーブルのためレコード数分の0を要素として持つリストが返されています。

データの読み込み

store-select関数でテーブルに格納したデータを読み込みます。

kaleidox> store-select 'City
Table[5x10]
kaleidox> :show:print
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━┓
┃name  │latitude │longitude │average_temperature│precipitation┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━┫
┃札幌  │43.055248│141.345505│8.0                │1158.0       ┃
┃仙台  │38.254162│140.891403│11.9               │1219.0       ┃
┃東京  │35.680909│139.767372│15.3               │1460.0       ┃
┃名古屋│35.154919│136.920593│14.9               │1575.0       ┃
┃大阪  │34.702509│135.496505│16.2               │1400.0       ┃
┃広島  │34.37756 │132.444794│15.0               │1603.0       ┃
┃福岡  │33.579788│130.402405│16.0               │1690.0       ┃
┃那覇  │26.20483 │127.692398│22.4               │2128.0       ┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━┛

store-select関数のデフォルト動作ではモデルの属性名が使用されます。このため、都市はname、緯度はlatitudeといったヘッダ名を使用したTableオブジェクトとなっています。

プログラム的にデータを処理する場合はこの方が都合がよいですが、外部出力を行う場合はモデル側のラベル名がTableオブジェクトのヘッダ名になっている方が便利です。

このための指定を行うオプションとしてstore-select関数ではtable-headerスイッチを用意しています。

以下では(Lispの)キーワード指定によってtable-headerスイッチにlabelを指定しています。このためstore-select関数では、データを読み込むTableオブジェクトのカラム名をモデルのラベル名にしています。

kaleidox> store-select :table-header 'label 'City
Table[5x10]
kaleidox> :show:print
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━┓
┃都市  │緯度     │経度      │平均気温│降水量┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━┿━━━━━━┫
┃札幌  │43.055248│141.345505│8.0     │1158.0┃
┃仙台  │38.254162│140.891403│11.9    │1219.0┃
┃東京  │35.680909│139.767372│15.3    │1460.0┃
┃名古屋│35.154919│136.920593│14.9    │1575.0┃
┃大阪  │34.702509│135.496505│16.2    │1400.0┃
┃広島  │34.37756 │132.444794│15.0    │1603.0┃
┃福岡  │33.579788│130.402405│16.0    │1690.0┃
┃那覇  │26.20483 │127.692398│22.4    │2128.0┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━┛

チャート表示

最後に、テーブルから読み込んでみたデータをチャートに表示しましょう。ここからは前回「Kaleidox/単回帰分析」と同じ処理です。詳細はそちらを参照してください。

まずTableオブジェクトからチャートに表示するデータを抽出します。

kaleidox> table-select 0,1,3 x
Table[3x8]
kaleidox> :show:print
Table[3x8]
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━┓
┃都市  │緯度     │平均気温┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━┫
┃札幌  │43.055248│8.0     ┃
┃仙台  │38.254162│11.9    ┃
┃東京  │35.680909│15.3    ┃
┃名古屋│35.154919│14.9    ┃
┃大阪  │34.702509│16.2    ┃
┃広島  │34.377560│15.0    ┃
┃福岡  │33.579788│16.0    ┃
┃那覇  │26.204830│22.4    ┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━┛

このTableオブジェクトをtable-chart関数でチャートに表示します。

まず、抽出後のTableオブジェクトを変数xに束縛します。Tableオブジェクトは抽出直後なのでスタックの先頭に積まれています。setq関数の第2引数を省略することでスタック上のTableオブジェクトが束縛対象となります。

kaleidox> setq x
kaleidox> table-chart x

以下のようなチャートが表示されます。


また単回帰分析を行う場合はanalyzeスイッチにsimple-regressionを指定します。

kaleidox> table-chart :analyze 'simple-regression x

以下のようなチャートが表示されます。


まとめ

頭出しレベルですが、モデル駆動開発のイメージを確認するために簡単な連携例を説明しました。

基本的な方向性としては、モデル定義を行った後は簡単なアクション言語による記述のみでアプリケーションを記述したいということです。

モデル定義がのままプログラミングにつながることで、モデルをプログラムに翻訳することによって起きているプログラミングの工数を大幅に低減することできます。また、これと同じぐらい重要なのはモデルとプログラムのインピーダンスミスマッチをなくすることができることです。モデルとプログラミングが一体となることで、よりモデリングに集中することができます。

SimpleModelerの改版版は現在開発中ですが、これをKaleidoxに組み込むことでより広範囲のモデルをモデル駆動の対象としてアプリケーション開発に活用できることができるようになります。

諸元

  • Kaleidox : 0.1.10
気象データ

以下のページ「5.おもな都市の月平均気温・月降水量」のデータを使用しました。

http://www.biodic.go.jp/reports/2-2/hyo/aa126_001.html

2020年2月29日土曜日

Kaleidox: 単回帰分析

KaleidoxプログラムではTableオブジェクトとRecordオブジェクトを中心に演算を行っていきます。

このためTableオブジェクトとRecordオブジェクトを操作するための機能を多数サポートする予定にしています。

今回はそういった便利な機能の中で、Tableオブジェクトのチャート表示と単回帰分析機能について説明します。

データ

単回帰分析を行う対象のデータをCSVファイルcity.csvとして用意しました。このデータでは日本の都市の緯度経度と平均気温、降水量を記述しています。

都市,緯度,経度,平均気温,降水量
札幌,43.055248,141.345505,8.0,1158
仙台,38.254162,140.891403,11.9,1219
東京,35.680909,139.767372,15.3,1460
名古屋,35.154919,136.920593,14.9,1575
大阪,34.702509,135.496505,16.2,1400
広島,34.377560,132.444794,15.0,1603
福岡,33.579788,130.402405,16.0,1690
那覇,26.204830,127.692398,22.4,2128

データの読み込み

KaleidoxのREPLからURIリテラルで上記CSVファイル名を投入すると、データを読み込んだTableオブジェクトが生成されます。

kaleidox> file:city.csv
Table[5x8]
kaleidox> :show:print
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━┓
┃都市  │緯度     │経度      │平均気温│降水量┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━┿━━━━━━┫
┃札幌  │43.055248│141.345505│8.0     │1158  ┃
┃仙台  │38.254162│140.891403│11.9    │1219  ┃
┃東京  │35.680909│139.767372│15.3    │1460  ┃
┃名古屋│35.154919│136.920593│14.9    │1575  ┃
┃大阪  │34.702509│135.496505│16.2    │1400  ┃
┃広島  │34.377560│132.444794│15.0    │1603  ┃
┃福岡  │33.579788│130.402405│16.0    │1690  ┃
┃那覇  │26.204830│127.692398│22.4    │2128  ┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━┛

table-chart関数

データを読み込んだ直後にtable-chart関数を投入すると、以下に示すようにTableオブジェクトから必要な情報を取得して散布図を表示します。

kaleidox> table-chart

デフォルトでは表の第1列を項目名、第2列をX軸、第3列をY軸と解釈して自動でデータを読み込みます。

Kaleidoxは関数の投入結果をスタックとヒストリに保存しています。table-chart関数を引数なしで起動するとスタックの先頭にあるオブジェクトを引数として使用するのでこのようなことが可能になっています。

historyコマンドで確認するとCSVファイルをローディングした結果のTableオブジェクトは6番目に格納されていました。今後はヒストリの6番目のTableオブジェクトを使用します。

kaleidox> :history
 1: nil
 2: Table[3x10]
 3: Matrix[1x10]
 4: Matrix[2x10]
 5: Matrix[1x2]
 6: Table[5x8]
 7: Window

行列

回帰分析のような数値演算を行う場合、通常はTableオブジェクトを行列に変換してから行列に対して線形代数の演算を行っていきます。

Kaleidoxの場合はtable-matrix関数でTableオブジェクトから行列を抽出することができます。

kaleidox> table-matrix #6
Matrix[4x8]
kaleidox> :show
Matrix[4x8]
┌                                ┐
│43.055248 141.345505 8.0  1158.0│
│38.254162 140.891403 11.9 1219.0│
│35.680909 139.767372 15.3 1460.0│
│35.154919 136.920593 14.9 1575.0│
│34.702509 135.496505 16.2 1400.0│
│34.37756  132.444794 15.0 1603.0│
│33.579788 130.402405 16.0 1690.0│
│26.20483  127.692398 22.4 2128.0│
└                                ┘

この例から分かるように、Tableオブジェクトの持つ表データのヘッダ行と数値以外の情報が入っている列を取り除いた情報を行列に再構築してくれます。

ただし今回は行列は使用しません。

table-select関数

回帰分析のような数値演算を行う場合、通常はMatrixオブジェクトを使用しますが、単回帰分析のような定番の分析手法は表に対して直接使用できるようになっています。

この記事の先頭でtable-chart関数によってTableオブジェクトの内容から自動抽出した情報を散布図として表示する操作を行いました。

この散布図に回帰分析の結果を重ねて表示してみることにします。

file.csvのデータをそのまま使ってもよいのですが、都市の緯度と経度に対して単回帰分析を行っても面白くないので、都市の緯度と平均気温に対して単回帰分析を行ってみます。

まずTableデータの第1列に都市名、第2列に緯度、第3列に平均気温を取るTableオブジェクトを生成します。これはtable-chart関数が散布図を描く時に、デフォルトでは第1列に項目名、第2列にX軸の値、第3列にY軸の値を想定しているためです。

この変換はtable-select関数で行うことができます。以下の例ではtable-select関数の引数に範囲オブジェクト「0,1,3」を指定しています。この結果オリジナルの表の第1列、第2列、第4列の内容をこの順番で並べたTableオブジェクトが生成されます。なお範囲オブジェクトの添字は0ベース、表の表現では添字は1ベースで表現しています。

kaleidox> table-select 0,1,3 #6
Table[3x8]
kaleidox> :show:print
Table[3x8]
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━┓
┃都市  │緯度     │平均気温┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━┫
┃札幌  │43.055248│8.0     ┃
┃仙台  │38.254162│11.9    ┃
┃東京  │35.680909│15.3    ┃
┃名古屋│35.154919│14.9    ┃
┃大阪  │34.702509│16.2    ┃
┃広島  │34.377560│15.0    ┃
┃福岡  │33.579788│16.0    ┃
┃那覇  │26.204830│22.4    ┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━┛

このTableオブジェクトに対してtable-chart関数を適用すると以下の散布図が表示されました。

kaleidox> table-chart

単回帰分析

単回帰分析を行った結果をチャート上に表示する場合はanalyzeオプションにsimple-regressionを指定します。

先程作成したTableオブジェクトのヒストリを確認します。

:history
 1: nil
 2: Table[3x10]
 3: Matrix[1x10]
 4: Matrix[2x10]
 5: Matrix[1x2]
 6: Table[5x8]
 7: Window
 8: Table[3x8]

8番目にあることが分かったので「#8」でアクセスすることができます。

単回帰分析の結果を散布図上に表示するためにはanalyzeオプションにsimple-regressionを指定します。

kaleidox> table-chart :analyze 'simple-regression #8

上記のtable-chart関数の結果、以下の単回帰分析付きの散布図が表示されました。


まとめ

CSVファイルから読み込んだTableオブジェクトに対して、table-selection関数による簡単な加工のみで散布図の表示、単回帰分析の結果表示を行うことができました。

通常のプログラミング言語でこの処理を行うとCSVファイルの読み込み、チャート機能の利用、数値演算処理による単回帰分析など案外大変なコーディングが必要になりますが、Kaleidoxではあっさりと実現できました。

このようなことが可能になるのは、Kaleidoxで汎用データ構造であるTableオブジェクトを言語の中軸機能と位置付け、Tableオブジェクトを対象にさまざまな関数を用意しているからです。

Kaleidoxはアクション言語として設計していますが、PIM(Platform Independent Model)的な抽象処理のみが記述できるだけだと実用性に乏しくなります。そういった抽象的な機能と同時に今回のTableデータ処理のような地味な実用機能もサポートし、シームレスに連携できることが必要だと考えています。

諸元

  • Kaleidox : 0.1.10
気象データ

以下のページ「5.おもな都市の月平均気温・月降水量」のデータを使用しました。

http://www.biodic.go.jp/reports/2-2/hyo/aa126_001.html

2020年1月31日金曜日

Kaleidox: Matrix

BigDataやAIなどがアプリケーションを構成する重要部品となってきています。このBigDataやAIをアプリケーションで扱う上で重要な技術要素として行列(matrix)があります。

Kaleidoxでは行列をMatrixオブジェクトとして記述して各種行列演算をすることができます。

入力

Matrixオブジェクトの入力方法として以下の方法について見ていきます。

  • Matrixリテラル
  • matrix-load関数
Matrixリテラル

Matrixオブジェクトのリテラルは以下のように二重のカギ括弧で囲まれたエリアに改行と空白を区切り記号として記述したものです。

[[
43.055248 141.345505 8.0 1158
38.254162 140.891403 11.9 1219
35.680909 139.767372 15.3 1460
35.154919 136.920593 14.9 1575
34.702509 135.496505 16.2 1400
34.377560 132.444794 15.0 1603
33.579788 130.402405 16.0 1690
26.204830 127.692398 22.4 2128
]]

REPLから入力すると以下のようになります。

kaleidox> [[
43.055248 141.345505 8.0 1158
38.254162 140.891403 11.9 1219
35.680909 139.767372 15.3 1460
35.154919 136.920593 14.9 1575
34.702509 135.496505 16.2 1400
34.377560 132.444794 15.0 1603
33.579788 130.402405 16.0 1690
26.204830 127.692398 22.4 2128
]]
Matrix[4x8]
kaleidox> :show
Matrix[4x8]
┌                                ┐
│43.055248 141.345505 8.0  1158.0│
│38.254162 140.891403 11.9 1219.0│
│35.680909 139.767372 15.3 1460.0│
│35.154919 136.920593 14.9 1575.0│
│34.702509 135.496505 16.2 1400.0│
│34.37756  132.444794 15.0 1603.0│
│33.579788 130.402405 16.0 1690.0│
│26.20483  127.692398 22.4 2128.0│
└                                ┘
matrix-load関数

matrix-load関数でCSVファイルの内容からMatrixオブジェクトを生成します。matrix-load関数では内容の中から自動的に行列化可能な数値部分を取り出してMatrixオブジェクトにします。

日本の都市について緯度経度と平均気温、降水量を記述したCSVファイル city.csv を用意します。

都市,緯度,経度,平均気温,降水量
札幌,43.055248,141.345505,8.0,1158
仙台,38.254162,140.891403,11.9,1219
東京,35.680909,139.767372,15.3,1460
名古屋,35.154919,136.920593,14.9,1575
大阪,34.702509,135.496505,16.2,1400
広島,34.377560,132.444794,15.0,1603
福岡,33.579788,130.402405,16.0,1690
那覇,26.204830,127.692398,22.4,2128

matrix-load関数を使うと以下のようにMatrixオブジェクトを取得することができます。

kaleidox> matrix-load file:city.csv
Matrix[4x8]
kaleidox> :show
Matrix[4x8]
┌                                ┐
│43.055248 141.345505 8.0  1158.0│
│38.254162 140.891403 11.9 1219.0│
│35.680909 139.767372 15.3 1460.0│
│35.154919 136.920593 14.9 1575.0│
│34.702509 135.496505 16.2 1400.0│
│34.37756  132.444794 15.0 1603.0│
│33.579788 130.402405 16.0 1690.0│
│26.20483  127.692398 22.4 2128.0│
└                                ┘
Tableオブジェクト

TableオブジェクトからMatrixオブジェクトを取得することができます。

引き続きCSVファイル city.csv を使用します。

都市,緯度,経度,平均気温,降水量
札幌,43.055248,141.345505,8.0,1158
仙台,38.254162,140.891403,11.9,1219
東京,35.680909,139.767372,15.3,1460
名古屋,35.154919,136.920593,14.9,1575
大阪,34.702509,135.496505,16.2,1400
広島,34.377560,132.444794,15.0,1603
福岡,33.579788,130.402405,16.0,1690
那覇,26.204830,127.692398,22.4,2128

このCSVファイルをtable-load関数を使ってTableオブジェクトとして読み込みます。

kaleidox> table-load file:city.csv
Table[5x8]
kaleidox> :show:print
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━┓
┃都市  │緯度     │経度      │平均気温│降水量┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━┿━━━━━━┫
┃札幌  │43.055248│141.345505│8.0     │1158  ┃
┃仙台  │38.254162│140.891403│11.9    │1219  ┃
┃東京  │35.680909│139.767372│15.3    │1460  ┃
┃名古屋│35.154919│136.920593│14.9    │1575  ┃
┃大阪  │34.702509│135.496505│16.2    │1400  ┃
┃広島  │34.377560│132.444794│15.0    │1603  ┃
┃福岡  │33.579788│130.402405│16.0    │1690  ┃
┃那覇  │26.204830│127.692398│22.4    │2128  ┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━┛

Tableオブジェクトを起点にさまざまな操作を行うことができますが、ここではmatrixメソッドを使用してMatrixオブジェクトに変換しています。matrix-load関数と同様に行列化可能なデータを抽出してMatrixオブジェクト化しています。

kaleidox> .matrix
Matrix[0x8]
kaleidox> :show
Matrix[4x8]
┌                                ┐
│43.055248 141.345505 8.0  1158.0│
│38.254162 140.891403 11.9 1219.0│
│35.680909 139.767372 15.3 1460.0│
│35.154919 136.920593 14.9 1575.0│
│34.702509 135.496505 16.2 1400.0│
│34.37756  132.444794 15.0 1603.0│
│33.579788 130.402405 16.0 1690.0│
│26.20483  127.692398 22.4 2128.0│
└                                ┘

参照

Matrixオブジェクトを参照して各種データを取得することができます。

準備としてcity.csvに入っているデータをMatrix化したものを変数xに束縛します。

kaleidox> setq x (matrix-load file:city.csv)
Matrix[4x8]
kaleidox> x
Matrix[4x8]
kaleidox> :show
Matrix[4x8]
┌                                ┐
│43.055248 141.345505 8.0  1158.0│
│38.254162 140.891403 11.9 1219.0│
│35.680909 139.767372 15.3 1460.0│
│35.154919 136.920593 14.9 1575.0│
│34.702509 135.496505 16.2 1400.0│
│34.37756  132.444794 15.0 1603.0│
│33.579788 130.402405 16.0 1690.0│
│26.20483  127.692398 22.4 2128.0│
└                                ┘
行の参照

Matrixオブジェクトの行はrowメソッドで取得することができます。引数に行番号を指定します。行番号は0始まりです。

kaleidox> x.row(0)
[43.055248 141.345505 8.0 1158.0]
列の参照

Matrixオブジェクトの列はcolumnメソッドで取得することができます。引数に列番号を指定します。列番号は0始まりです。

kaleidox> x.column(3)
[1158.0 1219.0 1460.0 1575.0 1400.0 1603.0 1690.0 2128.0]
データの参照

Matrixオブジェクトのデータはatメソッドで取得することができます。引数に列番号、行番号を指定します。

kaleidox> x.at(2,3)
14.9

演算

準備として2✕2の正方行列を変数xに束縛します。

kaleidox> setq x [[
1 2
3 4
]]
Matrix[2x2]
kaleidox> :show
Matrix[2x2]
┌       ┐
│1.0 2.0│
│3.0 4.0│
└       ┘
加算

加算は+メソッドです。

kaleidox>+ x x
Matrix[2x2]
kaleidox> :show
Matrix[2x2]
┌       ┐
│2.0 4.0│
│6.0 8.0│
└       ┘
積算

積算は*メソッドです。

kaleidox> * x x
Matrix[2x2]
kaleidox> :show
Matrix[2x2]
┌         ┐
│7.0  10.0│
│15.0 22.0│
└         ┘
逆行列

逆行列はinvメソッドで行います。

kaleidox> x.inv
Matrix[2x2]
kaleidox> :show
Matrix[2x2]
┌                                       ┐
│-1.9999999999999996 0.9999999999999998 │
│1.4999999999999998  -0.4999999999999999│
└                                       ┘
ランク

行列のランクはrankメソッドで取得します。

kaleidox> x.rank
2

出力

Matrixオブジェクトの出力方法として以下の方法について見ていきます。

  • matrix-save関数
  • Table

準備としてcity.csvに入っているデータをMatrix化したものを変数xに束縛します。

kaleidox> setq x (matrix-load file:city.csv)
Matrix[4x8]
kaleidox> x
Matrix[4x8]
kaleidox> :show
Matrix[4x8]
┌                                ┐
│43.055248 141.345505 8.0  1158.0│
│38.254162 140.891403 11.9 1219.0│
│35.680909 139.767372 15.3 1460.0│
│35.154919 136.920593 14.9 1575.0│
│34.702509 135.496505 16.2 1400.0│
│34.37756  132.444794 15.0 1603.0│
│33.579788 130.402405 16.0 1690.0│
│26.20483  127.692398 22.4 2128.0│
└                                ┘
matrix-save関数

Matrixオブジェクトはmatrix-save関数を使ってCSV形式でファイルに保存することができます。

第1引数に保存するファイル名、第2引数にMatrixオブジェクトを指定します。

kaleidox> matrix-save file:output.csv x
t

matrix-save関数で保存したCSVファイルoutput.csvには以下のCSVが格納されます。

"43.055248","141.345505","8.0","1158.0"
"38.254162","140.891403","11.9","1219.0"
"35.680909","139.767372","15.3","1460.0"
"35.154919","136.920593","14.9","1575.0"
"34.702509","135.496505","16.2","1400.0"
"34.37756","132.444794","15.0","1603.0"
"33.579788","130.402405","16.0","1690.0"
"26.20483","127.692398","22.4","2128.0"
Table

Matrixオブジェクトのtableメソッドで行列データをTableオブジェクトに変換することできます。表のカラム名は1からの連番となります。

kaleidox> x.table
Table[4x8]
kaleidox> :show:print
┏━━━━━━━━━┯━━━━━━━━━━┯━━━━┯━━━━━━┓
┃1        │2         │3   │4     ┃
┣━━━━━━━━━┿━━━━━━━━━━┿━━━━┿━━━━━━┫
┃43.055248│141.345505│8.0 │1158.0┃
┃38.254162│140.891403│11.9│1219.0┃
┃35.680909│139.767372│15.3│1460.0┃
┃35.154919│136.920593│14.9│1575.0┃
┃34.702509│135.496505│16.2│1400.0┃
┃34.37756 │132.444794│15.0│1603.0┃
┃33.579788│130.402405│16.0│1690.0┃
┃26.20483 │127.692398│22.4│2128.0┃
┗━━━━━━━━━┷━━━━━━━━━━┷━━━━┷━━━━━━┛

Tableオブジェクトを起点にMatrixオブジェクトによる行列演算の結果を各種応用に利用していくことができます。

まとめ

今回はKaleidoxで行列を扱うMatrixオブジェクトについて説明しました。

Matrixの演算に加えて、CSVによる外部入出力、Tableオブジェクトとの連携を容易に行うことができます。モデル駆動開発の場合でも、BigDataやAIの応用を考えた場合、アクション言語ではドメインモデルと行列を併用してシームレスに連携できる必要があるはずです。Matrixオブジェクトはこのような応用で便利に使用できると思います。

諸元

  • Kaleidox : 0.1.9
気象データ

以下のページ「5.おもな都市の月平均気温・月降水量」のデータを使用しました。

http://www.biodic.go.jp/reports/2-2/hyo/aa126_001.html

2019年12月30日月曜日

Kaleidox: Table

前回はKaleidoxの中軸となるRecordオブジェクトについて説明しました。

今回はRecordオブジェクトと同様にKaleidoxの中軸オブジェクトであるTableオブジェクトについて説明します。

Tableオブジェクトは文字通り表を記述するオブジェクトで、Recordオブジェクトの列にスキーマとヘッダ、フッタの情報を加えたものになっています。

準備

Tableをデータベース入出力で使用するための環境としてKaleidoxの初期化ファイルinit.kldに以下の設定をします。

この設定をしておくと、H2データベースのメモリデータベース上にpersonテーブルが作成され、2レコードが格納された状態になります。

* env

db.default.driver="org.h2.Driver"
db.default.url="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false"

* voucher

** person

#+caption: 特性一覧
|特性  | 名前 | 型     | 多重度 | ラベル  |
|------+------+--------+--------+---------|
| 属性 | id   | int    |      1 | User ID |
| 属性 | name | string |      1 | 名前    |
| 属性 | city | string |      ? | 市      |

* data

** person

*** x

100,Taro,Yokohama
200,Hanako,Kawasaki

* main

store-create 'person
store-insert 'person x
DATA区画

準備として用意したinit.kldでは以下のデータ区画を定義しています。

* data

** person

*** x

100,Taro,Yokohama
200,Hanako,Kawasaki

変数xにCSVで記述された表データを持つTableオブジェクトが束縛されます。

入力

Tableオブジェクトの入力方法として以下の方法について見ていきます。

  • データベース読込み
  • CSVファイル
  • LTSVファイル
  • XMLファイル
  • JSONファイル
  • HTMLファイル
データベース読込み

Kaleidoxでは、データベースアクセスの仕組みとして以下の2つを用意しています。

Store
データストア機能
SQL
SQLで直接データベースアクセス
Store

データストア機能を使うと、スキーマ定義といったメタ情報を登録することで、データベースに対してレコードベースのインタフェースでアクセスすることができます。

データストアからstore-selectで検索すると、検索結果がテーブルを記述するTableオブジェクトに格納されて返されます。

以下ではstore-selectに検索条件を指定せずテーブル名のみを指定しているので全件検索になります。(ただし、読込件数はデフォルト値の件数で制限されます。)

kaleidox> store-select 'person
Table[3x2]

読み込んだTableの詳細情報を表示すると以下になります。

kaleidox> :show
┏━━━┯━━━━━━┯━━━━━━━━┓
┃id │name  │city    ┃
┣━━━┿━━━━━━┿━━━━━━━━┫
┃100│Taro  │Yokohama┃
┃200│Hanako│Kawasaki┃
┗━━━┷━━━━━━┷━━━━━━━━┛

Tableはオブジェクトなのでプロパティまたはメソッドを使って必要な情報を取り出すことができます。

以下headメソッドを使用して先頭の要素を取り出しています。

kaleidox> .head
id:100 name:Taro city:Yokohama
kaleidox> :show
Record[2] name:Taro city:Yokohama
SQL

データストア機能を使わず直接SQLを使用してデータベース内のデータをTableとRecordとして取得することもできます。

sql関数でSQL文を指定するとSQLが発行されます。SELECT文の場合は結果がTableとして返ってきます。

kaleidox> sql "select * from person"
Table[3x1]
kaleidox> :show
Table[3x1]
┏━━━┯━━━━┯━━━━━━━━┓
┃ID │NAME│CITY    ┃
┣━━━┿━━━━┿━━━━━━━━┫
┃100│Taro│Yokohama┃
┗━━━┷━━━━┷━━━━━━━━┛
kaleidox> .head
ID:100 NAME:Taro CITY:Yokohama
CSVファイル

CSVファイルからテーブルを読み込むことができます。

以下のCSVファイルpersons.csvを用意します。

id,name,city
100,Taro,Yokohama
200,Hanako,Kawasaki

CSVファイルの読込みにはtable-load関数を用います。table-load関数の引数にCSVファイルのURLを指定します。ローカルファイルでもHTTP経由でのリモートファイルでも読込み可能です。

kaleidox> table-load file:persons.csv
Tabble[3x2]

showコマンドで内容を表示すると以下のようにTableオブジェクトとして読み込まれています。

kaleidox> :show
Table[3x2]
┏━━━┯━━━━━━┯━━━━━━━━┓
┃id │name  │city    ┃
┣━━━┿━━━━━━┿━━━━━━━━┫
┃100│Taro  │Yokohama┃
┃200│Hanako│Kawasaki┃
┗━━━┷━━━━━━┷━━━━━━━━┛
LTSVファイル

LTSVファイルからテーブルを読み込むことができます。

以下のLTSVファイルpersons.ltsvを用意します。

id:100 name:Taro city:Yokohama
id:200 name:Hanako city:Kawasaki

LTSVファイルの読込みにはtable-load関数を用います。table-load関数の引数にLTSVファイルのURLを指定します。ローカルファイルでもHTTP経由でのリモートファイルでも読込み可能です。

kaleidox> table-load file:persons.ltsv
Table[3x2]
kaleidox> :show
Table[3x2]
┏━━━┯━━━━━━┯━━━━━━━━┓
┃id │name  │city    ┃
┣━━━┿━━━━━━┿━━━━━━━━┫
┃100│Taro  │Yokohama┃
┃200│Hanako│Kawasaki┃
┗━━━┷━━━━━━┷━━━━━━━━┛
XMLファイル

XML文書から以下のいずれかの構造を読み取り、その情報に基づいてTableオブジェクトを生成することができます。

  • 表データ
  • レコードデータ
表データ

XML文書が以下のようにリスト構造になっている場合は表データとして解釈することができます。

<accounts>
  <account>
    <id>100</id>
    <name>Taro</name>
    <city>Yokohama</city>
  </account>
  <account>
    <id>200</id>
    <name>Hanako</name>
    <city>Kawasaki</city>
  </account>
</accounts>

table-make関数はXML文書の構造を解釈してTableオブジェクトとして読み込みます。

このXML文書をpersons.xmlとして用意してtable-make関数を呼び出すとTableオブジェクトとして読み込むことができます。

kaleidox> table-make file:persons.xml
Table[3x2]
kaleidox> :show
Table[3x2]
┏━━━┯━━━━━━┯━━━━━━━━┓
┃id │name  │city    ┃
┣━━━┿━━━━━━┿━━━━━━━━┫
┃100│Taro  │Yokohama┃
┃200│Hanako│Kawasaki┃
┗━━━┷━━━━━━┷━━━━━━━━┛
レコードデータ

XML文書が以下のようにレコード構造になっている場合はレコードデータとして解釈することができます。

<account>
  <id>100</id>
  <name>Taro</name>
  <city>Yokohama</city>
</account>

このXML文書をperson.xmlとして用意してtable-make関数を呼び出すとTableオブジェクトとして読み込むことができます。

レコード構造の場合は1レコードを持つTableオブジェクトとなります。

kaleidox> table-make file:person.xml
Table[3x1]
kaleidox> :show
Table[3x1]
┏━━━┯━━━━┯━━━━━━━━┓
┃id │name│city    ┃
┣━━━┿━━━━┿━━━━━━━━┫
┃100│Taro│Yokohama┃
┗━━━┷━━━━┷━━━━━━━━┛
JSONファイル

JSON文書から以下のいずれかの構造を読み取り、その情報に基づいてTableオブジェクトを生成することができます。

  • 表データ
  • レコードデータ
表データ

JSON文書が以下のように配列になっている場合は表データとして解釈することができます。

[{
  "id": "100",
  "name": "Taro",
  "city": "Yokohama"
},{
  "id": "200",
  "name": "Hanako",
  "city": "Kawasaki"
}]

このJSON文書をpersons.jsonとして用意してtable-make関数を呼び出すとTableオブジェクトとして読み込むことができます。

kaleidox> table-make file:persons.json
Table[3x2]
kaleidox> :show
Table[3x2]
┏━━━┯━━━━━━┯━━━━━━━━┓
┃id │name  │city    ┃
┣━━━┿━━━━━━┿━━━━━━━━┫
┃100│Taro  │Yokohama┃
┃200│Hanako│Kawasaki┃
┗━━━┷━━━━━━┷━━━━━━━━┛
レコードデータ

JSON文書が以下のようにレコード構造になっている場合はレコードデータとして解釈することができます。

{
  "id": "100",
  "name": "Taro",
  "city": "Yokohama"
}

このJSON文書をperson.jsonとして用意してtable-make関数を呼び出すとTableオブジェクトとして読み込むことができます。

レコード構造の場合は1レコードを持つTableオブジェクトとなります。

kaleidox> table-make file:person.json
Table[3x1]
kaleidox> :show
Table[3x1]
┏━━━┯━━━━┯━━━━━━━━┓
┃id │name│city    ┃
┣━━━┿━━━━┿━━━━━━━━┫
┃100│Taro│Yokohama┃
┗━━━┷━━━━┷━━━━━━━━┛
HTMLファイル

HTML文書からTableオブジェクトを生成することができます。この場合は、HTML文章内の表構造を抽出し、条件に適合するものをTableオブジェクトとして取り出します。

以下のHTMLファイルpersons.htmlにはtableタグによって表が1つ定義されています。

<html>
    <head>
<title>表サンプル</title>
    </head>
    <body>
<p>表のサンプルです</p>
<table>
   <thead>
<tr><th>id</th><th>name</th><th>city</th></tr>
   </thead>
   <tbody>
<tr><td>100</td><td>Taro</td><td>Yokohama</td></tr>
<tr><td>200</td><td>Hanako</td><td>Kawasaki</td></tr>
   </tbody>
</table>
    </body>
</html>

このHTMLファイルに対してtable-make関数を適用するとHTML文書内の表データをTableオブジェクトとして抽出することができました。

kaleidox> table-make file:persons.html
Table[3x2]
kaleidox> :show
Table[3x2]
┏━━━┯━━━━━━┯━━━━━━━━┓
┃id │name  │city    ┃
┣━━━┿━━━━━━┿━━━━━━━━┫
┃100│Taro  │Yokohama┃
┃200│Hanako│Kawasaki┃
┗━━━┷━━━━━━┷━━━━━━━━┛

出力

データストア機能を使ってTableのデータをデータベースに格納することができます。

以下ではstore-insert関数を使って、personテーブルにTableの情報を追加しています。

kaleidox> store-insert x
(101)
kaleidox> store-get 'person 101
ID:101 NAME:Hanako CITY:Kawasaki
kaleidox> store-select 'person
Table[3x2]
kaleidox> :show
Table[3x2]
┏━━━┯━━━━━━┯━━━━━━━━┓
┃id │name  │city    ┃
┣━━━┿━━━━━━┿━━━━━━━━┫
┃100│Taro  │Yokohama┃
┃101│Hanako│Kawasaki┃
┗━━━┷━━━━━━┷━━━━━━━━┛

参照

Tableの内容はRecordのシーケンスとして参照することができます。

準備

前述のinit.kldの設定によって、変数xにTableオブジェクトが束縛されています。

kaleidox> x
Table[3x2]
kaleidox> :show
Table[3x2]
┏━━━┯━━━━━━┯━━━━━━━━┓
┃id │name  │city    ┃
┣━━━┿━━━━━━┿━━━━━━━━┫
┃100│Taro  │Yokohama┃
┃200│Hanako│Kawasaki┃
┗━━━┷━━━━━━┷━━━━━━━━┛

このTableオブジェクトを参照の対象にします。

プロパティ取得

Tableオブジェクトのプロパティは以下のようにプロパティ名を指定して取得することができます。

以下ではTableオブジェクトのカラム数をプロパティwidthで、レコード数をプロパティheightで取得しています。

kaleidox> x.width
3
kaleidox> x.height
2
レコード操作

Tableオブジェクトの先頭レコードはプロパティheadで取得することができます。

kaleidox> x.head
id:100 name:Taro city:Yokohama
kaleidox> :show
Record[3] id:100 name:Taro city:Yokohama

また、先頭レコードを除いた残りのレコードを保持したTableオブジェクトはプロパティtailで取得することができます。

kaleidox> x.tail
Table[3x1]
kaleidox> :show
Table[3x1]
┏━━━┯━━━━━━┯━━━━━━━━┓
┃id │name  │city    ┃
┣━━━┿━━━━━━┿━━━━━━━━┫
┃200│Hanako│Kawasaki┃
┗━━━┷━━━━━━┷━━━━━━━━┛

Tableオブジェクトが空の場合はプロパティheadはnilを返します。

kaleidox> setq y x.tail
Table[3x1]
kaleidox> y.tail
Table[3x0]
kaleidox> :show
Table[3x0]
┏━━┯━━━━┯━━━━┓
┃id│name│city┃
┣━━┿━━━━┿━━━━┫
┗━━┷━━━━┷━━━━┛
kaleidox> .head
nil
スキーマ

Tableオブジェクトのスキーマ情報はプロパティschemaで取得することができます。

kaleidox> x.schema
Schema[3] id,int,1 name,string,1 city,string,?
kaleidox> :show
Schema[3]
┏━━━━┯━━━━━━━━┯━━━━━━━━━━━━┓
┃name│datatype│multiplicity┃
┣━━━━┿━━━━━━━━┿━━━━━━━━━━━━┫
┃id  │int     │1           ┃
┃name│string  │1           ┃
┃city│string  │?           ┃
┗━━━━┷━━━━━━━━┷━━━━━━━━━━━━┛

まとめ

今回はKaleidoxの中軸オブジェクトの一つであるTableについて説明しました。

データベースやCSVファイルといった外部リソースの情報をTableオブジェクトとして読込み、Recordオブジェクト単位で処理していくのがKaleidoxの基本処理モデルです。TableとRecordをハブにして各種の連携を進めていきます。

諸元

  • Kaleidox : 0.1.8

2019年11月30日土曜日

Kaleidox: Record

Kaleidoxはレコードを記述するRecordというオブジェクトを提供しています。

RecordはKaleidoxの中軸となるオブジェクトで、データ処理を行う場合などRecordをハブにして様々な連携を行うようになっています。

今回はRecordの使い方について説明します。

準備

Recordをデータベース入出力で使用するための環境としてKaleidoxの初期化ファイルinit.kldに以下の設定をします。

この設定をしておくと、H2データベースのメモリデータベース上にpersonテーブルが作成され、1レコードが格納された状態になります。

* env

db.default.driver="org.h2.Driver"
db.default.url="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"

* voucher

** person

#+caption: 特性一覧
|特性  | 名前 | 型     | 多重度 | ラベル  |
|------+------+--------+--------+---------|
| 属性 | id   | int    |      1 | User ID |
| 属性 | name | string |      1 | 名前    |
| 属性 | city | string |      ? | 市      |

* main

store-create 'person
store-insert 'person id:100,name:Taro,city:Yokohama

入力

LXSVによるリテラル記述とデータベース読込みでRecordを作成することができます。

LXSV

LXSVリテラルによってRecordを作成することができます。

kaleidox> name:Taro,city:Yokohama
name:Taro city:Yokohama

showコマンドで内容を表示すると2つのプロパティを持つRecordであることが分かります。

kaleidox> :show
Record[2] name:Taro city:Yokohama
データベース読込み

Kaleidoxでは、データベースアクセスの仕組みとして以下の2つを用意しています。

Store
データストア機能
SQL
SQLで直接データベースアクセス
Store

データストア機能を使うと、スキーマ定義といったメタ情報を登録することで、データベースに対してレコードベースのインタフェースでアクセスすることができます。

store-get関数ではデータベースのテーブルからデータをRecordとして読み込むことができます。

以下ではpersonテーブルからID 100のRecordを取得しています。

kaleidox> store-get 'person 100
ID:100 NAME:Taro CITY:Yokohama

データストアからstore-selectでRecordを検索すると、検索結果がテーブルを記述するTableオブジェクトに格納されて返されます。

以下ではstore-selectに検索条件を指定せずテーブル名のみを指定しているので全件検索になります。(ただし、読込件数はデフォルト値の件数で制限されます。)

kaleidox> store-select 'person
Table[3x1]

読み込んだTableの詳細情報を表示すると以下になります。

kaleidox> :show
Table[3x1]
┏━━━┯━━━━┯━━━━━━━━┓
┃id │name│city    ┃
┣━━━┿━━━━┿━━━━━━━━┫
┃100│Taro│Yokohama┃
┗━━━┷━━━━┷━━━━━━━━┛

Tableはオブジェクトなのでプロパティまたはメソッドを使って必要な情報を取り出すことができます。

ここではheadメソッドを使用して先頭の要素を取り出しています。この先頭の要素は本記事のテーマであるRecordとして取り出されます。

kaleidox> .head
id:100 name:Taro city:Yokohama
kaleidox> :show
Record[2] name:Taro city:Yokohama
SQL

データストア機能を使わず直接SQLを使用してデータベース内のデータをTableとRecordとして取得することもできます。

sql関数でSQL文を指定するとSQLが発行されます。SELECT文の場合は結果がTableとして返ってきます。

前述したとおり、Tableオブジェクトのheadメソッドを呼び出すと先頭の行がRecordとして返ってきます。

kaleidox> sql "select * from person"
Table[3x1]
kaleidox> :show
Table[3x1]
┏━━━┯━━━━┯━━━━━━━━┓
┃ID │NAME│CITY    ┃
┣━━━┿━━━━┿━━━━━━━━┫
┃100│Taro│Yokohama┃
┗━━━┷━━━━┷━━━━━━━━┛
kaleidox> .head
ID:100 NAME:Taro CITY:Yokohama

出力

データストア機能を使ってRecordをデータベースに格納することができます。

以下ではstore-insert関数を使って、personテーブルにRecordを追加しています。

kaleidox> store-insert 'person id:101,name:Hanako,city:Kawasaki
(101)
kaleidox> store-get 'person 101
ID:101 NAME:Hanako CITY:Kawasaki
kaleidox> store-select 'person
Table[3x2]
kaleidox> :show
Table[3x2]
┏━━━┯━━━━━━┯━━━━━━━━┓
┃id │name  │city    ┃
┣━━━┿━━━━━━┿━━━━━━━━┫
┃100│Taro  │Yokohama┃
┃101│Hanako│Kawasaki┃
┗━━━┷━━━━━━┷━━━━━━━━┛

参照

Recordの内容はプロパティとして参照することができます。

以下のように変数にpにRecordを束縛します。

kaleidox> setq p id:100,name:Taro,city:Yokohama
id:100 name:Taro city:Yokohama
プロパティ取得

プロパティ取得は以下のようにオブジェクトのプロパティを参照することで行なえます。

kaleidox> p.name
"Taro"
XPath

XPathを使ってRecordからプロパティ取得することもできます。

kaleidox> /name p
"Taro"

まとめ

今回はKaleidoxの中軸オブジェクトであるRecordについて説明しました。

次回はもう一つの中軸オブジェクトであるTableについて説明する予定です。

諸元

  • Kaleidox : 0.1.7