株式会社はてなに入社しました
windowsファイル転送バトル
実家に帰省するなどすると色々あり、例えばwindows8自体のPC(i3, メモリ4GB)を無理やりwindows10にしたかろうじて動いているPCからデータを別のwindowsPCに移動させるなどのミッションが生じる。
ここで外付けHDDやUSBメモリを使わないことを考えると、windows間で自宅ネットワークを活用してファイル転送をしたい。 ということで様々な方法でwindows間のファイル移動を試みた。
windows自体のファイル共有機能を使う -> 失敗
「windows ファイル移動 ネットワーク越し」的なのでぐぐるとまず出てくるやつ。
ネットワーク共有設定からファイル探索を許可した上で、共有したいファイルを選択するもの。 しかしこの方法ではwindowsPCが古すぎるのが原因か、微妙に違うSSIDに接続しているのが原因かは定かではないが、転送を受ける側のPCで転送元のPCがネットワーク探索できなかった。ので断念。
新パソコン側からsftpで旧パソコンからデータを回収する => 失敗
ファイル共有機能が使えなかったので、別の方法でネットワーク越しにファイルを転送することを考える。 市販のファイル転送ソフトは金かかるので却下。無料で行きたい。 windowsはデフォルトでrsyncが使えないこともあり、外部アプリを入れずに素手で戦う場合の次の一手はsftpになった。 windowsはWinSCPというイカしたGUIアプリがあるのでこれを使うと便利。
sftpを使うということはつまりssh接続が必要になるのでsshdを動かす必要がある。 なんと最近のwindows10はアプリストア的なところからsshdをインストールできるので古い方のPCでインストールをしようとしたところ、なぜかGUI経由だと20%でインストールが止まる。そんな。
調べたらPowerShellでインストールする方法があったので試したらこれで行けた。やはりCUIは正義。 docs.microsoft.com
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 # Start the sshd service Start-Service sshd # OPTIONAL but recommended: Set-Service -Name sshd -StartupType 'Automatic' # Confirm the Firewall rule is configured. It should be created automatically by setup. Run the following to verify if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) { Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..." New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 } else { Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists." }
これでsshdは元気に動き出したのだけど、sshしようとすると同頑張ってもPermissionDeniedになる。そんな。22番ポートは空いてるのに。
ユーザー側の設定がだいぶカオスな事になっていそうなので、ログインユーザーでログインするのを諦めてadministratorでsshできるように仕込んだ。
これでsshできたぞ!!! sftp/scpや!!! と思ったら、なんとadministratorでsshすると、移動したいファイルがsshした先では確認できるのに、sftp/scpすると確認できない(転送されない)という自体が出た。
所有権の問題かと思ってtakwown
で所有権を直したのだけど、効果は無く、キレそう。
ということでこの方法も諦めて、逆の発想で新PCから旧PCにsftpしてファイルを持ってくることにした
旧パソコン側からsftpで新パソコンにデータを送る ==> 成功
新パソコン側でsshdの設定をし、旧パソコンにWinSCPをまずいれる。 このインストールが曲者で、メモリが4GBしかないのでなんか良くわからないアプリが立ち上がるとすぐ固まる。 起動時にその部分さえケアできればあとは新PCに接続し、ファイルを転送すればすんなりクリアだった。
というわけでファイル転送バトルでした。年末という感じですね。良いお年を。
いい感じにuseするのを助けてくれるApp::perlimportsのご紹介
これはPerlアドベントカレンダー2021の12日目の記事です。
昨日はshogo82148さんで、Perl 5.35.5 の iterating over multiple values at a time を先取りでした。
今回はPerlでもいい感じのモジュールのImportをしてくれるApp::perlimportsの紹介です。
App::perlImports
このモジュールはThe Perl and Raku Conference 2021で作者本人からトークされていて、スライド及び動画が上がっています。
スライドはGitHub上にHTMLがあるので手で落とす必要があるようです...。(GitHubPagesは特に使っていない模様)
curl https://raw.githubusercontent.com/oalders/presentations/main/slides/6-perlimports/remark.html -o perlimports.html && open perlimports.html
「Where Did That Symbol Come From?」とトークタイトルが示す通り、Perlのシンボルがどこからimportされたかを明確にしたいというのが実装の動機になっているようです。合わせて動き自体はgolangでimportしているモジュールをいい感じに管理してくれるgoimportsを参考にしているようです。
このモジュールについて説明するにはまず、Perlのモジュールロードがどのように行われているかを確認する必要があります。ざっと見てみましょう。
Perlのモジュールロード
Perlはモジュールをロードする方法に主にuse
を使った方法とrequire
を使った方法があります。*1
歴史的経緯でrequire
の方が先に出ていて、 Perl5でuse
が導入されています。
requireはモジュールロード以外に外部ファイルの読み込みにも使われます。
近年はuse
を使ってモジュールロードをするのが一般的です。
例えば、PerlにデフォルトでバンドルされているHTTPクライアントのHTTP::Tiny
を使う場合は次のようにロードします。
use HTTP::Tiny; my $ua = HTTP::Tiny->new();
ロードタイミング
Perlは動的言語でありますが、use
を使ったモジュールロードの処理は、該当のPerlコードをPerlインタプリタが読み込む、コンパイルタイムに行われます。
例えば存在しないHoge
モジュールをロードしようとすると、次のようにprintが走ることはなくエラーで死にます。
#!/usr/bin/env perl use strict; use warnings; print "hello"; use Hoge;
❯ perl hoge.pl Can't locate Hoge.pm in @INC (you may need to install the Hoge module) (@INC contains: /Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/site_perl/5.32.0/darwin-2level /Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/site_perl/5.32.0 /Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/5.32.0/darwin-2level /Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/5.32.0) at hoge.pl line 2. BEGIN failed--compilation aborted at hoge.pl line 2.
対して、各関数呼び出しなどの処理は実行時に解決されます。 そのためuseを忘れても、対象のメソッドを実行しない限りは処理が止まることはありません。 例えば次のコードは、HTTP::Tinyをuseしていませんが、呼び出す前にdie(Perlでのエラー終了)をして処理を止めるので、ロードエラーは発生しません。
#!/usr/bin/env perl use strict; use warnings; print "hello\n"; die; my $ua = HTTP::Tiny->new(); $ua->get('https://example.com');
perl hoge.pl
hello
Died at hoge.pl line 6.
useの処理
ではuseは何をしているのでしょうか。Perldoc.jpを見るとこう書かれています。
指定したモジュールから、現在のパッケージにさまざまな内容をインポートします; 多くは、パッケージのサブルーチン名や、変数名に別名を付けることで、 実現されています。 これは、以下は等価ですが
BEGIN { require Module; Module->import( LIST ); }
BEGIN { }
はBEGIN ブロックと呼ばれるもので*2、コンパイルタイムに実行させるという処理です。
中ではrequire
を使い、モジュールを読み込んだ後に、そのモジュールにあるimport
メソッドを実行しています。
つまり、前述したPerlのモジュールロードのuse
とrequire
を比較すると次のような違いがあることがわかります。
項目 | use | require |
---|---|---|
ロードタイミング | コンパイルタイム | 実行時 |
副作用 | importを実行する | 読み込みのみ |
整理したところで、このimport
とは一体何でしょうか。
一般的には、これはモジュールで定義しているメソッドを、呼び出し元のコードで定義したかのように使うために実行されるものです。
詳しく見てみましょう。
import
Perlでは特定のモジュールをuse
すると使えるようになるメソッドがいくつか存在します。
例えばコアモジュールのJSONを扱うライブラリであるJSON::PPをuseすると、任意のPerlのハッシュをJSON文字列化できるencode_json
が使えるようになります。
use strict; use warnings; use JSON::PP; print encode_json({hoge => 'hello', isBool => \0});
encode_json
はJSON::PPで定義されているメソッドなので、通常はJSON::PP->encode_json
のように使う必要があります。
import
は特定のメソッドを、use
したタイミングで通常の関数のように呼び出せるようによしなにやってくれる処理になっています。
この処理のことをPerlでは関数のエクスポートなどと読んでいます。
近年ではExporterモジュールが行っています。
あるモジュールで、Exporterモジュールを継承、もしくはuse
すると、そのモジュール内で自動的に関数のエクスポート処理をimport
関数として登録してくれます。
あくまでExporter
を使った場合なので、独自にimport
関数を定義することも可能です。
その場合は通常の関数のように呼び出せるようによしなにやってくれる処理
以外の処理をuse
時に行うように差し込むことが可能となります。
例えばClass::Accessor::Liteはimport
をExport以外の目的に使っており、オブジェクトのアクセサを生成する処理を行っています。
use Class::Accessor::Lite ( new => 1, rw => [ qw(foo bar) ], ro => [ qw(baz) ], wo => [ qw(hoge) ], );
Perlでモジュールをuse
する際に、exportしたい関数名の指定ではないような引数を渡しているモジュールは、ほぼ独自にimport
を書いていると考えても良いでしょう。
use時に差し込まれるモノたち
Exporterを使っているモジュールがuse
されたときに差し込むものは、モジュール側で定義されている@EXPORT
配列に積んだシンボルになっています。((ここでのシンボルはPerlのメソッド(関数、サブルーチン)の他に、スカラ変数や配列も含みますが、Exporter
を使う上ではメソッド以外のシンボルを使うと問題が発生しがちなので、基本はメソッドのことです
))
例えば、Hoge::First
モジュールをuseしたときに、hogeメソッドを差し込みたい場合は、次の様に定義します。
package Hoge::First; use strict; use warnings; use feature qw/say/; use Exporter 'import'; our @EXPORT = (qw/hoge/); #サブルーチンの名前を文字列で配列の中にいれるとEXPORTされる sub hoge { say 'this is first!'; } 1;
Perlは@EXPORT
に積まれているオブジェクトをすべてロードします。
このためuse
しているモジュールが、内部で@EXPORT
に何かしら詰めていた場合、予期せぬシンボルをロードする可能性があります。
また、あるPerlファイルの中で出てきているメソッドが、一体どのモジュールから提供されているか、はたまたデフォルトのメソッドであるかの判断が難しくなってしまいます。
使用しているメソッドが、どのモジュールから提供されているのが解りづらいのは、リファクタリングの結果すでにあるモジュールが提供しているメソッドを使わなくなったのに、use
し続けてしまうモジュールが出たり、逆にプログラミングしている上で使えるだろうと思って書いたメソッドが、実はuse
しないといけなかったなどの問題を誘発しがちです。
また、同じ名前のオブジェクトをロードしてしまうと、呼び出し順序によって使われる関数が異なってしまいます。
例えばHoge::First
とHoge::Second
モジュールでそれぞれhoge
をEXPORTしてみます。
package Hoge::First; use strict; use warnings; use feature qw/say/; use Exporter 'import'; our @EXPORT = (qw/hoge/); sub hoge { say 'this is first!'; } 1;
package Hoge::Second; use strict; use warnings; use feature qw/say/; use Exporter 'import'; our @EXPORT = (qw/hoge/); sub hoge { say 'this is second!'; } 1;
#!/usr/bin/env perl # main.pl use strict; use warnings; use Hoge::First; use Hoge::Second; hoge();
実行すると、最後にロードされたSecondが出てきます。
❯ perl -Ilib hoge.pl this is second!
@EXPORT
はこのような思いがけない処理が走る可能性があるので、Exporterモジュールのコメントにも「とりあえずEXPORTするのはやめよう」といったことが書かれています。
pointoht.ti-da.net
さらにはPerlのLinterであるPerl::Criticにも@EXPORT
を警告するルールが追加されています。
とはいえ関数のエクスポートは便利なので、それはそれで使用したいです。
Perlでは@EXPORT
の他に@EXPORT_OK
があります。これはデフォルトではすべてをロードせず、use
時にエクスポートしたい関数名を書くことを強制させる機能です。
例えばこんな感じにuse
時に関数名を指定します。
use JSON qw(encode_json);
実は@EXPORT
で宣言している場合もuse
時に関数名を指定している場合はそのメソッドしかエクスポートされません。関数名を何も指定せずに()
を書いた場合は何もエクスポートされないため、宣言的に関数名を書くようにするとほぼ@EXPORT_OK
と同様の振る舞いをします。
自分が書いているコードに出てくるメソッドの宣言元をはっきりさせる単純な解決方法はEXPORTをやめ、Hoge::Foo->bar()
の様にフルパスでメソッドを実行することですが、テストヘルパなどEXPORTした方が利便性が高いものもあるでしょう。
decode_utf8
など、EXPORTされたメソッドを使うのがイディオム的に認知されているものもあります。
エクスポートした関数を使うのと、どこから取り込んできたのかの視認性を高めるには、use
時にエクスポートしたい関数名を漏れなく書くしか無いでしょう。
しかしこれを人間がやるのは面倒!!! ということで作られたのがperlimportsです。
....と、ここまで書いたところで奇跡的に裏番組のはてなエンジニア Advent Calendar 2021でid:papixさんが紹介している内容と前提知識が同じだった!!そんなことってあるんだ
あらためてApp::perlImports
- どこから来たのかわからないシンボルをエクスポートしている
use
を特定したい @EXPORT
を全部インポートするのではなく、一部だけ指定してインポートしたい
これらの課題に対して、golangのgoimportsを参考に、構文解析ライブラリPPIの力を借りて誕生したのがApp::perlimportsです。 metacpan.org
著者のOALDERSさんはLWPなどのメンテナもやられています。
使ってみる
例えば次の様なモジュールがあったとしましょう。
package MyPkg::One; use strict; use warnings; use Exporter 'import'; our @EXPORT = qw(f1); sub f1 { print "f1\n"; } 1;
package MyPkg::Two; use strict; use warnings; use Exporter 'import'; our @EXPORT_OK = qw(f2); sub f2 { print "f2\n"; } 1;
package MyPkg::Three; use strict; use warnings; use Exporter 'import'; our @EXPORT_OK = qw(f3); sub f3 { print "f3\n"; } 1;
package MyPkg::Four; use strict; use warnings; use Exporter 'import'; our @EXPORT = qw(f4); sub f4 { print "f4\n"; } 1;
これをhoge.pl
から以下の様に呼び出します。
use strict; use warnings; use MyPkg::One; use MyPkg::Two; use MyPkg::Three; use MyPkg::Four; f1(); f2(); MyPkg::Three->f3();
hoge.pl
の状況は次のとおりです。
f1
はMyPkg::OneがEXPORTしているものを使っているf2
はMyPkg::TwoがEXPORT_OKしているが、コード上は指定を忘れているf3
はフルパスで呼び出しているf4
はuseしているが特に使っていない
このコードはそのまま実行すると, f2の解釈で実行時エラーが発生します。また、どのモジュールからエクスポートされているのかパット見ではわかりません。
さらにMyPkg::Fourは必要がないf4
をエクスポートしてしまっています。
❯ perl -Ilib hoge.pl f1 Undefined subroutine &main::f2 called at hoge.pl line 10.
これをApp::perlimportsに通すと、以下のようにコードが書き換わります。
❯ perlimports --libs lib hoge.pl use strict; use warnings; use MyPkg::One qw( f1 ); use MyPkg::Two qw( f2 ); use MyPkg::Three (); use MyPkg::Four (); f1(); f2(); MyPkg::Three->f3();
見ると次の様な変換が行われています。
f1
,f2
がEXPORT元のモジュールが明記されるようになった- エクスポートした関数を使用していない
Three
とFour
は、空配列を指定することになり、何もエクスポートしなくなっている
変換されたコードはそのまま動かす事ができます。(今回はパイプで動かしています) こうすると、実行時エラーを未然に防ぐことが可能になり、どのモジュールからエクスポートしているか、パット見でわかるようになりました。
❯ perlimports --libs lib hoge.pl | perl -Ilib
f1
f2
f3
さらに、無駄にロードしているMyPkg::Four
を削除することも可能です。
❯ perlimports --libs lib --no-preserve-unused hoge.pl
use strict;
use warnings;
use MyPkg::One qw( f1 );
use MyPkg::Two qw( f2 );
use MyPkg::Three ();
f1();
f2();
MyPkg::Three->f3();
こうすると、無駄なファイルをuse
し続ける事もなくなり便利ですね!
インストール方法
cpanm
もしくはcpm
を使ってインストールしましょう
$ cpanm App::perlimports
$ cpm install -g App::perlimports
$ plenv rehash
使い方
CLIツールとして提供されているので、perltidyの様にコマンドで実行します。
$ perlimports --libs lib,local/lib/perl5 -f hoge.pl
主要なオプションを見てみます。
--libs
-f
- 変換対象のファイル名
--no-padding
- perlimportsが変換したあと、モジュール名の前後に余計な空白をいれない
- デフォルトでは
use Foo qw( bar baz );
の様になるが、--no-padding
を指定するとuse Foo qw(bar baz);
になる
--inplace-edit
,-i
- デフォルトでは変換したコードは標準出力に出るが、このオプションを指定すると変換元のファイルを上書きする
--no-preserve-unused
- 使っていなさそうなモジュールをuseしないようにする
注意点
自動でuseしてくれる訳ではない
下手にgoimportsを使ったことがあると誤解しやすい点で、perlimportsの(現状)の興味の対象はEXPORTしたシンボルをいい感じに特定することのみです。goimportsはimportの追加などもしていましたが、それに対応する機能はありません。モジュールのuse
し忘れなどは興味の範囲外になっています。
例えばMyPkg::One->f1()
の様なメソッド呼び出しをしていて、MyPkg::One
をuseし忘れた場合、perlimportsはこれを補完しません。
また、そもそもuse
しているモジュールがない状態で実行しても補完はしてくれません。
そのためモジュールのuse忘れなどのimportエラーは別系統で判断する必要があります。
❯ cat hoge.pl use strict; use warnings; use FindBin; use lib "$FindBin::Bin/lib"; use DDP {deparse => 1, hash_max => 0}; f1(); ~/workspace/test3 via 🐪 v5.34.0 ❯ perlimports --libs lib -f hoge.pl use strict; use warnings; use FindBin (); use lib "$FindBin::Bin/lib"; use DDP {deparse => 1, hash_max => 0}; f1();
関数エクスポート以外のuseは工夫の余地がある
また、Class::Accessor::Lite
やClass::Enumemonのようなuse
時にエクスポートする関数名を指定する以外に、独自にimports
を定義しているモジュールは、perlimportsはuse
したものを使っていないと見なしてしまいます。これはperiimportsの作者によって名指しでperlimportsの自動整形下に置かない様に書かれているモジュール群か、構文解析の結果自動調整を逃れたもの以外は、空配列のimportに変換されてしまいます。
❯ cat hoge.pl use strict; use warnings; use FindBin; use lib "$FindBin::Bin/lib"; use DDP {deparse => 1, hash_max => 0}; use Class::Accessor::Lite ( rw => [qw/ hoge/], ); f1(); ~/workspace/test3 via 🐪 v5.34.0 ❯ perlimports --libs lib,local/lib/perl5 -f hoge.pl use strict; use warnings; use FindBin (); use lib "$FindBin::Bin/lib"; use DDP {deparse => 1, hash_max => 0}; use Class::Accessor::Lite (); f1();
これを回避するには、調整したくないモジュールをuse
している行の後ろに特定のアノテーション(## no perlimports
)を書くか*3、コマンド実行時の引数でモジュール名を直接列挙、またはモジュール名が列挙されたファイルを渡すことで回避可能です。この指定には正規表現が使用可能です。
作者にPRを送るとignoreするモジュールに追加してくれる様なので、普段使うモジュールで管理されるとまずいものがあればPRを送ると良いでしょう。
~/workspace/test3 via 🐪 v5.34.0 ❯ cat hoge.pl use strict; use warnings; use FindBin; use lib "$FindBin::Bin/lib"; use DDP {deparse => 1, hash_max => 0}; use Class::Accessor::Lite ( rw => [qw/ hoge/], ); f1(); ~/workspace/test3 via 🐪 v5.34.0 ❯ perlimports --libs lib,local/lib/perl5 --ignore-modules-pattern '^Class::' -f hoge.pl use strict; use warnings; use FindBin (); use lib "$FindBin::Bin/lib"; use DDP {deparse => 1, hash_max => 0}; use Class::Accessor::Lite ( rw => [qw/ hoge/], ); f1();
メソッドの追加などの副作用で持つモジュールをuse
している場合は、--no-preserve-unused
に気をつける
Perlのモジュールでは、use
すると自分以外のモジュールのメソッドをオーバーライドやメソッドを名前空間に追加するものがあります。
例えばHTTP::Message::PSGIは、use
するとHTTP::Request名前空間にto_psgi
がメソッドとして追加されます。
これはuse
することで効果を発揮するモジュールであるため、HTTP::Message::PSGIがEXPORTしているメソッドを直接使っていないケースや、HTTP::Message::PSG->
で何かを呼び出しているコードが無い限り、perlimportsで--no-preserve-unused
オプションを付けて実行してしまうと、何も意味がないモジュールを呼び出しているとuse
を削除されてしまいます。
実際はHTTP::Message::PSGI
はperlimports側で明示的に整形する対象に含まないように指定されているので、この様なことは起きないのですが、自分たちで書いているライブラリに似た挙動があるものがあれば、使用する際は気をつけましょう。
明日はid:mp0liiu さんで、immutableなコレクションを作るです!
*1:動的ロードなどを考慮するとこの限りではない
*3:ブロック的に囲うことも可能です
https://metacpan.org/pod/perlimports#ANNOTATIONS/IGNORING-MODULES
ラズパイにgolangで書かれているdocker composeをinstallする
多分令和最新版。
linuxでdocker composeを使いたい場合、macOSやwindowsのDocker for xxx
系とは違い、docker composeはバンドルされてないので、自分でインストールする必要があった。
特にラズパイではdocker-compose
時代(Pythonスクリプト)の場合はpipでインストールする必要があった。
我々はdockerでローカルのことを考えずに動かしたいのにPython3関係の構築をしないといけないのは面倒....。
現代ではdocker composeはgolangで書き直されており、これを使う場合はpip install
が不必要。
さらにARMバイナリ用のdocker composeはGitHub Releaseで配布されているので、落としてくれば使えるという極めて親切な設計になっている。
今回はすでにdockerはインストールされているものとして、docker composeのインストールを見てみる。
インストール方法
基本はcompose
のREADMEに書いてあるとおり。
自分だけで使う場合は、$HOME/.docker/cli-plugins
に、全ユーザーで使いたい場合は/usr/local/lib/docker/cli-plugins
などにdocker-compose
の名前でバイナリをポン置きすれば使えるようになる。
バイナリはリリースから持ってくれば良い。
気をつける点として、uname -m
すると微妙にarmv7l
とcomposeで打たれているアーキテクチャ名(armv7
)と異なる。ワンライナーの中でuname -m
していい感じに取得しようとする場合は注意。
$ uname -m armv7l
自宅のラズパイ4で、全ユーザーにインストールしたい場合は次のような感じでインストールする。
とりあえずrootになり
$ sudo -i
後は素朴に
# mkdir -p /usr/local/libexec/docker/cli-plugins # cd /usr/local/libexec/docker/cli-plugins # wget https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-linux-armv7 -O docker-compose # chmod +x docker-compose
こうするとdocker compose
が使えるようになる。
$ docker compose Usage: docker compose [OPTIONS] COMMAND Docker Compose Options: --ansi string Control when to print ANSI control characters ("never"|"always"|"auto") (default "auto") --compatibility Run compose in backward compatibility mode --env-file string Specify an alternate environment file. -f, --file stringArray Compose configuration files --profile stringArray Specify a profile to enable --project-directory string Specify an alternate working directory (default: the path of the Compose file) -p, --project-name string Project name Commands: build Build or rebuild services convert Converts the compose file to platform's canonical format cp Copy files/folders between a service container and the local filesystem
バイナリポン置きなのでローカル環境に色々いれなくていいので便利ですね。現場からは以上です。
入社してから書いていた分報の行数を眺めてみる
この記事ははてなエンジニア Advent Calendar 2021 の4日目の記事です。
昨日は同期のid:gurriumくんでした。MOD Pythonで色々できるんだ...。僕も趣味で好きなゲームに色々コード書いて楽しんでますが、それは社のアドベントカレンダーには書けないゲームなのでまたの機会に書こうと思います。
社ではScrapboxを使っていて、僕は分報の置き場として色々書いています。個人的にはTwitter感覚で使っています。 分報は以下のフォーマットで基本書いています。
anatofuznote 2021/11/29 - 12/03 [anatofuznote 2021/11/22 - 11/26] <=> [anatofuznote 2021/12/06 - 12/10] #anatofuz #anatofuznote [** 2021/12/3]
実際には↓の用な感じでScrapbox上は見れる
忙しいときはツイート数が減っている気がしていて、逆になんか盛り上がっているときはツイート数が多いかなとなんとなく感じています。 つまりツイート数で自分の状態が測れるのではと思っています。 せっかくなので、本当にそうなのか調べてみました!!! 分報なのでツイート数==scrapboxの行数として考えてみます。
準備
APIを使ってかっこよくやるとかは頭になく、素朴にざっくりと外観が知られればいいので、雑スクリプトで集計します。
分報は先程例示したフォーマットで書いているので、[** 2021/%m/%d]
に囲まれている間の行数を雑にカウントする方法でやってみます。
データはどうやって持ってくるかというと、コピペですね。はい。素朴にやるのが早いねんな。 ということで次のようなPerlスクリプトを書きました。
use strict; use warnings; use Time::Piece; my $current_date = undef; my $result = {}; while (my $line = <DATA>) { if ($line =~ /^\A\s*\[\*.+\s*(2021.*)\]/) { $current_date = $1; } $result->{$current_date}++; } my @sorted_date = sort { Time::Piece->strptime($a, '%Y/%m/%d') <=> Time::Piece->strptime($b, '%Y/%m/%d') } keys %$result; for my $date (@sorted_date) { print "$date $result->{$date}\n"; } __DATA__ [** 2021/12/3] 今日も 1日 [** 2021/12/2] オハヨッ ....
Perlは__DATA__
以下に書いたテキストを<DATA>
で読めるので、Scrapboxからかき集めた分報をDATA以下にコピっています。
それを素朴な集計方法で数え上げて、最後に日付順にソートして表示します。
実行するとこんな感じ
❯ perl note.pl | head -4 2021/4/8 503 2021/4/9 11 2021/4/12 260 2021/4/14 256
これをグラフ化したいので、学生時代から慣れ親しんでいるgnuplotでシュッとグラフを作ります。matplotlib? 俺はgnuplotのほうが慣れてるんだ...。
❯ gnuplot G N U P L O T Version 5.4 patchlevel 2 last modified 2021-06-01 Copyright (C) 1986-1993, 1998, 2004, 2007-2021 Thomas Williams, Colin Kelley and many others gnuplot home: http://www.gnuplot.info faq, bugs, etc: type "help FAQ" immediate help: type "help" (plot window: hit 'h') Terminal type is now 'qt' gnuplot> set term svg Terminal type is now 'svg' Options are 'size 600,480 fixed enhanced font 'Arial,12' butt dashlength 1.0 ' gnuplot> set output 'out.svg' gnuplot> set xdata time gnuplot> set timefmt '%Y/%m/%d' gnuplot> plot '<perl note.pl' using 1:2 with line gnuplot> exit
ということでできたのがこれです。
見ていると入社当時は盛り上がっていたものの、9月から口数が少なくなり、11月は一般人みたいなツイート数になってましたね..。 最近ツイート数が上がってきているので盛り上がってきているという感じなきがする。 初期が長めなのは環境構築のログがはられていたりするからっぽかった。
可視化してみると色々見れて便利ですね。ここまで書いてmackerelのサービスメトリックに日毎に送信すると面白いのでは? みたいな気持ちになったので、調べたところ、過去のデータは送信できなさそう? なので次回にご期待ください。
明日はid:mizdraさんです。
調子が悪いときの傾向と対策
最近なんか調子悪かったんですがようやく回復してきたので自覚している調子の悪さについて書いておこう。。。
症状
- 口数が減る
- ツイート数が減る
- 分報を書かなくなる
- 頭の中は色々考えているがアウトプットする気力が減っている
- アウトプットに対してネガティブな反応をもらうと立ち直れなくなるなる状態になってるので本能的にしなくなるのだろう
- でかいタスクをどーんとやって死ぬみたいなのがでる
- ツイート数が減る
- 相談の内容がうまくまとまらない感じで相談する
- まとめるほどの気力がない
- まとめるエネルギーと時間がないので頭の中のごちゃごちゃした解像度で会話をする
- 言語化が全般的にできなくなる
- ちょっとしたことで傷つき出す
- ミーティング時の状態などで「あぁ....」みたいになり悲しい気持ちになる
- 寝る前や土日にそのことばかり考えてしまう
- 常に業務のことを考えている
- 土日と退勤後と出勤前もギョームのコードや情報を見ている
- 楽しくて見ているのではなく不安に駆られてみている
- よく考えればできること、普段できることができなくなる
- 質問の仕方とPRの説明分の情報量をごちゃまぜにして処理してしまう
- 普段なら気にせずできる作文などができなくなってくる
- おちついてデバッグすればわかる問題がわからなくなる
- ものをこぼしたり落としたりするようになる
- 不安感にさいなまれる
- なんか出勤したら大声で紛糾されるんじゃないかみたいな恐怖を考えてしまう(完全に調子悪いとき)
- なんか裏で自分のことについて話されているのではないか、責められているのではないかとなる
- 作業がバッティングする
- 同じ行を書いたり消したりする
- 金の出費が激しくなる
- DLsiteとDMMで色々買う
- 月末のカードの請求でさらに調子が悪くなる
- 新しいコンテンツが全般的に受け付けなくなる
- 音楽はだいたい同じプレイセットをリピートしたり、動画もよく見る人の投稿ばかりみる
- 映画館に行って新しいコンテンツを見る元気がなくなる
- 朝起きれなくなる
- 退勤後何もできなくなる
- 突然よくわからないLINEを知人に送る
対策
- 有給をとっても気持ちが休まらないので仕事はする
- 読書をする
- 好きなホラーを読む
- 紅茶(アールグレイ)を濃いめに入れて飲む
- 他の味のティーパックだと気持ち悪くなる
- 深く考えずできる単純作業をする
- たまに好きな言語でプログラミングする
- ポモドーロテクニックで切り替える
- どうせ仕事は頑張るので頑張りすぎないことを意識する
- とはいえ「もう少し頑張れるのでは...」となりがちなので難しい
- 1日の情報量/思考量を減らす
- あわあわしているときに毎日仕事の着手順を考えてたり他の進捗を考えているとリソースがだいぶ持っていかれる
- 今日しないといけないことだけ見ておく
- タスク順を他の人に考えてもらう
- 焼き肉に行く
- 思えば去年は安々行きまくってたな...
- 水辺に行く
少し余力が出てきたタイミングでまとめるとまぁこういう感じだったんだなーとなりますね。自覚症状以外にもいろいろありそう
TypeScriptで関数合成をする関数を書いてみた
関数型プログラミングといえば関数合成みたいなところありますよね。Haskellだと.でagdaだとoだった気がする。
最近やっているTypeScriptで関数合成をやってみました。
以下はググって出てきたこのサイトを参考にしています。 minaluke.medium.com
まず例題のために簡単なInterfaceを定義してみます。 nameというフィールドがあってstringが入っています。
interface IPerson { name: string; } const person: IPerson = { name: "Mina", };
ここから次の関数を順に実行することを考えます。
処理としてはgetName
でIPerson型からnameのstringだけ取り出して、その結果をgetLength
に流して文字列長を取得。
その結果が偶数かどうかを判定する感じですね。
const getName = (p: IPerson) => p.name; const getLength = (str: string) => str.length; const isEven = (num: number) => num % 2 === 0;
合成関数使わずに純粋に書くとこんな感じだと思います。
console.log(isEven(getLength(getName(person))))
これでも良いんだけど関数が多いと書くのがしんどい!!!! ので合成して一つの関数にまとめることを考えます。
そこで関数合成をする関数composeを考えます。 Javascript的な型を考えないと次のようなコードになりそうです。
const compose = (...fns) => arg =>
fns.reduce((composed, f) => f(composed), arg);
引数として関数の配列fns
を受け取って、reduceで部分適用して進めていく感じですね。
これをTypeScript化することを考えると、fns
やarg
に型を指定する必要があります。
それで実際に書いてみると次のようになりました。
const compose = (...fns: ((arg: any) => any)[]) => (arg: any) => fns.reduce((composed, f) => f(composed), arg);
...fns
は「なにかしらの引数を受け取って何かしらの返り値を返す関数 ( (arg:any) => any
)の配列」なので((arg: any) => any)[]
となり、arg自体は何かしらの型なのでanyになります。
書いてみるとまぁそうだよねという感じですね。多分いい感じのライブラリがありそうなので情報お待ちしております。