Linux コマンドの最長しりとりを求める
「 Linux コマンドでしりとりをすると、最長でいくつ繋がるのか?」
この疑問を解消すべく、最長しりとりを求めるプログラムを実装しました。
結論
- 論文「最長しりとり問題の解法」に沿って実装
- CentOS 8 Docker イメージでは 326 コマンドが最長
- ソースコードは GitHub: typewriter/any-shiritori に公開
最長しりとり問題
論文「最長しりとり問題の解法」
全パターン網羅して最長を決める… といきたいところですが、単語数が増えてくると数時間掛けても解けません。
調べてみると、なんと論文になっています。整数計画問題として定式化した上で、分枝限定法により解を得るものです。さらには、既に実装済みのWebサービスやソースコードまであります。
しかし、 Crystal を触りたい、という別の目的から車輪の再発明をすることにしました。
Crystal 実装
ソースコード
論文を読み解きつつ再発明した実装は、 GitHub に公開しています(ソルバーに GLPK を使用しています)。やみくもに木で探索するものも入っています。
Crystal 言語
Crystal は、 Ruby に非常に似た文法を持つ、型推論の付いた静的型付け言語です。
private def self.find_closed_path(v : Char, x : Hash(String, Int32), k : Int32) edges = x.select { |k, v| v > 0 }.map { |e| e[0] } find_closed_path_recursively(v.to_s, edges, k) end
型の指定方法、キャスト、プロパティの指定方法などは公式ドキュメントを参照しましたが、それ以外は Ruby のつもりで書けました。それくらい似ています。
ただし、 gem は使えません( C バインディングは使えます)。「文法が似ている全く別の言語」であることを思い出させてくれます。
結果
各種 Docker イメージの PATH
に含まれているコマンドを抽出し、最長しりとりを求めました(いずれも数秒程度で解き終わりました)。
Docker image:tag | コマンド数 | しりとり長 | 使用率 |
---|---|---|---|
alpine:3.10.3 | 317 | 173 | 54.6% |
amazonlinux:2.0.20191016.0 | 261 | 146 | 55.9% |
centos:8 | 573 | 326 | 56.9% |
debian:10.2 | 409 | 231 | 56.5% |
なんと CentOS 8 では 300 以上のコマンドが繋がります。 Amazon Linux が Alpine Linux よりもコマンド数が少ないなど、しりとりとは無関係に興味深い点もあります。
以下に、しりとりの内容を記載します。 w または z で始まり、数字で終わっている点はどのディストリビューションも共通です。
Alpine Linux 3.10.3
Length: 173 Shiritori: whoami => ipcs => strings => setlogcons => setkeycodes => swapoff => fgrep => powertop => pmap => pgrep => pwd => depmod => dd => dc => crontab => brctl => lzma => add-shell => login => nologin => nsenter => rmdir => rm => microcom => md5sum => mkdir => reset => timeout => test => ttysize => ether-wake => env => volname => eject => touch => halt => tr => realpath => hdparm => modinfo => openvt => truncate => expr => raidautorun => nl => logger => rfkill => lzopcat => tunctl => lzcat => tail => ln => nmeter => remove-shell => lsusb => bc => chown => nproc => comm => mpstat => tac => crond => dnsdomainname => expand => dirname => ed => deluser => rmmod => date => echo => od => deallocvt => tar => readahead => dumpkmap => passwd => delgroup => ping => gzip => patch => hexdump => pkill => lzop => printenv => vconfig => gunzip => pwdx => xzcat => top => pscan => nc => cksum => mkpasswd => du => unlzma => arping => getopt => true => egrep => printf => fsync => cal => lsof => fdflush => hostid => diff => fbsplash => head => df => fstrim => mesg => getconf => fsck => killall => losetup => poweroff => flock => klogd => dmesg => grep => pidof => findfs => sysctl => ls => setserial => less => sum => mkmntdirs => sha512sum => mkdosfs => su => unix2dos => sync => clear => run-parts => slattach => hd => dumpleases => sendmail => ldconfig => groups => sha3sum => mktemp => ps => sha256sum => mkswap => pipe_progress => sha1sum => mknod => dos2unix => xargs => shuf => fuser => rev => vi => ifconfig => getent => tty => yes => scanelf => fatattr => readlink => kill => lspci => ipcalc => cryptpw => whois => swapon => nameif => factor => rdev => vlock => killall5
Amazon Linux 2.0.20191016.0
Length: 146 Shiritori: zic => curl => ln => nohup => pwd => dd => db_load => diff => fg => gpg => gpg-error => rmdir => rpm => md5sum => mkdir => rpmkeys => sotruss => stat => tzselect => tsort => tset => trust => tput => timeout => test => tabs => split => tty => yes => signver => reset => tr => rpmdb => bg => gsettings => sum => makedb => bashbug => groups => stdbuf => false => ex => xmlcatalog => glib-compile-schemas => sprof => factor => rview => whoami => iconvconfig => getpcaps => shuf => fold => df => find => db_printlog => gdbm_load => dir => read => db_tuner => realpath => hostid => db_recover => rm => mknod => db_stat => truncate => expand => db_checkpoint => touch => head => dircolors => sha512sum => mkfifo => oldfind => db_hotbackup => pldd => db_dump => paste => egrep => printf => fgrep => pinentry-curses => ssltap => pr => rvi => infotocap => printenv => vi => infocmp => pinky => yum => mktemp => ptx => xargs => sleep => python => nice => expr => runcon => numfmt => true => echo => od => du => users => sha384sum => mv => vdir => rpcgen => nl => ls => sha256sum => modutil => lua => applygnupgdefaults => setcap => pk12util => ldconfig => getopts => sdiff => fmt => tail => luac => chown => nproc => cp => pydoc => cut => tic => chkconfig => gdbus => sync => crlutil => localedef => fc => cmp => p11-kit => tac => cmsutil => logname => env => view => wc => certutil => ldd => diff3
CentOS 8
Length: 326 Shiritori: zic => cracklib-unpacker => runuser => rtpr => rmdir => rmmod => dmfilemapd => depmod => dd => dbus-send => db_load => dnf => fg => gpg => groupdel => loginctl => localectl => lastb => busctl => logname => egrep => pmap => pgrep => ps => systemd-tmpfiles => systemd-sysusers => systemd-cgls => strings => ss => sotruss => systemd-delta => as => sum => md5sum => mksquashfs => sha512sum => mkfs.cramfs => sha384sum => mkfs => systemd-run => nologin => newusers => su => users => systemd-machine-id-setup => prlimit => tzselect => tsort => trust => timeout => test => telinit => taskset => tracepath => hash => halt => top => pinky => yum => mktemp => pwunconv => vi => ipcs => swapon => nohup => pwconv => vipw => w => wipefs => sulogin => newuidmap => pwscore => elfedit => type => eject => truncate => echo => objcopy => ypdomainname => evmctl => lesspipe.sh => hostnamectl => login => nl => losetup => pkill => lsns => systemctl => lslogins => sysctl => lslocks => swaplabel => ls => skill => less => setfacl => lnstat => timedatectl => last => tail => ldattach => hostname => ethtool => ldconfig => gpgparsemail => lastlog => gsettings => sg => groups => systemd-hwdb => bg => gapplication => namei => ifcfg => groupmems => systemd-cgtop => pkg-config => glib-compile-schemas => strip => ping => getpcaps => setpriv => vmcore-dmesg => getfacl => lsmem => makedb => bashbug => getopts => systemd-path => hexdump => pwdx => xmlcatalog => gprof => fips-finish-install => localedef => fips-mode-setup => printf => fgrep => poweroff => fsck.cramfs => swapoff => findfs => stdbuf => fstrim => mesg => gpgconf => fsck.minix => xzless => sprof => fold => df => find => dbus-test-tool => lsmod => dwp => pwd => dmsetup => pldd => dracut => tload => dmesg => genl => lsinitrd => db_printlog => gdbmtool => ldd => dbus-uuidgen => nisdomainname => expand => dbus-run-session => nm => mknod => dirmngr-client => touch => hostid => db_hotbackup => pkgconf => fsck => kmod => du => update-crypto-policies => sha256sum => mkinitrd => db_dump => pidof => flock => kill => ld.gold => dhclient-script => tty => yes => sha224sum => mkdumprd => dhclient => true => ex => x86_64-redhat-linux-gnu-pkg-config => gdbus => sha1sum => modinfo => od => dirmngr => read => dir => routef => fix-info-dir => resolvconf => factor => runlevel => logger => rpm => mkhomedir_helper => rm => mkdir => runcon => nsenter => rpmdb => blkid => dbus-monitor => ranlib => blkdiscard => db_tuner => realpath => head => db_recover => rtmon => nice => expr => rview => watchgnupg => gtar => rpmkeys => sleep => pwhistory_helper => routel => ld.bfd => delpart => tr => rfkill => ld => dbus-update-activation-environment => tar => raw => whoami => iconvconfig => gpg-error => rdma => applygnupgdefaults => shutdown => newgrp => printenv => vigr => readelf => fdisk => kernel-install => ln => newgidmap => pivot_root => tee => env => vdir => rdisc => coreutils => sync => command => dbus-daemon => nproc => chpasswd => dmstats => sha512hmac => chmod => dircolors => sha384hmac => chgpasswd => dbus-cleanup-sockets => sha256hmac => cd => db_stat => tipc => chkconfig => getconf => fc => capsh => hwclock => kexec => comm => mountpoint => tac => colrm => mkfs.minix => xzdec => cracklib-packer => rvi => ipcalc => chcpu => update-alternatives => sha224hmac => cksum => mv => view => wc => cp => pr => resolvectl => lsipc => chgrp => ptx => xargs => sha1hmac => chmem => mkswap => partx => xz => zless => shuf => faillock => kdumpctl => lscpu => unxz => znew => whereis => setterm => mkfifo => objdump => p11-kit => tracepath6
Debian 10.2
Length: 231 Shiritori: znew => wc => ctrlaltdel => ldconfig => groupmod => dd => diff => fgrep => paste => e2image => expr => runuser => rmt-tar => rmdir => rm => md5sum => mkfs.cramfs => ss => swapon => nologin => newusers => switch_root => tzselect => tsort => tset => tput => timeout => test => taskset => tarcat => tune2fs => stat => tabs => su => users => split => tty => yes => sum => mkfs.bfs => sha512sum => mkfs => sha384sum => mke2fs => sha256sum => md5sum.textutils => sha224sum => mkhomedir_helper => runcon => nsenter => rtstat => tr => run-parts => sha1sum => mkdir => resize2fs => sort => tar => rtcwake => e4crypt => truncate => expiry => ypdomainname => e2mmpstatus => script => true => env => vigr => renice => egrep => pr => rgrep => pager => rtmon => nohup => ptx => xargs => sulogin => newgrp => printf => factor => routef => fsck.cramfs => swapoff => findfs => stdbuf => fold => df => find => dash => hostid => delgroup => pwd => dumpe2fs => shred => dircolors => setsid => debugfs => sed => du => usermod => dpkg-deb => b2sum => mknod => debconf => fstrim => mklost+found => dpkg-trigger => readprofile => expand => dpkg-maintscript-helper => realpath => head => debconf-set-selections => sleep => policy-rc.d => debconf-apt-progress => setcap => pldd => dir => rbash => hostname => echo => od => dpkg => groupadd => dmesg => getconf => filefrag => gzip => ping => groups => shadowconfig => groupmems => sg => getpcaps => savelog => getopt => tzconfig => gpasswd => debconf-copydb => bashbug => getent => toe => e4defrag => gunzip => pidof => faillog => groupdel => lastlog => genl => login => nl => losetup => perl => lsattr => routel => logger => rename.ul => lsns => swaplabel => lscpu => userdel => ldd => deluser => remove-shell => localedef => fmt => tail => ln => namei => installkernel => lslogins => shuf => findmnt => tempfile => e2label => lslocks => setterm => mesg => grep => passwd => deb-systemd-helper => rdma => add-shell => lsipc => chown => nproc => comm => mountpoint => tipc => cksum => mount => tic => choom => mktemp => prlimit => tc => chmem => mkswap => pivot_root => tac => cppw => wdctl => lsmem => mv => vipw => wipefs => sync => chcpu => useradd => debconf-show => wall => ls => sdiff => fdformat => tee => e2freefrag => getcap => pwunconv => vdir => raw => whoami => install => lastb => badblocks => start-stop-daemon => numfmt => touch => hwclock => killall5
ふたたび結論
- 論文「最長しりとり問題の解法」に沿って実装(車輪の再発明)
- CentOS 8 Docker イメージでは 326 コマンドが最長
- ソースコードは GitHub: typewriter/any-shiritori に公開
紅葉スポットと紅葉状況をツイートから推定する
ひとり開発 Advent Calendar 2019 10日目の記事です。
成果物: ソーシャル紅葉見頃情報
Webサービス「ソーシャル紅葉見頃情報」を作りました。
動機: 紅葉情報サイトの「見頃」表示が信用できない
以下の写真は、複数の紅葉情報サイトで「紅葉状況:見頃」だった時の御岳昇仙峡(山梨県)の様子です。
正直、見頃はまだ先だと感じました。Twitterを調べても同様の感想を持った方が多いようで、甲府市観光課も「全体的に3分〜4分付き」と表現されている状況でした。
🍁紅葉情報🍁
— 甲府市観光課(公式) (@kofu_kanko) 2019年11月11日
本日の昇仙峡の様子です!
少しずつ紅葉が進み、全体的に3分〜4分付きといったところです🍁✨
種類によっては、鮮やかに色付いていました😀
《甲府市観光課ホームページ》https://t.co/rGZotqD6oO pic.twitter.com/3dy2y3qLGO
せっかく紅葉を見に行くなら、見頃のピークに行きたいものです。
しかし、紅葉情報サイトの「見頃」は判定が緩いようで使えません。Twitterで流れているツイートのほうが、はるかに信用できそうです……… ということは、ツイートを活用すれば、より厳しく「見頃」を判断出来る可能性がありそうです。
作ることにしました。
解説: サービス構成
サービスの全体像は以下の通り。ぜんぶ書き慣れている Ruby で楽しく書きました。
それぞれ順を追って説明していきます。
ツイート収集
「紅葉」「見頃」「色づき」などのキーワードで、紅葉状況と思わしきツイートをひたすらに収集します。
紅葉スポットの自動抽出
紅葉スポットは、それこそ ウォーカープラスの紅葉情報やウェザーニュースの紅葉Ch. から集める方法もありそうですが、依存したくないので ツイートから紅葉スポット名も抽出 します。
形態素解析: MeCab + mecab-ipadic-NEologd
ツイートを形態素解析エンジン MeCab に掛けて 固有名詞 を拾います。辞書には、最新の固有表現が多数採録されている mecab-ipadic-NEologd を用います。例えば、福島県会津地方にある景勝地「塔のへつり」も正しく抽出できます。
# 標準の辞書 (mecab-ipadic) $ echo "福島県南会津郡下郷町、塔のへつり。" | mecab -d /usr/local/lib/mecab/dic/ipadic/ 福島 名詞,固有名詞,地域,一般,*,*,福島,フクシマ,フクシマ 県 名詞,接尾,地域,*,*,*,県,ケン,ケン 南会津 名詞,固有名詞,地域,一般,*,*,南会津,ミナミアイヅ,ミナミアイズ 郡 名詞,接尾,地域,*,*,*,郡,グン,グン 下郷 名詞,固有名詞,地域,一般,*,*,下郷,シモゴウ,シモゴー 町 名詞,接尾,地域,*,*,*,町,マチ,マチ 、 記号,読点,*,*,*,*,、,、,、 塔の 名詞,固有名詞,地域,一般,*,*,塔の,トウノ,トーノ へ 助詞,格助詞,一般,*,*,*,へ,ヘ,エ つり 名詞,一般,*,*,*,*,つり,ツリ,ツリ 。 記号,句点,*,*,*,*,。,。,。 EOS
# 今回使用する辞書 (mecab-ipadic-NEologd) $ echo "福島県南会津郡下郷町、塔のへつり。" | mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd/ 福島県 名詞,固有名詞,地域,一般,*,*,福島県,フクシマケン,フクシマケン 南会津 名詞,固有名詞,地域,一般,*,*,南会津,ミナミアイヅ,ミナミアイズ 郡 名詞,接尾,地域,*,*,*,郡,グン,グン 下郷町 名詞,固有名詞,地域,一般,*,*,下郷町,シモゴウマチ,シモゴーマチ 、 記号,読点,*,*,*,*,、,、,、 塔のへつり 名詞,固有名詞,一般,*,*,*,塔のへつり,トウノヘツリ,トーノエツリ 。 記号,句点,*,*,*,*,。,。,。 EOS
ここから固有名詞を拾って、「福島県」「南会津」「下郷町」「塔のへつり」が紅葉スポットの候補となります。
都道府県推定
紅葉スポットの候補に対し、都道府県を推定します。
方法はシンプルで、『京都の南禅寺』のように都道府県名が一緒になっているものを拾い上げ、その最頻値を推定結果としているだけです。ただし、「高尾山口」が「山口」になったり「東京都」が「京都」になったりしないよう、一部正規表現で避けています。
ノイズ除去
紅葉スポットの候補から、ノイズと思われる名称を除外します。
- 都道府県の推定が分散しすぎている場合
- 例えば「植物園」は全国にあり、特定のスポットを指していないので除外したい
- 半角文字を含むか、漢字が1文字も含まれない場合
- 「1日」「エモい」なども固有名詞として候補に挙がっているため除外したい
紅葉状況の推定
ここまでで紅葉スポットの候補が定まったため、紅葉状況を推定します。
スコア算出
ツイートに含まれる「紅葉状況っぽいキーワード」を抽出し、 0(青葉)〜1(見頃)〜2(落葉) の間で数値化します。
正直ここはかなりゴリ押しです。たとえば 「早かった」は0.4、「もう少し」は0.7、「ちょうど」は1、 のように勘で数値を設定しています。複数あれば平均を取ります。
Webサービス
これで紅葉スポット・紅葉状況の情報が揃ったため、Webサービスとして提供します。息を吸うようにフロントエンドを書ける人間ではないので、古き良き非SPAでの実装です。
非紅葉スポットフィルタ(雑)
ノイズ除去をしても紅葉スポット「でない」固有名詞が大量に残るため、ここでフィルタします。ひたすらキーワードでフィルタを掛ける一時しのぎで、改善の余地が残る部分です。
place !~ /^(.+観光客|外国人.+|最盛期|今日この頃|.+気温|何度|四季桜|可能性|寒い朝|世界遺産|誕生日|今シーズン|黄緑|[0-9]+日|皇室献上|.+観光|お久しぶり|開催中|全国的|敷地内|温暖化|五平餅|お勧め|青紅葉|.+に|.+科|.+流星群|.+の|.+旅行|暑さ|.+的|桜の木|桜を見る会|ロケ地|.+公開|女子力|仙山線|お題|お姉ちゃん|外国人|私たち|朝活|好きだ|平野部|数年|異常気象|トロッコ列車|知らんけど|山手線|予防接種|午前中|募集中|.週間|観光.+|冬桜|落葉高木|今月末|.+市内|目的地|質問箱|.+人|.+年前|リア充|.回|分からん|.+希望|オフ会|聖地巡礼|子どもたち|大嘗宮|代表者|落羽松|つけ麺|定点観測|入場料|競馬場|.年|.枚|.?週末|冷たい雨|娘|お客さん|時間帯|涙)$/
検証: 紅葉状況の推定結果
約3週間動かしたので、推定結果を見ておきます。と言っても、Twitterのツイート群からの推定結果とツイートを比べるので出来レースです。
高尾山
東京 高尾山の推定結果ですが、11/27頃から見頃に近く、12/7頃に見頃を過ぎようとしているようです。高尾ビジターセンターのTwitterアカウントと比べてみます。
11/16 見頃間近
天気:晴 気温:12.0℃ 富士山展望:〇
— 高尾ビジターセンター【公式】 (@takaovc) 2019年11月16日
ここ数日の冷え込みで、一気に紅葉が進みました。山頂はもう見頃間近です。
紅葉と言えば、カエデの仲間が有名ですね。3枚目の写真に写る7種類の葉の中で、カエデの仲間は5つ。カエデの葉の形にも、バラエティーがありますね。#高尾山 #紅葉 #カエデ pic.twitter.com/FJQQFZEJW9
11/24 見頃
天気:小雨 気温:14.0℃ 富士山展望:×
— 高尾ビジターセンター【公式】 (@takaovc) 2019年11月24日
昨日からの雨にも耐え、まだ麓、薬王院、山頂で紅葉が見頃となっています。
今年の紅葉は葉の状態がしっかりしていて、見ごたえがあるように思います。#高尾山 #高尾ビジターセンター #紅葉 pic.twitter.com/VBDkQhcy7R
12/1 見頃
天気:晴 気温:5.0℃ 富士山展望:×
— 高尾ビジターセンター【公式】 (@takaovc) 2019年12月1日
薬王院周辺の紅葉や、登山道のあちこちで黄葉がとてもきれいです!
山内は、昨日に引き続き早くも多くの人で賑わっています。
迷子や落とし物には十分ご注意ください!#高尾山 #高尾ビジターセンター #紅葉 pic.twitter.com/QcsYatqI97
12/6 落葉進む
天気:曇 気温:2.5℃ 富士山展望:×
— 高尾ビジターセンター【公式】 (@takaovc) 2019年12月6日
雲の多い冬空が広がっています。
山頂周辺では落葉が加速し、落ち葉が降り積もって絨毯のようになっているところも。
本格的な冬が近づいています。#高尾山 #高尾ビジターセンター #落葉 #師走 pic.twitter.com/gwZaddCMFb
推定結果「11/27頃から見頃に近く、12/7頃に見頃を過ぎ」は実際の様子とかなり近そうです。
東福寺
京都 東福寺は、11/23〜11/30頃で見頃間近〜見頃を行ったり来たりしています。12/1以降は見頃過ぎのようです。公式Twitterアカウントがないため、ユーザのツイートと比べてみます。
11/16 見頃間近
東福寺の紅葉②
— まきこ@写真とピアノの人 (@makiko_photo) 2019年11月16日
京都に来て良かった~😊
本当に見事✨
紅葉の見頃はもう少し先かもです。#東福寺 #京都 #紅葉 pic.twitter.com/FUaXb7cHuC
11/23 見頃
#Bura_Bi_Now #京都 #東福寺 #紅葉
— hikari (@hikari705) 2019年11月23日
早起きして朝9時前には通天橋に着いたらすでに大混雑。駅も人がこぼれそうだった😅
紅葉はほぼピーク。
①谷から通天橋を
②通天橋から向かいの臥雲橋を
大満足😊
入場から物凄い混雑、内部もごった返し。橋の上は既に大混雑で撮影禁止、入場制限あるかも? pic.twitter.com/lxISBa9q81
11/27 見頃
本日の東福寺の様子。
— 京阪電車おでかけ情報【公式】 (@okeihan_net) 2019年11月27日
通天橋からは鮮やかに染まった紅葉が望めます。
京都の紅葉は今が全盛です🍁
紅葉情報はこちら ⇒ https://t.co/CEJKKL73cj#紅葉 #紅葉とる人おけいはん #東福寺
(2019.11/27撮影) pic.twitter.com/l7FARS1eak
12/1 落葉始まり
東福寺の紅葉。
— 遍路J@TFCC (@henrobicycle) 2019年12月1日
ピークが過ぎて散り始めてるけど
それでも圧巻。#京都#紅葉 pic.twitter.com/7sjlKi5fpe
12/7 ほぼ終わり
東福寺の紅葉はほぼ終わってましたが、まだ少し残ってました。
— ハッチ🍄 (@hutchnosuke) 2019年12月7日
見頃を過ぎたお寺は人も少なく、風情があります。
これから寒い寒い冬がやって来るのねー。
四季って素晴らしーーー! pic.twitter.com/2FYAsGtZCe
推定結果「11/23〜11/30頃で見頃間近〜見頃」と期間的には合致しました。スコアがもう少し安定していれば、良い結果と言えそうです。
課題
概ね出来上がりましたが、まだまだ課題は多いです。
- サンプル数の不足。マイナーな紅葉スポットではほとんど推定出来ない
- 紅葉スポットのノイズ。紅葉スポット以外が大量に含まれている
- 紅葉以外への対応。春までに桜の開花状況に対応したい
まとめ
- 紅葉スポットと紅葉状況を推定するWebサービスを開発した
- 有名所の推定精度はそれなりにありそうだった
- 精度向上や桜への対応なんかは今後の課題
私の転職活動は中学生から始まっていた
SIer脱出を語る Advent Calendar 2019 8日目の記事です。
「アウトプット大事」というHow toの話しかしません。 転職の経緯や転職後については SIerから自社サービス系に転職して半年ちょっと経過した (2019/05/15) をご覧ください。
目次
どんなアウトプットをしてきたか
現存するものを列挙するとこんな感じです。古いものだと、中学生の頃に公開したものもあります。
ソフトウェア
- P2P地震情報
- 窓の杜やスラドに載ったり、Interfaceに記事を書かせてもらったりSBSラジオにちょっと出たりした
- 仮想ステレオミキサー、ニコ生アラート(弱) (kram.nyamikan.net)
- WFログビューア
その他
アウトプットに救われた転職活動
「それ、職務経歴書に書いたほうが良いですよ」
初の転職活動は、右も左も分からず転職エージェントを2社活用しました。印象的だったのはIT特化のエージェントでした。
プログラミングを始めたきっかけから今後どうしたいかまでを洗いざらい話した後、こう言われました。
職務経歴書を1ページ増やしても良いので、業務外の活動やSIerで自主的にやっていたこと(Demo or Die(SI現場でいろいろ作った話))をもっと書いたほうが良いと思います
職務経歴書に業務外の活動、という矛盾するアドバイスでしたが、結論から言えばこれに救われました。
私に求められていたもの
転職先はいわゆるWeb系とか自社サービス企業と呼ばれるところですが、現職のメンバ(ソフトウェアエンジニア)には次の特徴があると感じています。
- とにかく技術が好きで、吸収が早い
- プロダクトやビジネスに興味を持っている
- 当事者意識が強い
裏を返せば、メンバとして加わることになる私にも、これらが求められていました。
SIerのギャップとアウトプットのフィット
振り返ると、所属していたSIerでの「普段の仕事」は、求められていたものと大きなギャップがありました。
- 属人性を排除し、標準化された技術を使う
- 作業を細分化するか、細分化された作業を仕上げるかのどちらか
- プロダクトやビジネスに影響を与えにくい
エージェントの方のアドバイスを受けて、上記の仕事にはほぼ触れず「業務外の活動やSIerで自主的にやっていたこと」を中心に据えてアピールをしたわけですが、それこそが「求められていたもの」に近いものでした。
それが評価されたことで、無事転職に至りました。
中学生の頃から始めていた業務外でのアウトプットが、転職に繋がりました。つまり、私の転職活動は中学生から始まっていたのです。
めでたしめでたし。
おまけ: アウトプットでの心がけ
私がアウトプットで心がけているのは2点です。
やりたいことをやる
「やらなきゃ(義務感」は辛くなるので、やりたいことをやってアウトプットにしています。
ポイントは「如何にやりたいことを増やすか」です。情報を仕入れたほうがやりたいことが増えるケースもあります。SIerの頃はWEB+DB PRESSやSoftware Designを読んだり、Podcastを聞いたり(Rebuild.fm、Misreading Chat、その他)していました。
最近は現職のメンバから刺激を受けることがめちゃくちゃ多いです。
稚拙でも良いので、正しさとリスペクトを意識する
ブログやQiita記事の場合は、次の2点を意識しています。
- 公式ドキュメントなどで正しさを確認する
- 参考にしたものや引用元を明記する
「巨人の肩の上に立つ」という表現がありますが、それです。
かなり時間を使ってしまうことも多いですが、もっと良い方法を見つけたり、間違っていることに気づいたり、デタラメ記事の作成を避けたり出来るので、メリットは多いです。
さあ、アウトプットを増やしていきましょう。
Fargate Spotのお値段比べてみた
Fargate Spotが発表された.
70%ディスカウントは魅力的であるものの,EC2と比べて高いのか安いのかがわからない.ということで表にした.
タイプ | vCPU | Mem | EC2 オンデマンド |
EC2 スポット |
EC2 リザーブド (1年前払い) |
Fargate | Fargate Spot |
---|---|---|---|---|---|---|---|
m5.large | 2 | 8 | 0.124 | 0.035 | 0.081 | 0.145 | 0.044 |
c5.large | 2 | 4 | 0.107 | 0.033 | 0.072 | 0.123 | 0.037 |
r5.large | 2 | 16 | 0.152 | 0.037 | 0.089 | 0.190 | 0.057 |
※ap-northeast-1(アジアパシフィック (東京)),2019年12月5日(木)23時時点,単位は USD/h
厳密には, C5 は M5, R5 とプロセッサのクロック周波数が異なるし,そもそもFargateのプロセッサシリーズは公表されていない点に注意.
見づらい表になってしまったが,Fargateはメモリ単価が少々高いという印象.
気象庁の天気予報文をパースする ☀☁☂
天気予報文
気象庁の天気予報には 予報文 があります。
北東の風 日中 東の風 くもり 昼過ぎ まで 時々 晴れ 所により 夜遅く 雨
気象庁 | 天気予報 : 東京都 (10/20 11:00発表 21日の天気予報文)
やや難解ですが、 気象庁|予報用語 と照らし合わせることで下記であることが分かります。
時間 | 天気 |
---|---|
0〜15時 | くもり 時々 晴れ |
15〜21時 | くもり |
21〜24時 | くもり 所により 雨 |
天気予報文と正規表現
ここからは根拠のない独自研究です。
文法
気象庁が発表する天気予報文には 文法 が存在し、どの予報文もいずれかの文法にマッチします。以下は例です。
文法 | 例 |
---|---|
天候 | 晴れ |
天候 頻度 天候 | 晴れ 時々 くもり |
天候 時間帯 天候 | くもり 昼過ぎ から 夕方 雨 |
天候 時間帯 頻度 天候 | くもり 昼前 まで 時々 晴れ |
天候 頻度 天候 時間帯 天候 | 晴れ 時々 くもり 夜のはじめ頃 から 雨 |
天候 所により 天候 | くもり 所により 雨 |
天候 所により 時間帯 天候 | くもり 所により 明け方 まで 雨 |
天候 所により 修飾 | 雨 所により 雷を伴い 激しく 降る |
天候 所により 時間帯 修飾 | 雨 所により 夕方 から 雷 を伴う |
天候 頻度 天候 時間帯 天候 所により 時間帯 修飾 | 晴れ 時々 くもり 夕方 から 雨 所により 夜遅く 雷を伴い 激しく 降る |
正規表現
文法を正規表現で表すと、次のようになります(雪 後 雪
などのありえない表現もマッチしますが、今回は無視します)。
(晴れ|くもり|雨|雪|雨か雪|雪か雨|雪 で ふぶく|雨 で 雷 を伴う|雨 で 雷を伴い 激しく 降る)( (時々|一時|後) (晴れ|くもり|雨|雪|雨か雪|雪か雨|雪 で ふぶく|雨 で 雷 を伴う|雨 で 雷を伴い 激しく 降る))?( ((朝晩|朝夕|夜)|(未明|明け方|朝|昼前|昼過ぎ|夕方|夜のはじめ頃|夜遅く)( (まで|から( 未明| 明け方| 朝| 昼前| 昼過ぎ| 夕方| 夜のはじめ頃| 夜遅く)?))?)( (時々|一時))? (晴れ|くもり|雨|雪|雨か雪|雪か雨|雪 で ふぶく|雨 で 雷 を伴う|雨 で 雷を伴い 激しく 降る))?( (所により|[^ ]+ では)( ((朝晩|朝夕|夜)|(未明|明け方|朝|昼前|昼過ぎ|夕方|夜のはじめ頃|夜遅く)( (まで|から( 未明| 明け方| 朝| 昼前| 昼過ぎ| 夕方| 夜のはじめ頃| 夜遅く)?))?))? ((晴れ|くもり|雨|雪|雨か雪|雪か雨|雪 で ふぶく|雨 で 雷 を伴う|雨 で 雷を伴い 激しく 降る)|(雷 を伴う|激しく 降る|雷を伴い 激しく 降る|霧)))?
何を言っているんだという感じなので、擬似的に表現をすると次のようになります。
天候( 頻度 天候)?( 時間帯( 頻度)? 天候)?( (所により|地名)( 時間帯)? (天候|修飾))?
項目 | 正規表現 |
---|---|
天候 | (晴れ|くもり|雨|雪|雨か雪|雪か雨|雪 で ふぶく|雨 で 雷 を伴う|雨 で 雷を伴い 激しく 降る) |
頻度 | (時々|一時|後) ※時間帯の後ろは (時々|一時) |
時間帯 | ((朝晩|朝夕|夜)|(未明|明け方|朝|昼前|昼過ぎ|夕方|夜のはじめ頃|夜遅く)( (まで|から( 未明| 明け方| 朝| 昼前| 昼過ぎ| 夕方| 夜のはじめ頃| 夜遅く)?))?) |
地名 | 気象庁 | 気象警報・注意報や天気予報の発表区域 の「市町村等をまとめた地域」に準じる |
修飾 | (雷 を伴う|激しく 降る|雷を伴い 激しく 降る|霧) |
※「雪 で ふぶく」等は修飾が入っていますが、簡単のために天候にまとめています。
※カバー出来ていない天気予報文が存在する可能性があります。
テスト
下記サイトで試してみるとマッチします。
天気予報文とパーサ
正規表現のままパーサを書くのはつらいため、より簡単にして概ね次のルールでパーサを書きますました。
- 入力は予報文、出力は0〜21時の3時間ごとの予報
- 「時間帯」「所により(地名)」「何もない(終端)」が出現した場合に分割
- 「所により」「頻度」が含まれる場合は予報を追記。それ以外は指定時間帯の予報として上書き
動作
$ ruby runner.rb 天気予報を入力: 晴れ 時々 くもり 夕方 から 雨 所により 夜遅く 雷を伴い 激しく 降る 解析結果: {:wind=>nil, :weather=> {0=>"晴れ時々くもり", 3=>"晴れ時々くもり", 6=>"晴れ時々くもり", 9=>"晴れ時々くもり", 12=>"晴れ時々くもり", 15=>"雨", 18=>"雨", 21=>"雨所により雷を伴い激しく降る"}} 天気予報を入力: 北東の風 日中 東の風 くもり 昼過ぎ まで 時々 晴れ 所により 夜遅く 雨 解析結果: {:wind=>"北東の風 日中 東の風", :weather=> {0=>"くもり時々晴れ", 3=>"くもり時々晴れ", 6=>"くもり時々晴れ", 9=>"くもり時々晴れ", 12=>"くもり時々晴れ", 15=>"くもり", 18=>"くもり", 21=>"くもり所により雨"}}
3時間ごとの天気予報がわかるようになりました。めでたしめでたし。
同じ構図で何枚も撮って,最も手ブレしていない 1 枚だけを残す
結論
- ImageMagick の
compare
コマンドで, Perceptual Hash (pHash) により同一構図かチェックする. - ImageMagick の
convert
とidentify
コマンドで, Canny 法によりエッジ検出し同一構図の n 枚から最も鮮明な 1 枚を残す. - ソースはここに置いた: GitHub - typewriter/unblurred-photo-picker
背景
手ブレしたくないが, ISO 感度もあまり上げたくない(綺麗に撮りたい).その場合,ギリギリのシャッタースピードで何枚も撮る,ということをよくやります.しかし,撮影後の写真の選別が極めて面倒なうえ,かなり機械的な作業です.
機械的な作業… ということは機械で出来るのではないでしょうか.やってみました.
同一構図の判定
同一構図の判定は,画像の類似度が高いかどうかで行うこととします.ただし,画像処理はさっぱり分からないため, ImageMagick に全力で甘えます.
ImageMagick には compare
コマンドがあり,お手軽に画像比較を行うことが可能です.
画像比較に使用できるメトリクスの一覧は ImageMagick - Command-line Options#-metric type にありますが,今回は PHASH (Perceptual Hash) を用います.
# ImageMagick 7 系.小さいほど類似度が高い. $ magick compare -metric PHASH P9240031.JPG P9240035.JPG NULL: 13.7601
Perceptual Hash ではハミング距離を取って類似度とする例を見かけましたが, ImageMagick の数値はそうではないようです.まあ気にせずいくつかの画像で試してみます.
画像1 | 画像2 | 備考 | 出力値 |
---|---|---|---|
同じ構図 | 0.501766 | ||
少し下に向けた | 8.26181 | ||
別構図 | 39.5355 |
同一構図では数値が小さく,異なる構図では数値が大きくなりました.使えそうです.
「最も鮮明な」写真の判定
やはり画像処理はさっぱり分からないため,こちらも ImageMagick に頼ります.
当初は「標準偏差 (Standard deviation) が大きければ値のバラつきが大きい→鮮明」と考えていましたが,手ブレした写真のほうが標準偏差が高いケースが複数枚でみられたため不採用です.
$ magick identify -format "{ \"Kurtosis\": %[kurtosis], \"StandardDeviation\": %[standard-deviation], \"Skewness\": %[skewness], \"Entropy\": %[entropy] }" 20171104_PB040552.JPG { "Kurtosis": 3.04856, "StandardDeviation": 11783.4, "Skewness": 1.91352, "Entropy": 0.760502 }
画像(切り抜き) | 標準偏差 |
---|---|
11783.4 | |
11899.3 |
代わりに, Canny Edge Detection でエッジ検出した結果の標準偏差を用いることにします.
$ magick convert 20171104_PB040552.JPG -canny 0x1+10%+30% edge.png $ magick identify -format "{ \"Kurtosis\": %[kurtosis], \"StandardDeviation\": %[standard-deviation], \"Skewness\": %[skewness], \"Entropy\": %[entropy] }" edge.png { "Kurtosis": 13.5456, "StandardDeviation": 14823.4, "Skewness": 3.94279, "Entropy": 0.303514 }
画像(切り抜き) | エッジ検出後(同左) | 標準偏差 |
---|---|---|
14823.4 | ||
11678.3 |
良さげな感じになりました.
スクリプト
Rubyで書きました。裏でImageMagickのコマンドガンガン実行します。
参考
*1:バックアップも兼ねているのでアップロードはしている
日用品のストック管理ツール Stokk を作った
日用品のストックを管理出来るツール「Stokk」を公開しました。
以下はポエムです。
技術選定
- サーバをわざわざ建立するほどの機能はない
- mBaaSと親和性が高そう
- フロントエンドを書きたい
ということで、SPA + mBaaSで作ることにしました。
使ったもの
- ツール
- 言語
- TypeScript
- フレームワーク
- その他ライブラリ
- mBaaS
ツール: Adobe XD
初使用。無料の「スタータープラン」があり、アカウントさえあればシュッと使えます。
プロトタイプは作らず、ちょっと便利な図形描画ツール程度に使いました。事前にデザインや要件を整理でき、その後データやイベントの流れを練り練りするのにも大いに役立ちました。
言語: TypeScript
JavaScriptを操れる自信はなかったのでTypeScriptで書きました。エディタはVisual Studio Code。
ただ、厳格に型付けをするのは諦めました。動く状態で、「thisはVueだからお前が定義したプロパティなぞ存在せんわ!」などと言われていました。「ぐ、Gradual Typingだから…」と意味不明な言い訳をしつつキャストしたりanyにしたりしました。
フレームワーク: Vue.js + Vue Router, Bootstrap + BootstrapVue
ふわっと書きたかったのとReactは仕事で書く機会があるのとでVue.jsにしました。Vue CLIでTypeScriptも含めて一括で初期設定でき、とても楽でよいです。
デザインは安定のBootstrap。BootstrapVueも入れましたが、「BootstrapのListってBootstrapVueでどう書くんだっけ」と毎回マッピングしつつ書いていました。効率的に書けたかは分かりません。
その他ライブラリ: Lodash, Font Awesome
Lodashはdebounceに、Font Awesomeはアイコンに。
mBaaS: Cloud Firestore, Firebase Hosting, Firebase Authentication + FirebaseUI
作るサービスと親和性高そうなので全面的に依存しました。
FirebaseUIについては、 Firebase 6.6.1 と FirebaseUI 4.2.0 でドキュメント通りに書いたつもりで動作せず(firebase.auth()
と書くとfirebase
がないと怒られる)。
メジャーバージョンを1つ落として FirebaseUI 3.6.1 を使用することで落ち着きました。何故なんだ。
そのほか
命名: Stokk
当初「にちまね!(日用品在庫マネージャ)」で けいおん!風にしようと思っていました。が、サブドメインを割り当てるタイミングで nichimane
は無いな… と思って在庫(stock)を意図的に誤字らせました。
アニメーション
「動いているんだけど動いている感がない」という印象を受けたため、「小気味よく動いている」感を演出するべくアニメーションを設定しました。
- アイテム追加、削除、並び替え時
- 読み込み・保存時(スピナー表示)