グラフィックスプログラミング入門

ゲーム開発用のフレームワークやライブラリを使用せずJavaScriptのみでシンプルなシューティングゲームを作成することで、Canvas APIを利用したグラフィックの描画方法から、三角関数やベクトル、行列などグラフィックを扱ううえで必要となる数学の基礎とホーミングやエフェクトといったゲームでの応用までぎっちり詰め込んだ本。これだけの内容を350ページほどにまとめているのだが、解説を端折ってる感じはなく読みやすい。流石に最後のゲームとは離れたピクセル単位での画像処理について書かれた章は突然だしボリューム的にも物足りない感じだけど、まぁボーナス・トラックみたいなものと思えばお得ともいえる。

普段、ゲーム(と呼べるほどのものではないが)を開発するときはC#でDXライブラリを利用しているのだが、書かれている内容のほとんどはDXライブラリにも応用の利く内容なのもありがたい。

文系プログラマーのためのPythonで学び直す高校数学

コンピューターでの数値の取り扱い(2進数とか浮動小数点数の説明)から始まり、方程式、ベクトル、行列、統計、微積分と高校数学で習う様々なトピックについてカタログ的に列挙して解説していくというスタイルの本。各題材についてそれぞれPythonによるサンプルコードがついてくる。扱っている範囲が広いこともあって解説はかなり簡潔だがボンクラな文系プログラマにも判るような平易さで書かれている。

1から体系的に学び直すという感じの本ではないが、コードを書いてて数学的な疑問が出てきたときにパラパラめくって確認するにはちょうど良い感じ。

「超」入門 微分積分

微分については遥か昔のかすかな記憶として学校で習った覚えがあるが、積分に関しては習った記憶がない。一応、高校は出ているが頭の悪い学校で3年間の授業で積分にまで辿りつくことができなかった。というわけで積分を知らずに(せいぜい微分の反対でしょ?でも反対ってどういう意味?ぐらいの感じで)のうのうと生きてきた訳だが、いい加減、言葉の意味ぐらいは理解しておいた方が良いなと思い読んでみた。

一般的な数学の本というと練習問題がついてきて、ここで実際に計算してみましょう。的な展開になることが多いが、この本は読者に計算をさせず、計算の仕方ではなく微分とは何か、積分とは何かという概念を言葉と図版で具体的に判りやすく解説するという本。確かに大変判りやすく微分積分について具体的なイメージを持つことができたが、実際に自分の頭を使っての計算を伴っていないのですぐに忘れてしまいそうではある。とっかかりとしては大変良い感じで、本書の意図は十二分に満足しているが、ここから先の継続が重要であり難しい。

「超」入門 微分積分 (ブルーバックス)

「超」入門 微分積分 (ブルーバックス)

e-Tax で専従者の年末調整

去年から家人を専従者給与の対象にしたので、年もあけて期日も近づいてきたので専従者に対する源泉徴収に対する年末調整をこの連休にすることにした。

年末調整では源泉徴収簿での年末調整額の計算、その計算結果をもとに「給与所得の源泉徴収票等の法定調書」の作成と税務署への提出、「給与支払い報告書(個人別明細・総括表)」の作成と役所への提出を、あと、年末調整で出た不足分(個人事業主の専従者給与の場合、控除対象となるものがあまりないので大抵の場合、不足分が発生するはず)の支払いを行うことになる。

このうち、役所に提出する「給与支払い報告書(個人別明細・総括表)」に関しては所定用紙に手書きで記入して提出する必要があるが、税務署に提出する「給与所得の源泉徴収票等の法定調書」に関してはe-Taxを利用してネット上で書類の作成から提出まで行うことができる。ちょうど良い機会なので「給与所得の源泉徴収票等の法定調書」に関しては、e-Taxで提出することにした。

ICカードリーダー

「ID・パスワード方式」というのを利用すれば事前に登録したIDとパスワードを利用してe-Taxを利用できるらしいが、確定申告以外の手続きに対応しているのか良く判らなかったのと、昨年、マイナンバーカードを作成していたので、今回はマイナンバーカードを利用することに。

マイナンバーカードで e-Tax を利用するのは、マイナンバーカードから証明書情報を読み出すためにICカードリーダーが必要なので、NTT Comの 非接触型のカードリーダーである ACR1251CLを amazon で購入。説明書にはWindowsの場合、USBで接続すれば自動でドライバのインストールが始まると記述されているが、実際には自分でNTT Comのサイトからドライバをダウンロードしてインストールする必要があった。ドライバさえインストールすれば特に問題なく利用できている。

NTTコミュニケーションズ ICカードリーダライタ ACR1251CL-NTTCom

NTTコミュニケーションズ ICカードリーダライタ ACR1251CL-NTTCom

  • 発売日: 2016/03/01
  • メディア: エレクトロニクス

e-Tax ソフト

e-Tax を利用する場合、クライアントで実行するアプリケーション版と、ブラウザで実行するWEB版がある。WEB版の方が機能的には少ないのだが、マインバーカード方式を利用する場合はWEB版の方が良い。アプリケーション版はドキュメントに従ってインストールしただけだと、マインバーカード方式では利用できないのだが、どうすればマイナンバーカード方式で利用できるようになるのかさっぱりわからない。WEB版の場合も、単純に所定のサイトにアクセスすれば良いという訳ではなく、プラグインや証明書のインストールなどいろいろ面倒な事前準備が必要なのだが、とりあえず判りにくいドキュメントに従って作業すれば、事前登録なしでマインバーカード方式によるログインができた。ただ、WEB版もWindowsの場合、IE11とEdge のみの対応で Chrome では利用できず、Edge もe-Tax用のプラグインをインストールしたにも関わらずウチの環境だとカードリーダーの読込が巧く動かなかったためIE11での作業となった。おそらくChromeベースになる時期Edgeでも動作しないだろうし、令和元年度の確定申告はIE11でやることになりそう。ここら辺、なんとしかしてほしい。

ソフトが利用できるようになれば、後は所定の書類(今回は「給与所得の源泉徴収票等の法定調書」)を選んで画面の指示に従って入力していくだけで書類の作成から提出まで特に難しいところはない。ただ、環境の問題か作成途中のデータの保存が巧く動作しなかった。

アプリ版にしてもそうだけど、ソフトの出来はお世辞にも良くない。いかにもNTTとか富士通とか大手SIerが受注して、丸投げされた孫請けあたりが作りましたという感じ。e-Tax普及のために優遇措置とかいろいろやってるけど、このままじゃ個人事業種や中小企業など専門部署を持てないような規模のトコには導入難しいと思う。

www.e-tax.nta.go.jp

高校数学でわかるディープラーニングのしくみ

年末から読んでいたベレ出版、涌井貞美著「高校数学でわかるディープラーニングのしくみ」を年を跨いでようやく読了。

最終章である誤差逆伝播法の解説の章の見出しに「高校では習わない偏微分を多用します」と書かれているのはご愛嬌だが、基本的なニューラルネットワークを利用した教師あり学習については、それこそ数学などもはや忘却の彼方になってしまった自分のような人間にも判るよう、高校数学の知識さえいらないレベルで判りやすく解説してくれている。ただ、EXCELの実践部分が紙幅の都合だろうか、シートのスクリーンショットを並べただけみたいな感じで終わっていて非常に判りづらい。サンプルをダウンロードすれば良いんだろうが、それにしてももうちょっと何とかならなかったのかと思う。図を多用した解説が悪くない分余計に残念に思う。

しかし、この本に限ったことではないが、機械学習関連の本は勾配降下法が出てくるあたりで急激に難しくなって毎回躓いてしまう。著者がいくら判りやすく解説しようにもやはり限度はあるので、読み手にもそれ相応の数学的知識は必要なのは仕方がないが、この年齢になって数学を勉強しなおすのは気力はあってもなかなか脳髄がついていかないので困る。

高校数学でわかるディープラーニングのしくみ

高校数学でわかるディープラーニングのしくみ

  • 作者:涌井 貞美
  • 出版社/メーカー: ベレ出版
  • 発売日: 2019/12/11
  • メディア: 単行本

Rust入門(6) 簡単なツールを作ってみる

Rust 入門の続き。

仕事でテキストデータをRDBにインポートする必要があったので、Rustの勉強がてらテキストファイルを読み込んで、SQLのINSERT文を出力するプログラムを書いてみることにした。

テキストデータは単純なTSV形式のファイルで改行コードはLF。
対象となるテキストデータは複数存在するので、インポート先のテーブルの情報は pg_dump コマンドで出力したもので以下のような形式。今回対象となるシステムでは数値型と文字列型しかデータ型は利用していないので、インポートするさいにクォートが必要なのは文字列型のみとなる。

CREATE TABLE m_master (
    category integer NOT NULL,
    id smallint NOT NULL,
    name character varying(100),
    order_no smallint DEFAULT 0,
    del_flg smallint DEFAULT 0,
    in_time character varying(12) DEFAULT to_char(now(), 'YYYYMMDDHH24MI'::text),
    in_staff integer,
    up_time character varying(12) DEFAULT to_char(now(), 'YYYYMMDDHH24MI'::text),
    up_staff integer
);

とりあえず、いろいろ試行錯誤しながらコーディングして、とりあえず動くとこまで持っていったのが次のコード。
CRERATE TABLESQLを読み込んで構造体を返す部分は他にも転用できそうな気がするのでモジュールとして作成した。

// table_def.rs

extern crate regex;
use regex::Regex;

#[derive(Debug)]
pub struct FieldDef {
    pub field_name: String,
    pub field_type: String,
    pub options: String
}

#[derive(Debug)]
pub struct TableDef {
    pub table_name: String,
    pub fields: Vec<FieldDef>
}

fn parse_fields(s: &str) -> Vec<FieldDef> {
    let mut vec:Vec<FieldDef> = Vec::new();
    for line in s.split("\n") {
        let re = Regex::new("(\\S+)\\s+(\\S+)(.*),").unwrap();
        if let Some(m) = re.captures(line) {
            vec.push(FieldDef {
                field_name: (&m[1]).to_string(),
                field_type: (&m[2]).to_string(),
                options: (&m[3]).to_string()
            });
        }
    }
    vec
}

impl TableDef {
    pub fn parse(sql: &str) -> Result<TableDef, &str> {
        let re = Regex::new("(?s)CREATE\\s+TABLE\\s+(\\S+)\\s+\\(\\s*(.+)\\s*\\);").unwrap();
        match re.captures(sql) {
            Some(m) => Ok(TableDef { 
                table_name: (&m[1]).to_string(),
                fields: parse_fields(&m[2])
            }),
            None => Err("Invalid Data")
        }
    }
}

SQLをパースする部分は定形なので正規表現で抜き出している。文字列には改行コードを含むため(?s)を指定して改行コードをく白文字にマッチするようにしてる。フィールド定義については一度まるごと抜き出したのを改行コードで分割したあと、さらに正規表現でフィールド名とデータ型などを抜き出すようにしている。ここら辺、正規表現でもっと頑張れば簡潔に書けそうな気がする。フィールド名以降の属性のパースについては、文字列型かそれ以外の判定さえできれば問題ないので非常に杜撰なものとなってる。

で、メインの処理はこんな感じに。

// main.rs

use std::env;
use std::fs;
use std::io::BufReader;
use std::io::BufRead;
use std::io::BufWriter;
use std::io::stdout;
use std::io::Write;
use std::result::Result;
mod table_def;
use table_def::TableDef;

fn load_table_def(s: &str) -> Result<TableDef, &str> {
    if let Ok(content) = fs::read_to_string(s) {
        if let Ok(t) = TableDef::parse(&content) {
            return Ok(t);
        }    
    }
    Err("Read Error")
}

fn output(table_def: &TableDef, data_path: &str) {
    if let Ok(file) = std::fs::File::open(data_path) {
        let mut fb = BufReader::new(file);
        let mut buff = String::new();
        let out = stdout();
        let mut out = BufWriter::new(out.lock());

        let mut fields = table_def.fields.iter().map(|f| &f.field_name).fold(String::new(), |s, n| s + n + ",");
        fields.pop();
        writeln!(out, "INSERT INTO {} ({}) VALUES ", &table_def.table_name, &fields).unwrap();
        let mut sw = false;
        loop {
            match fb.read_line(&mut buff) {
                Ok(n) => {
                    if n > 0 {
                        buff.pop();
                        if sw {
                            write!(out, ",\n(").unwrap();
                        } else {
                            write!(out, "(").unwrap();
                            sw = true;
                        }
                        for (i, v) in (&buff).split("\t").enumerate() {                            
                            let value = if table_def.fields.len() > i && table_def.fields[i].field_type == "character" {
                                format!("'{}'", v)
                            } else {
                                if v != "" { v.to_string() } else { "NULL".to_string() }
                            };
                            if i == 0 {
                                write!(out, "{}", value).unwrap();
                            } else {
                                write!(out, ",{}", value).unwrap();
                            }
                        }
                        write!(out, ")").unwrap();
                        buff.clear();
                    } else {
                        break;
                    }
                },
                _ => break
            }
        }
        writeln!(out, ";").unwrap();
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let table_def = load_table_def(&args[1]);
    match table_def {
        Ok(t) => {
            output(&t, &args[2]);
        },
        Err(err) => {
            println!("{:?}", err);
        }
    }
}

一番、苦労したのは項目定義の構造体のVecから項目名を取り出して連結した一つの文字列を取得するとこで、いろいろ試行錯誤た結果、次のような感じになった。

let mut fields = table_def.fields.iter().map(|f| &f.field_name).fold(String::new(), |s, n| s + n + ",");
fields.pop();

配列であれば connect() というメソッドがあって&strの連結ができるようだが、Vec<String> を簡単に結合するメソッドのようなものはなさそう。仕方がないので、map() で構造体からフィールド名を保持する String の参照を抜き出して、fold()Stringに連結するようにした。末尾に余計な","が付くので最後に fields.pop() で取り除いている。

配列、スライス、Vecの違い、iter()into_iter()の違い、&strStringの違いなど、いろいろまだキチンと理解していないのでなかなかコンパイルできるコードにたどり着けず苦労した。もっとエレガントというか、良くあるコードなのでイディオムが存在してそうな気がする。

あと、これはいまだに訳が判っていないトコがあって、テキストファイルから読み込んだテーブル定義のStringをパース処理するメソッドに喰わせた結果の戻り地の処理で、以下のような記述でコンパイルが通るようなったのだが、

fn load_table_def(s: &str) -> Result<TableDef, &str> {
    if let Ok(content) = fs::read_to_string(s) {
        if let Ok(t) = TableDef::parse(&content) {
            return Ok(t);
        }    
    }
    Err("Read Error")
}

なぜ、↓だとコンパイルが通らないのかが判らない。

fn load_table_def(s: &str) -> Result<TableDef, &str> {
    if let Ok(content) = fs::read_to_string(s) {
        TableDef::parse(&content)
    } else {
      Err("Read Error")
    }
}
returns a value referencing data owned by the current functionrustc(E0515)

というエラーが発生するのだが、なぜ戻り値をそのまま返すのが駄目で、一度、Resutから値を取り出して再びResultで包むとエラーにならないのか、その理屈が判らない。

うーん、Rust の道はなかなかに険しい。

SwiftUI徹底入門

SB Creative 金田浩明「SwiftUI徹底入門」を読了。

今年のWWDCで発表された、Apple の宣言型構文を採用した新しいUIフレームワークの入門書。SwiftUIに特化した入門書なので、XCodeやSwiftの解説はSwiftUIを利用するうえで必要になるプロパティラッパーやファンクションビルダーなどの新機能に限られており、ある程度、iOSの開発に関する基礎知識を有しているのが前提となっている。余計な記述がない分、SwiftUIについて学ぶには良い本だと思った。ぱっと見、SwiftUIのコードはSwiftの文法的になんじゃこりゃ?てな感じのコードになるので、そこらへんの仕組みをキチンと説明してくれているのがありがたい。そこらへんのSwiftの新機能は「詳解Swift第5版」では読み飛ばしてたとこなんで、ざっくりとでも概要を把握することができた。

また、UIKitとの連携だけでなく、CoreDataとの連携まで解説してくれてるのもポイント高い。個人的にはSwiftUIは、iOSだけなくWatchOSやMacOSにも対応している筈なので、そこら辺についても少し言及が欲しかったなと思うけど、入門書に対してそれは高望みすぎか。

SwiftUI 徹底入門

SwiftUI 徹底入門