まぁまぁ穏やか

しばらく書いていなかったので書き方を忘れてしまった。
もう 10 月も終わりそうなの?早いね。

最近は仕事ではコードをほとんど書かなくなった。 自動化したいタスクはいくつかあれど、そのためにプログラムを書く時間がない。まぁしょうがないよね。

プライベートでは 8 月頃に少しだけ書いてた。特定のアプリケーションにキー入力を送って、キャプチャを撮って、Gyazo にアップロードして esa に書き込むという感じ。

PythonPyAutoGUI というライブラリを使ってみたらなかなか便利だった。

動かしているプログラムから一部抜き出すとこんな感じ。

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 はアカウントは作ったものの、ほとんど使っていなかったな。 活用したら便利なのかも。

で、軽く触ってみた限りでは 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 時間の幅を持たせて指定する形になる。

https://gyazo.com/9fb8af857d229de347bbf33a88c5ddb6

最近ずっと寒いね

  • なんだかずっと忙しい感じが続いている。
  • 先週金曜くらいに仕事の効率アップに役立つ便利ツールの構想を思いつき、土日はずっと作っていた。
  • 今回は GCP (Google Cloud Platform) を使ってみる。最初 Python で少し書いて API を触ってみてから Go で書いている。
  • 最終的にはインターフェースとして Slack の bot も作る予定。
  • GAE がいいのかなと思ったけれど、多分 Heroku の方が適していそう。
  • EmacsPython を書いていると auto-complete の補完が少し物足りないと感じた。外部パッケージの関数とかを補完してくれていないっぽい。
  • Python はしばらくの間 Visual Studio CodePyCharm で書いていたから Emacs で書くのは久しぶり。多分設定が足りていないのかなと思うけれどひとまず後回しにしよう。
  • あ、あと Emacs先日設定ファイルを一新してから popwin とか細かいところで少しこれまでの操作感と違う部分があるから、おって設定ファイルを見直そうと思う。

引き続き自炊してる

f:id:ryskosn:20180130234311j:plain

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

自炊するようになった

最近また自炊するようになった。これまでと少しスタンスを変えて、食材を一度にたくさん買わないようにした。当日か、最大でも翌日には使い切るくらいのものしか買わない。内容量によっては残ることもあるけれどあまり気にしない。

幸い自宅からスーパーまでそんなに離れていないので、実際に計測してみたら往復しても大して時間はかからないことがわかったし、むしろ運動になってちょうどよいのだと考えるように切り替えた。

今までは、ある程度まとめて買う方が効率的だろうと考えていたけれども、自炊スキルがあまり高くないこともあって、たくさん買おうとすると「用途のイメージがクリアではないけれども購入する」という部分の占める割合が大きくなっていたと思う。

そして「外食するかもしれないし、自炊するかもしれない」という曖昧な状態だと、自炊を選択しない場面も出てくるので使いきれないうちに傷んでしまったりして、しょんぼりした気持ちになったりする。

一方、今日はこれを作ろう、だからアレとアレを買おう、と明確に決まっている状態であれば売り場でも迷わないし、買って帰って作って食べる、とスムーズな進行が可能になる。言われてみれば当たり前だけどさ。

ただ、もう少し短時間で済ませられるようになりたいというのはある。

f:id:ryskosn:20180123235200j:plain

ダイソーのバックレストがよかった

これ、すごくよかった。このつくりで 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 は起動が遅いし実行権限の設定を変更しないとスクリプトを実行できなかったり、管理者用という感じの印象。

そういえば、WindowsUbuntu が動くやつ、まだ試したことないのだけれどどうなんだろう。

ノート PC の設定してた

(last update: 2018/01/22)

しばらく書いてなかったな。

先週末にノート PC の OS をインストールしなおしたのでセットアップしてた。 少し前に OS を high Sierra にアップデートしてみたら、Haskell の管理ツール stack が動かなくなったみたいで、これもよいきっかけと思いクリーンインストールしなおすことにした。

数年前から各種設定ファイルは ~/dotfiles にまとめて Github で管理しているので、ここから引っ張ってきてちょこちょこと作業すればいいのだけれど、今回は少し変更したいところもあったので設定ファイルを作り直すことにした。

変更したかったのは

  1. Cask での Emacs パッケージ管理をやめる
  2. Emacs の設定ファイル init.el を分割する
  3. $GOPATH$HOME/go ではなく $HOME にする
  4. zsh はもう使わない、fish だけでよい
  5. Ruby のインストールもいらないかな

などなど。

Emacs のパッケージ管理

これまでは Cask を使っていた。~/.emacs.d/Cask というファイルに使いたいパッケージを記入しておくと $ cask install とコマンド実行するだけで全部インストールしてくれるもの。

これはこれで便利だったけれど、use-package というパッケージを使えば、elisp のみでパッケージのインストールとその設定をまとめて記述することができて、外部コマンドを導入する必要もなくすっきりするので、変更してみたくなった。

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 先生の有料メルマガを購読して質問してみるのがいいかもしれない。

ripgrep 入れようとしたら

ripgrep 入れようとしたらスムーズにいかなかくて yak shaving 感あったと tweet したら作者の方からバイナリあるよ、とリプライいただいてびっくりした。

素直にダウンロードして /usr/local/bin に置いておいた。まだ使いこなせていないけれどせっかくだから使いたい。

Go の TOML ライブラリの作者として認識していた。すごい人はいろいろすごい。

fish の設定

昨年使い始めてから、もう二度と zshbash には戻れないと思わせる、とても便利なシェル fish。

今回は、使いたい関数は config.fish に直接書けばいいかなと思い、プラグインマネージャー fisherman は入れなかった。実際、ヒストリ検索と ghq 管理下のリポジトリcd するやつの 2 つがあれば満足かなと。

って思っていたのだけれど、z という最近 cd したディレクトリを列挙してくれるツールが快適そうに思えてきて、やっぱり fisherman 入れようかなと少し揺れているところ。

一回使ってみようかしらん。

そんなこんなで

設定ファイル init.el の内容を見直しつつ、分割して、書き直していった。一つ一つ調べながらやっていたら、結局一週間かかった。

Github にある既存のリポジトリをいずれ差し替えたいのだけれど、Git の操作に疎いのでひとまず bitbucket に置いた。(公開設定がプライベートになっていたので修正した)

README.md の更新もだいたい落ち着いたのでようやく一息つけるわ。

OCaml モジュールとシグネチャ

OCaml モジュールとシグネチャ

ソースファイル .mlコンパイルするとオブジェクトファイル .cmo.cmx .o と同時にインターフェイスファイル .cmi が生成される。

この .cmi について調べてみた。理解してしまえば何ということはないのだけれど、少し時間がかかった。

  • .mliコンパイルすると .cmi が生成される。
  • .mli がない場合は .ml から .cmi が生成される。

では .mli とは何だろう。

ではモジュールとは、シグネチャとは何だろう。

モジュールとは

詳細な解説は以下の記事、資料を読むとよさそう。

ただ、一度に全部を理解しようとするのはかなり困難だったので、ひとまずは「関連する処理やデータなどのまとまり」くらいの理解でよいと思う。Python や Go でいうパッケージと同じようなものだと考えればよいだろう。

OCaml では一つのファイルを一つのモジュールとするそう。前回書いた、階乗を求める処理でもモジュールとして扱っていた。

fact.mlFact モジュールとなる。

(* 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()

モジュールに別名をつけて読み込む

Pythonas で別名をつけて 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 だと名前の頭文字が大文字なら外部からアクセス可能で小文字だとアクセス不可、という仕様になっている。

で、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 プログラマが意図的にシグネチャを書かないというケースはなさそう(想像)だから、気にしなくていいんじゃないかな。

参照