「超」入門 微分積分
微分については遥か昔のかすかな記憶として学校で習った覚えがあるが、積分に関しては習った記憶がない。一応、高校は出ているが頭の悪い学校で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
- 発売日: 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普及のために優遇措置とかいろいろやってるけど、このままじゃ個人事業種や中小企業など専門部署を持てないような規模のトコには導入難しいと思う。
高校数学でわかるディープラーニングのしくみ
年末から読んでいたベレ出版、涌井貞美著「高校数学でわかるディープラーニングのしくみ」を年を跨いでようやく読了。
最終章である誤差逆伝播法の解説の章の見出しに「高校では習わない偏微分を多用します」と書かれているのはご愛嬌だが、基本的なニューラルネットワークを利用した教師あり学習については、それこそ数学などもはや忘却の彼方になってしまった自分のような人間にも判るよう、高校数学の知識さえいらないレベルで判りやすく解説してくれている。ただ、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 TABLE
のSQLを読み込んで構造体を返す部分は他にも転用できそうな気がするのでモジュールとして作成した。
// 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()
の違い、&str
とString
の違いなど、いろいろまだキチンと理解していないのでなかなかコンパイルできるコードにたどり着けず苦労した。もっとエレガントというか、良くあるコードなのでイディオムが存在してそうな気がする。
あと、これはいまだに訳が判っていないトコがあって、テキストファイルから読み込んだテーブル定義の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にも対応している筈なので、そこら辺についても少し言及が欲しかったなと思うけど、入門書に対してそれは高望みすぎか。
cocopar モバイルディスプレイ 15.6インチ
C#でWIndows用のデスクトップアプリケーションの面倒をみている分にはそれほど不便さを感じることはなかったけど、来年から本格的にPHPで書かれたサーバー側のプログラムも面倒みることになって、 使っているDELL XPS13のモニターだけだとつらいな(WEB系の開発だとやっぱりブラウザとエディタは並べて開発したい)と思い、モバイルディスプレイを買うことにした。
Amazon でモバイルディスプレイを検索すると、見たことも聞いたこともないようなメーカーの安価な何が違うのか良く判らない同じような製品が出てくる。違いといえば多少の値段と入力ポートの種類と数ぐらいで、特に決めてとなるような有意な差は感じられない。 とりあえず、XPS13の外部モニター出力がHDMIではなくUSB Type-Cなのでそれに対応していること、13inchは老眼につらいので15inchクラスのFullHDという感じで絞り込んで適当にcocoparとかいう知らないメーカーの製品に決めた。
注文した翌日には製品が届いたが、安物のインクジェットプリンターで印刷したようなぼやけたアサシンクリード風のイラストが描かれた箱に一抹の不安を感じたけど、製品そのものは特に問題ない。取り扱い説明書の類は何一つ添付されていないがモニター調整のUIはキチンと日本語化されているので困ることはない。接続もXPS13からUSB Type-Cのケーブル一本で電源も供給してくれるのでお手軽。故障時のサポートなど不安ではあるが、値段が値段だから壊れたら買い替えれば良いぐらいの気持ちで使う分には良い買い物だったといえる。
あと、ついでに今まで使っていたロジクールの無線マウスがUSBポートを一つ占有して不便だったんで、マイクロソフト製のBluetooth接続のマウスに買い替えた。特にこれっといった特徴のないマウスだが薄いのとシンプルなデザインが良い感じだ。
マイクロソフト マウス Blutooth/モダン モバイル マウス KTF-00007
- 発売日: 2019/01/25
- メディア: Personal Computers
M5Stack でシリアル通信
日曜電子工作の続き。
M5StackとMacとの間でシリアル通信を試してみる。
M5Stackの開発ではホストPCとUSBケーブルで接続してシリアル通信をプログラムの転送を行うので、その環境をそのまま利用する。
M5Stack側
M5Stack側のプログラムは前回写経した内蔵加速度センサのサンプルを少し改造して取得したデータをシリアルポートに出力するようにする。
#define M5STACK_MPU6886 #include <M5Stack.h> #include "utility/MPU6886.h" #define MULTISAMPLE 20 void readAccelMulti(float *ax, float *ay, float *az, int multi) { float x, y, z; float _x, _y, _z; x = y = z = 0; for (int i = 0; i < multi; i++) { M5.IMU.getAccelData(&_x, &_y, &_z); x += _x; y += _y; z += _z; } *ax = x / multi; *ay = y / multi; *az = z / multi; } void drawGrid() { M5.Lcd.drawLine(41, 120, 279, 120, CYAN); M5.Lcd.drawLine(160, 1, 160, 239, CYAN); M5.Lcd.drawCircle(160, 120, 119, CYAN); M5.Lcd.drawCircle(160, 120, 60, CYAN); } int oldX = 0; int oldY = 0; void drawSpot(int ax, int ay) { int x, y; x = map(constrain(ax, -300, 300), -300, 300, 40, 280); y = map(constrain(ay, -300, 300), -300, 300, 240, 0); M5.Lcd.fillCircle(oldX, oldY, 7, BLACK); drawGrid(); M5.Lcd.fillCircle(x, y, 7, WHITE); oldX = x; oldY = y; } float offsetX, offsetY, offsetZ; #define MOVINGAVG 10 float movingavgx[MOVINGAVG], movingavgy[MOVINGAVG]; int _index = 0; void setup() { M5.begin(); // INIT M5Stack M5.Power.begin(); // M5.IMU.Init(); // Init MPU6886 readAccelMulti(&offsetX, &offsetY, &offsetZ, MULTISAMPLE); for (int i = 0; i < MOVINGAVG; i++) { movingavgx[i] = offsetX; movingavgy[i] = offsetY; } } int serialStart = 0; void loop() { float x, y, z; float ax, ay; M5.update(); readAccelMulti(&x, &y, &z, MULTISAMPLE); movingavgx[_index] = x; movingavgy[_index] = y; _index = (_index + 1) % MOVINGAVG; ay = ax = 0; for (int i = 0; i < MOVINGAVG; i++) { ax += movingavgx[i]; ay += movingavgy[i]; } ax = ax / MOVINGAVG; ay = ay / MOVINGAVG; if (M5.BtnA.wasPressed()) { offsetX = ax; offsetY = ay; } if (M5.BtnC.wasPressed()) { serialStart = (serialStart == 0) ? 1 : 0; } if (serialStart == 1) { Serial.printf("{x:%f, y:%f}¥n", ax- offsetX, ay - offsetY); } drawSpot((int)((ax - offsetX) * 1000), (int)((ay - offsetY) * 1000)); delay(100); }
シリアルポートのオープンは M5.update()
の中で既に行われているのでデフォルトの通信速度(115,200bps)で問題なければ何もする必要はないみたい。
シリアルポートへの読み書きは、Serial オブジェクトのメソッドを利用して行う。ここらへんは普通に Arduino 互換な感じでいける。リファレンスにはprintf()
は存在しないがなんか使えるっぽい。
Mac側
親機であるMac用のプログラムはPythonで書いてみた。といってもたったの5行。
import serial ser = serial.Serial("/dev/cu.SLAB_USBtoUART", 115200, timeout=0.1) while True: line = ser.readline() print(line)
シリアル通信にはpyserialを使用するので、事前にpipを利用してインストールしておく。
通信ポートは開発環境をセットアップするときにインストールしたUSBのシリアルドライバによって生成されたデバイスを指定する。
$ pip3 install pyserial
実行してみる
M5StackとMacをUSBケーブルで接続、プログラムを転送してM5Stackの電源を入れてから、Mac側の受信プログラムを実行する。
$ python3 serial_test.py b'{x:-0.019182, y:-0.096029}\xc2\xa5n' b'{x:-0.003403, y:-0.126227}\xc2\xa5n' b'{x:-0.031735, y:-0.192700}\xc2\xa5n' b'{x:-0.045695, y:-0.215953}\xc2\xa5n' b'{x:-0.048987, y:-0.247052}\xc2\xa5n' b'{x:-0.049235, y:-0.274598}\xc2\xa5n' b'{x:-0.048007, y:-0.308534}\xc2\xa5n'
とりあえずデータの受信はできているようだけど、前後に謎のデータが付与されている。 これらはなんじゃろかい?