Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

どんな劣悪環境でも負けないためにvimrcをダウンロードできるようにした。

世の中には「gitがインストールされていない」というおぞましい環境が存在します。弊学の演習室とかね。そういった、dotfilesを使うことすらできない劣悪環境でプログラミングしなければならないとき、せめてvimrcだけでも使えるようにしておこうと思って整備しました。

今回実装したもの

必要最低限の設定を詰め込んだ1ファイルのvimrcをダウンロードできるようにしました(僕向けにカスタマイズしてあるので他の人が使うことは前提にしておりません)。このファイルを~/.vimrcとして置くだけで最低限の快適さを得ることができます。

https://pandanoir.net/vimrc

こちらからダウンロードができるようになっています。GitHubのWebhooksを使って、dotfilesが更新されるたびに生成しているので常に最新の状態になっています。

仕組み

  1. dotfilesが更新されたらWebhooksが起動する
  2. 指定したURLにPOSTリクエストが飛ぶ
  3. Secret KeyをつかってGitHubからのリクエストか確認
  4. git pullする
  5. vimrcを1つのファイルにまとめる(普段は編集しやすいように複数ファイルに分かれている)

1. WebhooksにURLを登録する

まず、更新をチェックしたいリポジトリのSettingsに行きます(自分のリポジトリでないと設定できません)。すると、Webhooksという項目があるのでクリックします。

f:id:panda_noir:20190812174445p:plain

Add hookを押すと以下の画面になります。ここで入力したURLにPOSTが飛ぶので、好きなURLを設定してください。

Secret Keyは公式にも書いてありますが、ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'を使うなどして生成してください。

f:id:panda_noir:20190812174457p:plain

これでGitHub上の設定は終了です。

2. 受け取るプログラムを書く

今回はPHPで書きますが、他の言語でも処理の流れは変わりません。

<?php
$SECRET_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

$sig = array_key_exists('HTTP_X_HUB_SIGNATURE', $_SERVER) ? $_SERVER['HTTP_X_HUB_SIGNATURE'] : "";
$request_body = file_get_contents('php://input'); // シグネチャを確認するために必要
$hmac = "sha1=" . hash_hmac('sha1', $request_body, $SECRET_KEY);
if (!hash_equals($sig, $hmac)) {
    // ハッシュが一致していなかった
    echo "permission denied.";
    http_response_code(403) ;
    exit;
}
// ここで実行したい処理を行う
echo "successed!";

公式にはSecret Tokenをハードコーディングするなとありますが、めんどくさかったのでハードコーディングしました(ここ以外で使う予定も特になかったので)。

HMACを使ってhashを計算して一致しているか確認しているだけのコードです。

参考

俺のためだけのsedまとめ

迷ったときにとりあえずこれ読めば8割くらいわかるように書きました。

sedでできること

sedは受け取った文字列を加工するためのコマンドです。パイプラインやファイルから文字列を受け取って処理します。置換や削除などができます。四則演算はできません。

sedの使い方

sedコマンドにsedスクリプトを渡す方法は2通りあります。

  • 直接スクリプトを書く(sed -e 'sed-script')
  • sedスクリプトを書いたファイルを指定する(sed -f sed-file)

sedスクリプト

sedの基本動作は「条件を満たす行に対してコマンドを実行する」というものです。例えば「10行目を削除する」「3、6、9が含まれている行を削除する」といったことが可能です。

処理の全体の流れは次のようになります。

  1. 入力から1行読み込み、パターンスペースに格納する
  2. パターンスペース内の文字列にコマンドを実行する
  3. パターンスペースの内容を出力する(-nコマンドをつけていた場合は出力しない)

パターンスペースとはバッファのことです。

この流れを理解できていれば、次のコマンド意味がわかるはずです。

$ seq 1 5 | sed -e "2 a append
3 a append
4 a append"

アドレス

コマンドを実行したい行を、アドレスを用いて指定します。アドレスを指定せずにコマンドを実行すると、すべての行に対して実行されます。

アドレスは主に正規表現か、行番号で指定します。正規表現を指定した場合はパターンにマッチする行に対してコマンドが実行されます。他にも以下のような指定方法があります。

  • 正規表現
  • 行指定
  • 範囲指定(1,10など)
  • ステップつき範囲指定(3\~2なら、3行目以降を2行おきに指定する)

コマンド

sedで使用できるコマンドは以下のようなものがあります。

  • y/置換前/置換後/ 後述
  • s/ターゲット文字列/リプレース文字列/ ターゲット文字列をリプレース文字列で置き換えます。s/hoge/fuga/gというように、gフラグをつけることで一致するすべての箇所を置換できます。
  • d 行を削除します。
  • c テキスト テキストで行全体を置換をします。
  • a テキスト 指定位置の1行うしろにテキストを追加します(例: 3 a appendを実行すると3行目にappendという行が追加される)
  • i テキスト 指定位置にテキストを追加します(例: 3 i insertを実行すると3行目にinsertという行が追加される)

yコマンドはすこし説明が難しいので、例を交えながら説明します。

たとえば、Aをaに、Bをbに変えたいとします。そのとき、sedではsed -e 's/A/a/g' | sed -e 's/B/b/g'とする代わりにyコマンドを用いてsed -e 'y/AB/ab/'と書くことができます。

このように、ある文字と別の文字の対応づけをする(つまり換字を行う)コマンドがyコマンドになります。

アドレス例

$ seq 1 5 | sed -e " 3 d" #3行目を削除
1
2
4
5
$ seq 1 5 | sed -e "1~2 d" #1行目以降を、2行おきに削除
1
3
5
$ seq 1 5 | sed -e "2,4 d" #2行目から4行目を削除
1
5
$ seq 1 5 | sed -e "/[369]/ d" #3,6,9が含まれる行を削除
1
2
4
5

コマンド例

$ seq 1 5 | sed -e " 3 y/12345/abcde/" #1をa、2をb…とういふうに変換
a
b
c
d
e
$ seq 1 5 | sed -e " 3 y/15/51/" #1と5を入れ替える
5
2
3
4
1
$ seq 1 5 | sed -e "3 i insert" #3行目の位置にinsertと挿入
1
2
insert
3
4
5
$ seq 1 5 | sed -e "3 a append" #3行目の後ろにappendと追加
1
2
3
append
4
5
$ seq 1 5 | sed -e " 3 c hoge" 3行目をhogeで置き換える
1
2
hoge
4
5

ここまでは見やすさのためにアドレスとコマンドの間にスペースを入れていましたが、sed -e "3choge"のようにスペースを抜いてもきちんと動作します。

実はVimの置換はSedだった

実はVimの:s/target/replacement/というコマンドはSedと同じように書くことができます(相違点も多いのでVim版Sedくらいに捉えておくと扱いやすいです)。

  • :1,9/hoge/fuga/ 1行目から9行目のhogeをfugaに置換する
  • :/1st/s/hoge/fuga/ 1stが含まれる行を探して、hogeをfugaに置換する。ただし、複数の行に対して置換を行わない模様。
  • :/OMG/d OMGが含まれる行を削除する

参考

そうだ、明日京都行こう。

f:id:panda_noir:20190807161220j:plain

京 都 行 き た い ! ! ! ! !

…失礼しました。授業で「複数のWeb APIを組み合わせてオリジナルのサービスを作ってみましょう!」という演習があって、それで作ったサービスを公開したので紹介します。

https://pandanoir.net/webcomp

(元となったアイデア)

サービスの概要

明日、全国のどこかで開催されるイベントを紹介してくれます。サービス名は「そうだ、明日京都へ行こう。」ですが、京都以外のイベントも表示されます。駅すぱあとで行き方をすぐ見られるので、「明日どっかで遊びたい!!!」となったら0秒で予定を立てることができます。

こんな感じでアクセスするとタイムテーブルと経路情報、イベント情報が表示されます。

f:id:panda_noir:20190807160059p:plain

要するにクソサービスです。

使用したAPI

  • Eventon API(イベント情報の取得)
  • 駅すぱあとAPI フリープラン(経路情報の取得)

あと、駅データ.jpから駅データをもらってきて最寄り駅の計算などをしています。

使用技術

  • React(create-react-app)
  • SCSS
  • Nginx
  • PHP

外部のAPIを呼び出すだけなので、データベースすら使わずに構築しました。Hooksを使ってガリガリ実装してみましたが、Hooks楽しいですね。Reactがすごく簡潔になるのでいい感じです。

create-react-appもこのくらいのサイズ感なら環境構築の手間がなくなるメリットのほうが大きいのでいいですね。ただ、watchができなくていちいちbuildしないといけないのはBadですが…

最近の状況

ここのところブログの更新をサボっていたので近況報告でお茶を濁そうと思います。生存報告とかいう動画を投稿するYouTuberっぽいですね。

研究室とか

現在大学4年生で、今年の4月に研究室に配属になりました。前期のあいだは教科書の輪講を主にやっていました。

それから、前期は部活のほうにも週に2、3回顔を出していました。プログラミングと部活の両立はさすがに難しくてプログラミングのほうがおざなりになっていました…

あとは朝バイトに週3で入っていて若干体調が崩れてきています。なんとか立て直したい…

競プロ

4月からはじめた競プロですが、このあいだセールをしていたので螺旋本を購入しました。今は半分くらいまで終わりました。

ここのところ思っているのが、競プロは数学とかなり似ているな、ということです。発想力がなければ解けない問題は、数学と同じで「この問題、あの問題の解法で行けそうじゃないか?」というくらいまで量をこなし知識をつけるしかない気がします。そのため、ABCのDを埋める作業を螺旋本と並行してやっています。センスがある人はぱっと解法が浮かぶのでしょうが、僕にはムリです…

彼女ほしい

ああああああああああああああああああああああああああああああああああああああああああああ彼女ほしいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいい

スターリンソートをJSで実装してみた

スターリンソートなる、「整列されていない要素を"粛清"することでO(N)でソートを実現する」というネタが盛り上がっているようなので、便乗して書きました。

実装

リストを先頭から見ながら暫定の最大値を持っておきます。その最大値よりも小さい値があったら、それは整列されていない要素なので結果に加えないこととします。

const stalinSort = arr => {
    if (arr.length == 0)
        return [];
    let max = arr[0]; // 暫定の最大値
    const res = [arr[0]];
    for (const item of arr) {
        if (max < item) {
            max = item;
            res.push(item);
        }
    }
    return res;
};

スターリンソートの開始位置を変えてみる

このスターリンソートは、[100, 1, 2, 3, 4, 5]のように最大値が先頭に来てしまうと残りの要素がすべて粛清されてしまうという問題点があります(他にもあるやろ!)。そこで、開始位置を変えることで、得られる配列の長さを最大化してみようと思います。

これは「最長部分増加列」を求める問題と同一になります。ちなみに、最長部分増加列を求めるにはO(NlogN)かかるのでスターリンソートのメリットがなくなります。本末転倒ですね。

const bound = comp => (arr, key) => {
    let s = 0, t = arr.length;
    while (t - s > 1) {
        const mid = s + Math.floor((t - s) / 2);
        if (comp(arr[mid], key))
            s = mid;
        else
            t = mid;
    }
    return comp(arr[s], key) ? t : s;
};
const lower_bound = bound((a, b) => a < b);
const upper_bound = bound((a, b) => a <= b);

const longestStalinSort = arr => {
    const dp = [];
    for (const item of arr)
        dp[lower_bound(dp, item)] = item;
    return dp;
};
console.log(longestStalinSort([100,1,2,100,3,4,5]));
console.log(longestStalinSort([1,3,5,2,4,6]));