まぁまぁ穏やか
しばらく書いていなかったので書き方を忘れてしまった。
もう 10 月も終わりそうなの?早いね。
最近は仕事ではコードをほとんど書かなくなった。 自動化したいタスクはいくつかあれど、そのためにプログラムを書く時間がない。まぁしょうがないよね。
プライベートでは 8 月頃に少しだけ書いてた。特定のアプリケーションにキー入力を送って、キャプチャを撮って、Gyazo にアップロードして esa に書き込むという感じ。
Python の PyAutoGUI
というライブラリを使ってみたらなかなか便利だった。
動かしているプログラムから一部抜き出すとこんな感じ。
import pyautogui as pag pag.PAUSE = 0.8 class Captor: def activate_by_image(self, filepath: str) -> None: """search image on screen and activate application""" location = pag.locateOnScreen(filepath, grayscale=True) if location: x, y = pag.center(location) pag.click(x, y) else: print('image missing on screen...') return
既存の画像との照合ができるので普通の GUI アプリの自動化にも使えるよ。
やらなきゃいけないことはたくさんあるから全然気を抜けないし、だらけている暇はないけれど、その割にはまぁ何とか一応は穏やかに過ごせているかな。
失効しそうな年休が 10 日以上残っていたので期限までに利用したい旨を伝えたら、何だかんだで繰り越して OK ということになったのでしばらく水曜を休みにして週休 3 日生活を送ろうと考えている。
メールが来ていないことを通知する
朝、以下の tweet を見かけて面白そうだったので考えてみた。
子どもが登下校時に端末にカードをタッチすると、親にメールが届く仕組みになっているんだけど、メールが届くのに慣れてしまって、万が一届かなかった場合に気づきにくい。IFTTTを使って「一定時間までにメールが届かなかったらLINEに通知が飛ぶ」という仕組みを構築したいのだが、どうすればいいのか
— Ayumu (@acomacom) 2018年5月12日
IFTTT はアカウントは作ったものの、ほとんど使っていなかったな。 活用したら便利なのかも。
で、軽く触ってみた限りでは IFTTT の機能だけでは上記を実現するのは難しそう。
来るべきメールが来ていないことを知るには、最後にそのメールが来た時点からの経過時間を所定の閾値と比較すればよいのではないだろうか、と考えて、頭の体操がてら、Google Apps Script で書いてみた。
Google ドライブで 新規
-> その他
-> Google Apps Script
を選択して、下記コードを貼り付けて保存すると動かせると思う。
// Gmail の検索クエリ var query = 'newer_than:1d 何とかの通知'; // 何分以上前かの閾値 var threshold = 120; // 結果の通知を送るメールアドレス var adminAddress = 'test@example.com'; /** * エントリポイント * この関数にトリガーを設定して毎日実行する * * 例えば、通常 8:00-9:00 の間に通知メールが来るのであれば、 * トリガーを「日タイマー: 9:00〜10:00」に、 threshold を 120 に設定すれば * 来るべきメールが来ていないことを検知できるのではないだろうか。 */ function main() { var d = getLatestMailDate(query); if (isLongerPeriod(d, threshold)) { sendNotificationMail(); } } /** * 指定したアドレスへメールを送る */ function sendNotificationMail() { var subject = '直近 ' + threshold + ' 分以内にメールはありませんでした。(クエリ: ' + query + ' )'; var body = 'Hello world!'; GmailApp.sendEmail(adminAddress, subject, body); } /** * クエリで検索したメールのスレッドから最新のメールを取り出し、日時を取得する * @param {String} q - 検索クエリ文字列 * @return {Date} */ function getLatestMailDate(q) { var threads = GmailApp.search(q); var latestThread = threads[0]; var messages = latestThread.getMessages(); // 配列の最後の要素(メール) var latestMessage = messages[messages.length - 1]; var d = latestMessage.getDate(); return d; } /** * 実行時の現在時刻との差分が指定値よりも大きいか比較する * @param {Date} d - 日時オブジェクト * @param {Number} t - 何分以上前か * @return {Boolean} */ function isLongerPeriod(d, t) { var now = new Date(); // 現在時刻との差分(単位はミリ秒) var diff = now.getTime() - d.getTime(); // 分に換算 var minutes = diff / (1000 * 60); return minutes > t; }
あとはこの通知メールの受信を IFTTT で LINE に通知すればよいのではないか。たぶん。
初回実行時の手順
GAS から Gmail にアクセスするために初回実行時には認証ダイアログが表示される。
GAS のトリガー設定
毎日繰り返し実行する場合、実行タイミングは 1 時間の幅を持たせて指定する形になる。
最近ずっと寒いね
- なんだかずっと忙しい感じが続いている。
- 先週金曜くらいに仕事の効率アップに役立つ便利ツールの構想を思いつき、土日はずっと作っていた。
- 今回は GCP (Google Cloud Platform) を使ってみる。最初 Python で少し書いて API を触ってみてから Go で書いている。
- 最終的にはインターフェースとして Slack の bot も作る予定。
- GAE がいいのかなと思ったけれど、多分 Heroku の方が適していそう。
- Emacs で Python を書いていると auto-complete の補完が少し物足りないと感じた。外部パッケージの関数とかを補完してくれていないっぽい。
- Python はしばらくの間 Visual Studio Code や PyCharm で書いていたから Emacs で書くのは久しぶり。多分設定が足りていないのかなと思うけれどひとまず後回しにしよう。
- あ、あと Emacs は先日設定ファイルを一新してから popwin とか細かいところで少しこれまでの操作感と違う部分があるから、おって設定ファイルを見直そうと思う。
引き続き自炊してる
- 今日はナス炒めを作った。
- といっても、ナスを買ってきて切って炒めただけ。
- 最近、キッコーマンのこのシリーズを買っている。
- 自分で用意する食材が 1 品か 2 品というのが初心者にやさしい。
Python 2.7 のインストール
つい忘れるので書いておこう。
MacPorts でのインストール
$ sudo port install python27
実際は他の何かを入れた際に依存関係で入っていたようなのでやっていない。
virtualenv で仮想環境を作成する
$ pip install virtualenv $ virtualenv -p (which python2.7) ~/py27 # (which python2.7) は fish の書き方
PATH を通す
config.fish
に以下を追加する。
set -x PATH $HOME/py27/bin $PATH
see also
自炊するようになった
最近また自炊するようになった。これまでと少しスタンスを変えて、食材を一度にたくさん買わないようにした。当日か、最大でも翌日には使い切るくらいのものしか買わない。内容量によっては残ることもあるけれどあまり気にしない。
幸い自宅からスーパーまでそんなに離れていないので、実際に計測してみたら往復しても大して時間はかからないことがわかったし、むしろ運動になってちょうどよいのだと考えるように切り替えた。
今までは、ある程度まとめて買う方が効率的だろうと考えていたけれども、自炊スキルがあまり高くないこともあって、たくさん買おうとすると「用途のイメージがクリアではないけれども購入する」という部分の占める割合が大きくなっていたと思う。
そして「外食するかもしれないし、自炊するかもしれない」という曖昧な状態だと、自炊を選択しない場面も出てくるので使いきれないうちに傷んでしまったりして、しょんぼりした気持ちになったりする。
一方、今日はこれを作ろう、だからアレとアレを買おう、と明確に決まっている状態であれば売り場でも迷わないし、買って帰って作って食べる、とスムーズな進行が可能になる。言われてみれば当たり前だけどさ。
ただ、もう少し短時間で済ませられるようになりたいというのはある。
ダイソーのバックレストがよかった
これ、すごくよかった。このつくりで 100 円で買えるというのはちょっと信じられない。自宅と会社の両方に設置しても 216 円!
買いだめしておいた方がよいだろうかと考えてしまうレベル。置く場所がないから難しいけれど。
バッチファイルでログを取る
会社で他の人にも使ってもらっているツールについて、うまく動かないようだという連絡をもらったので、エラーの内容を確認するためのバッチファイルを用意した。
@echo off rem 実行する.bat のディレクトリに移動 cd /d %~dp0 set log_file="log.txt" echo ---------- >> %log_file% rem %var:~i,j% で var の i 文字目から j 文字目までを抜き出す rem i は 0 始まり echo %date% %time:~0,8% >> %log_file% set host=%COMPUTERNAME% echo host: %host% >> %log_file% set user=%USERNAME% echo user: %user% >> %log_file% echo ----- >> %log_file% rem ログを取りたいアプリケーションを実行 rem something.exe >> %log_file%
コマンドプロンプトはテキストのコピペがやりにくかったり、色々とフレンドリーではないよね。
PowerShell は起動が遅いし実行権限の設定を変更しないとスクリプトを実行できなかったり、管理者用という感じの印象。
ノート PC の設定してた
(last update: 2018/01/22)
しばらく書いてなかったな。
先週末にノート PC の OS をインストールしなおしたのでセットアップしてた。
少し前に OS を high Sierra にアップデートしてみたら、Haskell の管理ツール stack
が動かなくなったみたいで、これもよいきっかけと思いクリーンインストールしなおすことにした。
数年前から各種設定ファイルは ~/dotfiles
にまとめて Github で管理しているので、ここから引っ張ってきてちょこちょこと作業すればいいのだけれど、今回は少し変更したいところもあったので設定ファイルを作り直すことにした。
変更したかったのは
- Cask での Emacs パッケージ管理をやめる
- Emacs の設定ファイル
init.el
を分割する $GOPATH
を$HOME/go
ではなく$HOME
にする- zsh はもう使わない、fish だけでよい
- Ruby のインストールもいらないかな
などなど。
Emacs のパッケージ管理
これまでは Cask を使っていた。~/.emacs.d/Cask
というファイルに使いたいパッケージを記入しておくと $ cask install
とコマンド実行するだけで全部インストールしてくれるもの。
これはこれで便利だったけれど、use-package
というパッケージを使えば、elisp のみでパッケージのインストールとその設定をまとめて記述することができて、外部コマンドを導入する必要もなくすっきりするので、変更してみたくなった。
- package.el + use-package で落ち着いた - YAMAGUCHI::weblog
- use-package.el : Emacs の世界的権威が行っている最先端ラクラク init.el 整理術
- use-package|定形のパッケージ設定を短く簡単に記述する! | 今日の実験ノート
use-package
のいいところ
require
したパッケージが入っていなかった場合でもinit.el
の読み込み自体はエラーにならない。:ensure t
と書いておくとそのパッケージをインストールしてくれる- key-bind や add-hook なども簡潔に記述できる
こんな感じ。
;; https://github.com/benma/visual-regexp.el (use-package visual-regexp :ensure t :bind (("M-%" . vr/query-replace)) )
Emacs の設定ファイル分割
分割をやめた方の話も一応。
ag とか ripgrep とか
grep の代替となる高速検索ツール、ag とか名前は聞いたことがあったけれど使っていなかったので今後は活用してみたい。そもそも grep すらあまり使っていないので何かと効率悪いことをしていると思われる。
anything と組み合わせて便利な感じになったりするんだと思う。多分。もしかしたら helm じゃないとだめなのかな。
何ならもう一回、rubikitch 先生の有料メルマガを購読して質問してみるのがいいかもしれない。
- Emacs に ag を入れたら速すぎて捗る - PartyIX
- ag.el : the silver search(ag)の素晴しいインターフェース
- ripgrep.el : 【 ag より、ずっとはやい!!】超音速 grep と Emacs インターフェース(Windows 安心)
- agとptとripgrep,どっちがどうでどれを使えばいいのか調べた予習 - Lambdaカクテル
ripgrep 入れようとしたら
ripgrep 入れようとしたらスムーズにいかなかくて yak shaving 感あったと tweet したら作者の方からバイナリあるよ、とリプライいただいてびっくりした。
素直にダウンロードして /usr/local/bin
に置いておいた。まだ使いこなせていないけれどせっかくだから使いたい。
You can download pre-compiled binaries for Windows, Mac or Linux: https://t.co/mFtDwKBVBG
— Andrew Gallant (@burntsushi5) 2018 年 1 月 18 日
Go の TOML ライブラリの作者として認識していた。すごい人はいろいろすごい。
fish の設定
昨年使い始めてから、もう二度と zsh や bash には戻れないと思わせる、とても便利なシェル fish。
今回は、使いたい関数は config.fish
に直接書けばいいかなと思い、プラグインマネージャー fisherman は入れなかった。実際、ヒストリ検索と ghq 管理下のリポジトリに cd
するやつの 2 つがあれば満足かなと。
って思っていたのだけれど、z
という最近 cd
したディレクトリを列挙してくれるツールが快適そうに思えてきて、やっぱり fisherman 入れようかなと少し揺れているところ。
一回使ってみようかしらん。
そんなこんなで
設定ファイル init.el
の内容を見直しつつ、分割して、書き直していった。一つ一つ調べながらやっていたら、結局一週間かかった。
Github にある既存のリポジトリをいずれ差し替えたいのだけれど、Git の操作に疎いのでひとまず bitbucket に置いた。(公開設定がプライベートになっていたので修正した)
README.md
の更新もだいたい落ち着いたのでようやく一息つけるわ。
OCaml モジュールとシグネチャ
ソースファイル .ml
をコンパイルするとオブジェクトファイル .cmo
や .cmx
.o
と同時にインターフェイスファイル .cmi
が生成される。
この .cmi
について調べてみた。理解してしまえば何ということはないのだけれど、少し時間がかかった。
.mli
をコンパイルすると.cmi
が生成される。.mli
がない場合は.ml
から.cmi
が生成される。
では .mli
とは何だろう。
ではモジュールとは、シグネチャとは何だろう。
モジュールとは
詳細な解説は以下の記事、資料を読むとよさそう。
ただ、一度に全部を理解しようとするのはかなり困難だったので、ひとまずは「関連する処理やデータなどのまとまり」くらいの理解でよいと思う。Python や Go でいうパッケージと同じようなものだと考えればよいだろう。
OCaml では一つのファイルを一つのモジュールとするそう。前回書いた、階乗を求める処理でもモジュールとして扱っていた。
fact.ml
は Fact
モジュールとなる。
(* fact.ml *) let rec fact n = if n = 0 then 1 else n * fact (n - 1)
利用する側からは open
で読み込む。
(* main.ml *) open Fact let () = print_int (fact 10); print_newline()
もしくは、モジュール名.関数
の形でドット .
をつけてモジュール内の関数を利用する。
(* main.ml *) let () = print_int (Fact.fact 10); print_newline()
モジュールに別名をつけて読み込む
Python で as
で別名をつけて import
するのと同じように
import numpy as np
以下の書き方で別名をつけて open
することができる。
module F = Fact let () = print_int (F.fact 10); print_newline()
- この場合
open
というキーワードは使わない。 - 別名
F
の部分は大文字で始める必要あり。
シグネチャとは
モジュールについては他の言語でも似たような仕組みはあるので、何となくではあるけれどわかった。次はシグネチャである。
OCaml におけるシグネチャは、モジュールの中身に対するアクセスの可否を制御する仕組みで、個々の関数には private とか public とかつけずに別途まとめて管理できるようになっている。
今まで触ったことのある言語(Python, Go, JavaScript あたり)ではこれに該当するものはなかったと思われ、新鮮に感じた。言語によっては、関数の仮引数や戻り値の型、アクセス修飾子などのことを指してシグネチャと呼ぶ場合があるみたいだけれど。
Python だと名前の頭にアンダースコア _
や __
をつけることで private 扱いになり、Go だと名前の頭文字が大文字なら外部からアクセス可能で小文字だとアクセス不可、という仕様になっている。
- 9. クラス 9.6 プライベート変数 — Python 3.6.1 ドキュメント
- Pythonを書き始める前に見るべきTips - Qiita
- A Tour of Go - Exported names
で、OCaml の場合。
Fact
モジュールを少し拡張して試してみる。階乗 n!
のバリエーションとして多重階乗というものがあるらしい。二重階乗 n!!
であれば一つ飛ばしで積をとる。
5!! = 5 * 3 * 1 10!! = 10 * 8 * 6 * 4 * 2
この多重階乗を求める関数 multifact
を書いてみた。
(* fact.ml *) let rec fact n = if n = 0 then 1 else n * fact (n - 1) let rec multifact x n = match n with | 0 -> 1 | 1 -> 1 | _ -> if x > n then n else n * multifact x (n - x) let doublefact = multifact 2
モジュールを利用する側のコード。
(* main.ml *) open Fact let () = print_int (fact 10); print_newline (); print_int (multifact 2 10); print_newline (); print_int (doublefact 10); print_newline (); ;;
実行結果はこうなる。
# コンパイルして $ ocamlc -o fact10 fact.ml main.ml # 実行する $ ./fact10 3628800 # fact 10 3840 # multifact 2 10 3840 # doublefact 10
シグネチャを *.mli
に書く
次に Fact
モジュールのシグネチャを fact.mli
に書く。公開するものだけを書き、シグネチャに書かれていないものは外部からアクセスできなくなる。
試しに multifact
は非公開としてみる。
(* fact.mli *) val fact : int -> int val doublefact : int -> int
このシグネチャの書式はコンパイラに -i
オプションをつけて当該ファイルを渡すと確認できる。-i
オプションのみの場合は実際のコンパイルはしない(.cmo
ファイルは生成されない)。
$ ocamlc -i fact.ml val fact : int -> int val multifact : int -> int -> int val doublefact : int -> int
*.mli
にリダイレクトして、公開したくないものを削除すればよい。
$ ocamlc -i fact.ml > fact.mli
ファイル *.mli
が存在する場合は、*.ml
よりも先にコンパイルしないとエラーになる。
$ ocamlc -c fact.ml File "fact.ml", line 1: Error: Could not find the .cmi file for interface fact.mli.
fact.mli
をコンパイルして、
$ ocamlc -c fact.mli # fact.cmi が生成される
fact.ml
をコンパイルして、
$ ocamlc -c fact.ml # fact.cmo が生成される
main.ml
をコンパイルしようとすると、エラー!
$ ocamlc -c main.ml File "main.ml", line 4, characters 13-22: Error: Unbound value multifact
ふむふむ。
それでは修正してみよう。main.ml
から multifact
を呼ぼうとしている箇所をコメントアウトして、
(* main.ml *) open Fact let () = print_int (fact 10); print_newline (); (* print_int (multifact 2 10); print_newline (); *) print_int (doublefact 10); print_newline (); ;;
コンパイルすると、今度は無事通った。
$ ocamlc -c main.ml # main.cmo が生成される $ ls . .. fact.cmi fact.cmo fact.ml fact.mli main.cmi main.cmo main.ml
これで必要なファイルが揃ったので、以下のように -o
オプションで実行ファイルを出力する。
$ ocamlc -o fact10 fact.cmo main.cmo $ ./fact10 3628800 3840
あるいは、以下のようにして最初から実行ファイルを出力することも可能。この場合もコンパイラには *.mli
を先に渡す必要あり。
$ ocamlc -o fact10 fact.mli fact.ml main.ml
シグネチャを書かない場合は全て公開される
先のエントリで見たようにシグネチャを書かなくても *.ml
から *.cmi
が生成されていた。この場合は、モジュールの中身は全て公開されることになる。
まぁ OCaml プログラマが意図的にシグネチャを書かないというケースはなさそう(想像)だから、気にしなくていいんじゃないかな。