2015-08-27

xkcd 1566と1567

xkcd: Board Game

ボードゲーム

「そして、この山は控除可能カードですね。手札と組み合わせて得点の維持をします」
「そしてこっちは扶養者トークン」

毎年、近所のボードゲームクラブをだまくらかして税金の計算をさせている。

いいアイディアだ。

xkcd: Kitchen Tips

キッチンのコツ

みなさんは、私と同じように、肉が焼けたかどうか判断が着かないことがあるでしょう。

勘に頼るのではなく、サーモメーターを使いましょう。

みなさんは、私と同じように、汚れたお皿とかグラスを投げ捨てていることでしょう。でも、洗えば、再利用できるのです。

スクランブルエッグを作るときは、フライパンを間にはさみましょう。

作るのが簡単になりますし、コンロも汚れません。

みなさんは、私と同じように、冷凍庫に水をぶちまけてからドアを勢い良く占めることで氷を作っていることでしょう。

でも、もっといい方法があるのです。

どうも、馬鹿馬鹿しいテレビ広告手法を皮肉っているらしい。

2015-08-19

江添ボドゲ会@8月の案内

江添ボドゲ会兼引越し祝い@8月 - connpass

引越がようやく落ち着いたので、引越し祝いを兼ねて、自宅でボドゲ会を開催する。

今回は会場にたどり着くところからゲームだ。最寄り駅から1kmほど離れている。

江東区塩浜2-14-2 アライハウスⅡ 101号室

最寄り駅は東西線木場駅。都営バスならば業10か錦13が塩浜二丁目に止まります。

木場駅4b出口から出て南下。イトーヨーカドーを通り過ぎ、中央自動車学校と歩道橋がある塩浜通りで東に進み、サンクスとすき家に挟まれた道を南下し、木場リサイクルの向かい側のマンションの一階の101号室。

入り口のインターホンで101号室を呼び出してください。

2015-08-15

ボルダリング

ボルダリングを始めてからすでに9ヶ月が過ぎた。ボルダリングの技量は徐々に向上している実感がある。

ボルダリングを始めてから、タランチュラとソリューションで壊していたが、今使っているジーニアスは三ヶ月たっても壊れる気配がない。いい靴だ。壊したクライミングシューズは二足とも左足のつま先のソールが剥がれてしまった。足の使い方が悪いからだと指摘されたが、なんとも解せない。

ボルダリングを始めた頃は液体チョークを使っていたが、今は粉チョークに切り替えた。数ヶ月ほどして、ようやくチョークボールを一個使い切った。摩擦力の性能としては、液体チョークのほうが優れているように思う。

今日は久しぶりに錦糸町のT-Wallに行ってきた。一階の水色課題をこなそうとしたが、いくつかできないものもあった。

また、たまたまズボンがやすかったので、今のズボンがだいぶ傷んでいることもあり、購入した。

ロープもしたいものだが、あいにくとなかなかビレイをするパートナーに恵まれないものだ。

ところで、錦糸町のT-Wallのアイシング用の氷を入れてある冷凍庫に、「関節炎、腱鞘炎を抱えながらトレーニングを続ける人を優先してください」と書いてあり、何やら闇を感じた。

2015-08-14

MLからC++テンプレートへのコンパイラー

Evil ML: ML to C++ template language

akabe/evilml

MLからC++テンプレートメタコードへのコンパイラーが公開されている。

以下のMLにおけるクイックソートが、

(* Example: quick sort *)

type 'a list = [] | :: of 'a * 'a list

let rec foldr f xs acc = match xs with
  | [] -> acc
  | x :: xs -> f x (foldr f xs acc)

let length xs = foldr (fun _ acc -> acc + 1) xs 0
let append xs ys = foldr (fun y acc -> y :: acc) xs ys
let filter f xs = foldr (fun x acc -> if f x then x :: acc else acc) xs []

let rec qsort xs = match xs with
  | [] -> []
  | [x] -> [x]
  | pivot :: rest ->
    let ys = qsort (filter (fun x -> x < pivot) rest) in
    let zs = qsort (filter (fun x -> x >= pivot) rest) in
    append ys (pivot :: zs)

let rec nth i xs = match xs with
  | [] -> error
  | x :: xs -> if i = 0 then x else nth (i-1) xs

let l1 = [5; 4; 8; 1; 6; 3; 7; 2]
let l2 = qsort l1
let x0 = nth 0 l2
let x1 = nth 1 l2
let x2 = nth 2 l2
let x3 = nth 3 l2
let x4 = nth 4 l2
let x5 = nth 5 l2
let x6 = nth 6 l2
let x7 = nth 7 l2

(*!
// This is C++ code.

#include <cstdio>

int main () { // We use printf in order to output readable assembly code.
  std::printf("%d  ", x0::val);
  std::printf("%d  ", x1::val);
  std::printf("%d  ", x2::val);
  std::printf("%d  ", x3::val);
  std::printf("%d  ", x4::val);
  std::printf("%d  ", x5::val);
  std::printf("%d  ", x6::val);
  std::printf("%d\n", x7::val);
  return 0;
}
*)

以下のような素敵なC++テンプレートメタコードに変換される。

#include "evilml.hpp"


struct __ml_nil {
  static const int tag = 1000123519;
};

template <class x0, class x1>
struct __ml_cons {
  static const int tag = 478463344;
  typedef x0 fst;
  typedef x1 snd;
};

struct foldr {
  template <class f, class xs, class acc>
  class fun {
  private:
    struct __then2 {
      template <class>
      struct fun {
        static const int tag = 0;
        typedef acc type;
      };
    };
    struct __else2 {
      template <class>
      class fun {
      private:
        struct __then1 {
          template <class>
          class fun {
          private:
            typedef typename xs::fst x;
            typedef typename xs::snd xs1;
          public:
            static const int tag = 0;
            typedef typename
              f::template fun
                <x, typename foldr::template fun<f, xs1, acc>::type>::type type;
          };
        };
        struct __else1 {
          template <class>
          struct fun {
            static const int tag = 0;
            typedef void type;
          };
        };
      public:
        static const int tag = 0;
        typedef typename
          __ml_if<(xs::tag == 478463344), __then1, __else1>::type::
            template fun<void>::type type;
      };
    };
  public:
    static const int tag = 0;
    typedef typename
      __ml_if<(xs::tag == 1000123519), __then2, __else2>::type::template fun
        <void>::type type;
  };
};

struct length {
  template <class xs>
  class fun {
  private:
    struct __fun1 {
      template <class, class acc>
      struct fun {
        static const int tag = 0;
        typedef __ml_int<(acc::val + 1)> type;
      };
    };
  public:
    static const int tag = 0;
    typedef typename
      foldr::template fun<__fun1, xs, __ml_int<0> >::type type;
  };
};

struct append {
  template <class xs, class ys>
  class fun {
  private:
    struct __fun2 {
      template <class y, class acc>
      struct fun {
        static const int tag = 0;
        typedef __ml_cons<y, acc> type;
      };
    };
  public:
    static const int tag = 0;
    typedef typename foldr::template fun<__fun2, xs, ys>::type type;
  };
};

struct filter {
  template <class f, class xs>
  class fun {
  private:
    struct __fun3 {
      template <class x, class acc>
      class fun {
      private:
        struct __then3 {
          template <class>
          struct fun {
            static const int tag = 0;
            typedef __ml_cons<x, acc> type;
          };
        };
        struct __else3 {
          template <class>
          struct fun {
            static const int tag = 0;
            typedef acc type;
          };
        };
      public:
        static const int tag = 0;
        typedef typename
          __ml_if<f::template fun<x>::type::val, __then3, __else3>::type::
            template fun<void>::type type;
      };
    };
  public:
    static const int tag = 0;
    typedef typename foldr::template fun<__fun3, xs, __ml_nil>::type type;
  };
};

struct qsort {
  template <class xs>
  class fun {
  private:
    struct __then8 {
      template <class>
      struct fun {
        static const int tag = 0;
        typedef __ml_nil type;
      };
    };
    struct __else8 {
      template <class>
      class fun {
      private:
        struct __then7 {
          template <class>
          class fun {
          private:
            typedef typename
              xs::fst x;
            struct __then6 {
              template <class>
              struct fun {
                static const int tag = 0;
                typedef __ml_cons<x, __ml_nil> type;
              };
            };
            struct __else6 {
              template <class>
              class fun {
              private:
                struct __then5 {
                  template <class>
                  class fun {
                  private:
                    typedef typename xs::fst pivot;
                    typedef typename
                      xs::snd rest;
                    struct __fun4 {
                      template <class x>
                      struct fun {
                        static const int tag = 0;
                        typedef __ml_bool<__ml_lt<x, pivot>::val> type;
                      };
                    };
                    typedef typename
                      qsort::template fun
                        <typename filter::template fun<__fun4, rest>::type>::
                        type ys;
                    struct __fun5 {
                      template <class x8>
                      struct fun {
                        static const int tag = 0;
                        typedef __ml_bool<__ml_ge<x8, pivot>::val> type;
                      };
                    };
                    typedef typename
                      qsort::template fun
                        <typename filter::template fun<__fun5, rest>::type>::
                        type zs;
                  public:
                    static const int tag = 0;
                    typedef typename
                      append::template fun<ys, __ml_cons<pivot, zs> >::type type;
                  };
                };
                struct __else5 {
                  template <class>
                  struct fun {
                    static const int tag = 0;
                    typedef void type;
                  };
                };
              public:
                static const int tag = 0;
                typedef typename
                  __ml_if<(xs::tag == 478463344), __then5, __else5>::type::
                    template fun<void>::type type;
              };
            };
          public:
            static const int tag = 0;
            typedef typename
              __ml_if<(xs::snd::tag == 1000123519), __then6, __else6>::type::
                template fun<void>::type type;
          };
        };
        struct __else7 {
          template <class>
          class fun {
          private:
            struct __then4 {
              template <class>
              class fun {
              private:
                typedef typename xs::fst pivot;
                typedef typename
                  xs::snd rest;
                struct __fun6 {
                  template <class x9>
                  struct fun {
                    static const int tag = 0;
                    typedef __ml_bool<__ml_lt<x9, pivot>::val> type;
                  };
                };
                typedef typename
                  qsort::template fun
                    <typename filter::template fun<__fun6, rest>::type>::type ys;
                struct __fun7 {
                  template <class x10>
                  struct fun {
                    static const int tag = 0;
                    typedef __ml_bool<__ml_ge<x10, pivot>::val> type;
                  };
                };
                typedef typename
                  qsort::template fun
                    <typename filter::template fun<__fun7, rest>::type>::type zs;
              public:
                static const int tag = 0;
                typedef typename
                  append::template fun<ys, __ml_cons<pivot, zs> >::type type;
              };
            };
            struct __else4 {
              template <class>
              struct fun {
                static const int tag = 0;
                typedef void type;
              };
            };
          public:
            static const int tag = 0;
            typedef typename
              __ml_if<(xs::tag == 478463344), __then4, __else4>::type::
                template fun<void>::type type;
          };
        };
      public:
        static const int tag = 0;
        typedef typename
          __ml_if<(xs::tag == 478463344), __then7, __else7>::type::
            template fun<void>::type type;
      };
    };
  public:
    static const int tag = 0;
    typedef typename
      __ml_if<(xs::tag == 1000123519), __then8, __else8>::type::template fun
        <void>::type type;
  };
};

struct nth {
  template <class i, class xs>
  class fun {
  private:
    struct __then11 {
      template <class>
      struct fun {
        static const int tag = 0;
        typedef void type;
      };
    };
    struct __else11 {
      template <class>
      class fun {
      private:
        struct __then10 {
          template <class>
          class fun {
          private:
            typedef typename xs::fst x;
            typedef typename
              xs::snd xs1;
            struct __then9 {
              template <class>
              struct fun {
                static const int tag = 0;
                typedef x type;
              };
            };
            struct __else9 {
              template <class>
              struct fun {
                static const int tag = 0;
                typedef typename
                  nth::template fun<__ml_int<(i::val - 1)>, xs1>::type type;
              };
            };
          public:
            static const int tag = 0;
            typedef typename
              __ml_if<(i::val == 0), __then9, __else9>::type::template fun
                <void>::type type;
          };
        };
        struct __else10 {
          template <class>
          struct fun {
            static const int tag = 0;
            typedef void type;
          };
        };
      public:
        static const int tag = 0;
        typedef typename
          __ml_if<(xs::tag == 478463344), __then10, __else10>::type::
            template fun<void>::type type;
      };
    };
  public:
    static const int tag = 0;
    typedef typename
      __ml_if<(xs::tag == 1000123519), __then11, __else11>::type::
        template fun<void>::type type;
  };
};

typedef __ml_cons
  <__ml_int<5>, __ml_cons
     <__ml_int<4>, __ml_cons
        <__ml_int<8>, __ml_cons
           <__ml_int<1>, __ml_cons
              <__ml_int<6>, __ml_cons
                 <__ml_int<3>, __ml_cons
                    <__ml_int<7>, __ml_cons<__ml_int<2>, __ml_nil> > > > > > > > l1;

typedef qsort::fun<l1>::type l2;

typedef nth::fun<__ml_int<0>, l2>::type x0;

typedef nth::fun<__ml_int<1>, l2>::type x1;

typedef nth::fun<__ml_int<2>, l2>::type x2;

typedef nth::fun<__ml_int<3>, l2>::type x3;

typedef nth::fun<__ml_int<4>, l2>::type x4;

typedef nth::fun<__ml_int<5>, l2>::type x5;

typedef nth::fun<__ml_int<6>, l2>::type x6;

typedef nth::fun<__ml_int<7>, l2>::
  type x7;

// This is C++ code.

#include <cstdio>

int main () { // We use printf in order to output readable assembly code.
  std::printf("%d  ", x0::val);
  std::printf("%d  ", x1::val);
  std::printf("%d  ", x2::val);
  std::printf("%d  ", x3::val);
  std::printf("%d  ", x4::val);
  std::printf("%d  ", x5::val);
  std::printf("%d  ", x6::val);
  std::printf("%d\n", x7::val);
  return 0;
}

ドワンゴ広告

この記事はドワンゴ勤務中に見つけたのでメモ代わりに書いた。

ドワンゴ社内に関数型プログラマーが何人いるかは把握していない。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-08-13

Emscriptenがpthreadを実験的にサポート

Emscripten gains experimental pthreads support! - Google Groups

Emscrptenがpthreadを実験的にサポートしたそうだ。

JavaScript上でスレッドを実装する上で障害になるのは、スレッドに相当する方法が存在しないということだ。Web Workerは実行媒体ごとに隔離されて、メッセージパッシングで他の実行媒体と通信をする仕組みになっている。これは同じメモリに複数の実行媒体からアクセスするというスレッドとは異なり、プロセスに近い。

現在、JavaScript上で共有メモリを扱えるようにしようというドラフト規格、SharedArrayBufferが議論中であり、Firefox Nightlyが実験的実装を進めている。この機能を用いて、Emscriptenでpthreadを実験的に実装したそうだ。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-08-12

Lenovoのファームウェアがファイルシステムを改ざんするクソ仕様なので絶対に使ってはいけない

最近のLenovoのBIOSのアップデートに以下のものがある。

Lenovo Newsroom | Lenovo Statement on Lenovo Service Engine (LSE) BIOS

この脆弱性はLenovoの一部の顧客用PCにインストールされているBIOS中に存在するMicrosoft Windows機構に関与する機能、Lenovo Service Engine(LSE)に関連したものである。

などと抽象的でわけのわからない文面で脆弱性の説明と修正した旨が案内されている。では具体的にどんな脆弱性だったのか。驚くべきバカなことが行われていた。

Lenovo G50-80 dialog box - Ars Technica OpenForum

Windows 7か8をブートする前に、BIOSはC:\Windows\system32\autochk.exeがLenovoのものかMicrosoftオリジナルのものかどうかを調べ、Lenovoのものでなければ、オリジナルはC:\Windows\system32\0409\zz_sec\autobin.exeに移動され、独自のautochk.exeが書き込まれる。

ブート中に、Lenovoのautochk.exeは、LenovoUpdate.exeとLenovoCheck.exeをsystem32ディレクトリに書き込み、インターネット接続が行われた場合に、どちらかをサービスとして実行する。これが何をしているのかについて詳しくは知らないが、この内の一つは、http://download.lenovo.com/ideapad/windows/lsebios/win8_en-us_32_oko.jsonを読み込むらしい。これはsslを使っていないし、"ForceUpdate"というパラメーターがあることから、どうも怖い。通信を改変できる誰からでも(public wifiとか)任意のコードを実行可能な脆弱性として利用できそうだ。

アホか? Lenovoは脳の髄までアホなのか? なぜBIOSがファイルシステムの、しかもOSの重要なファイルを改変しているのだ? Windowsのアップデートやアップグレードで動かなくなることは必然として、悪意すら感ずるほどのスガスガしいマヌケっぷりだ。

Lenovoがこれを実装するのに必要な技術者の誰か一人でも、「俺の屍を越えて行け」という態度を取らなかったのは驚くべきマヌケっぷりだ。Lenovoは上から下までマヌケしかいないか。

Lenovo製品を使うのは大いにセキュリティ上のリスクだ。絶対に使ってはいけない。

Lenovo quietly installed software on laptops, even if you wiped it

なお、Lenovoが利用していたのはMicrosoft公認(Lenovoの脆弱性が発覚してから利用規約が修正された)の機能で、もともとは盗難防止プログラムなどの、Windowsの再インストールを経ても維持されるソフトウェアをPCベンダーが作りたいときに使えるWindows Platform Binary Tableという機能だったらしい。

2015-08-09

引越しの片付けがようやく終わりそうだ

先月引越しをしたのだが、色々と日常の雑事に追われていて、部屋の片付けがなかなかできなかった。この週末は一念発起して残りのダンボールの山を片付けた。ようやくリビングが空いたので、これでボドゲ会が開けるようになった。

最後まで片付かなかった荷物は紙の本だ。しかたなく本棚を買った。

また、今回、事務用の折り畳み机と折りたたみ椅子を買ってみた。人が来た時に使え、普段は邪魔にならない家具が欲しかったので、あまりご家庭にあるものではないが、思い切って買ってみた。使ってみるとこれがなかなか具合がよい。値段が安いのもいい。ちょっとしたおしゃれな机と椅子は何万もする。事務用の折りたたみ椅子と机は安い上にはるかに機能的だ。

さて、家の中が片付いたので、近々ボドゲ会や引越し祝いのパーティを開きたいものだ。

2015-08-07

GCCのconceptブランチがtrunkにマージされる

Jason Merrill - Huge C++ PATCH to merge c++-concepts branch

GCCのconceptブランチがtrunkにマージされるようだ。

現在、Concept TSとして提案中のコンセプトは、C++11に提案されていたコンセプトとは違い軽量版で、コンセプトチェック機能のみを提供している。

ドワンゴ広告

今年の8月14日は、とある理由により有給を取得するドワンゴ社員が多そうだ。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-08-04

C++参考書、C++11/14 コア言語を9月に出版予定

C++11/14のコア言語の参考書。「C++11/14コア言語」が出版社ドワンゴ、販売KADOKAWAとして、9月に出版される。内容は、以下を書籍にしたものだ。内容としてはすでにすべて読むことができる。

EzoeRyou/cpp-book

EzoeRyou/cpp14-appendix

アマゾンには情報が掲載されたようだ。

Amazon.co.jp: C++11/14 コア言語: 江添 亮: 本

紙の本の他にも、KindleとepubとPDF版も現時点で出す予定だ。また、もちろん本のソースコードも公開される。

ソースコードは、Markdown形式だ。もともとこの本はXHTMLで手書きされたが、それでは扱いづらいため、これを一度Markdownに変換した上で修正し、pandoc経由でtexに変換してから各フォーマットを生成している。不自由なフォント以外のソースコードは公開できるだろう。

本の帯に書く短い説明として、「他の解説書ではわからないC++コア言語の詳細を不必要なまでに解説」などと冗談で言ったら、本当に採用されてしまった。本の内容は、通常C++を書くのであれば、まず書かないだろうコードが合法かどうかや、その意味まで解説している。規格としては、コードは合法か違法かを規定しなければならず、また合法の場合は、その挙動も規定しなければならないからだ。

例えば、クラスやenumは名前は、変数やデータメンバーの名前によって隠されるという規程がある。

class ClassName {} ;

void f()
{
    ClassName ClassName ; // OK、ClassName型の変数、ClassName

    ClassName x ; // エラー、ClassNameは、ここでは変数名を指す。

    class ClassName x ; // OK、明示的にクラス名であると指定している。
}

クラス名を変数名で隠すこのようなコードは、現実ではお作法的に推奨できないが、規格上は合法である。

この参考書は、このような些細な、現実的な実務で使うコードには推奨できないような例も多い。規格上は興味深いが、それを知っても日々のコーディング力にはあまり貢献しないような些細な詳細が書かれている。

ドワンゴ広告

出版社はKADOKAWAとなっているが、販売がkADOKAWAで、出版はドワンゴが行う。組版などの作業はドワンゴ社内で行われた。編集者は私の席のとなりに座っていたため、極めてやりやすい。しかし、出版社が私の雇用主で、編集者が同僚というのはなかなかない環境ではなかろうか。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2015-08-02

佐川急便のWeb上の再配達受付がクソすぎる件

アマゾンで家具を注文したのだが、土曜日の不在中に届けに来たようで、不在届が入っていた。そこで。再配達で明日の時間を指定して受け取れるようにしようと試みた。その結果、佐川急便のWeb上の再配達申し込みは、この2015年にありえないほどクソであることが明らかになった。

私は電話は持っていないし、10桁以上もある受付番号やら何やらを電話越しに番号でブッシュするのも面倒なので、Webサイト上で申し込むようにした。

ところが、このWebサイト上での申し込みが、非常にクソだったのだ(2015-08-02現在)

まず、電話による従来の再配達の手順を書いてみる。

  1. 受付用の電話番号に電話する
  2. 自分の電話番号をプッシュ信号で通知する
  3. 受付番号をプッシュ信号で通知する
  4. 再配達希望日時をプッシュ信号で通知する

プッシュ信号経由で合計25個ほど数字を通知する必要がある。これはだるいが、技術上の制約上いかしかたないことだ。

ではWeb上で再配達を申し込む場合、どうなるのか。

まず、ユーザー登録をしなければならない。メールアドレスを入力し、やってきたメールに示されたURLにアクセスして、利用規約に同意し、ユーザーIDとパスワードと苗字と名前とそのカナ表記と生年月日と性別と郵便番号と住所とその他諸々の入力をしなければならない。受付番号を入力して再配達を申し込むのは、このユーザー登録をしてからだ。

利用規約はある程度長いが、第三者と個人情報を共有するような罠条項は一見したところないようだ。

さて、あまりにも入力すべき個人情報が多すぎる。名前はまだわかるが、なぜ生年月日や性別や住所まで入力しなければならないのか。電話による従来の再配達に比べて、入力すべき項目があまりにも多すぎる。生年月日や性別といった個人情報を収集する意図がわからないし、電話による再配達受付では住所など入力する必要がない。

さて、この住所がクセモノだ。なぜか正しく入力したのにバリデーションがはねられる。一体なぜだろうか。住所を正しく入力せよ以外のメッセージはない。私は正しくUTF-8としてデコードできるバイト列使っているはずだし、クロスサイトスクリプティングやSQLインジェクションを引き起こすような文字列を入力してもいない。ここで詰まってしまった。なぜ入力が受け付けられないのか。

しかし、ふとみると、入力欄には注意書きとして、(全角20文字以内)という文字列があるではないか・・・まさか。

筆者はASCIIに含まれる文字をすべて似たような意味のUnicodeで置き換えてみた(1を1にするような変換)。すると住所のバリデーションが通った。

アホか? アホなのか?

そして、次には、メール通知のチェックボックス。メール通知など要らないので、全て外そうとすると、どれかひとつはメール通知を受け取るにしなければならないというバリデーションエラーが。

この時点で、筆者は登録を辞めた。佐川急便のWeb上の再配達受付の設計、実装は100万人月で行われたとしか思われない。信じられないクソさだ。

最近、アマゾンは佐川急便を使うようになったそうだが、だいぶ不便だ。もうアマゾンを使うのが面倒になった。