輪番停電の夜

我が家の最寄り駅の駅舎から外に出ると、そこはいつもの真夜中より暗い街だった。輪番停電の割り当て日だったのは知っていたけど、実施されるのかは確認していなかったので少し驚く。
駅前のバス乗り場は、駅から漏れる明かりでまだ明るいのだが、警察官--あるいは乗客誘導のバス会社社員か--の持っている懐中電灯の光で、逆に暗さが強調されてるように感じる。
住宅地の中へ続く細い路地へ入っていく。真っ暗闇に飲み込まれるんじゃないかという不安感と期待。
街灯も自動販売機の明かりもない街は、思いのほか明るかった。辺りは昔の映画の夜--昼間の風景にフィルタをかけて暗くしている--のように見える。まったくの暗闇ではないにしても、もう少し暗いものだと思っていたので軽く失望する。この光はどこから来るのだろうと空を見上げると、雲や濁った大気が停電していない街の明かりで鈍く光っている。あの光がさらに反射した光で、こんなにはっきり見えるものなのだろうか?
どこからともなく甘い匂い。「焼〜き芋」。視線を落とすと焼き芋屋が、暗い街を白熱電球でこうこうと照らしている。明るい。わきを通り過ぎると、この場所での集客に見切りをつけたのか移動を始める。私を追い抜きさらに狭い路地へ入っていく。路地の中へ遠ざかっていく焼き芋屋は、暗闇の中にぽっかり浮いているように見える。絵本の1ページかアニメの1シーンか、どこかで見たような、絵に描いたような幻想的な光景。
少し広い通りに出て、コンビニの前を通りかかると「通常通り営業してまーす」真っ暗な中、懐中電灯を持って呼び込みをしている。自家発電のコストを上乗せしても儲かるんかね。がんばれよー、と心でエールを送りつつ家路を急いだ。

Hatena.Star.User.getProfileIcon

ちょっと前に、はじめてはてなアイデアにてお願いをしてみた。
はてなアイデア
で、しばらくは何も起こらないで「まあ、そんなもんか」と忘れかけてたら、いつの間にか希望がかなえられていたんだけど、再度いつのまにか元に戻っていた。
HatenaStar.js 内の Hatena.Star.User.getProfileIcon()の当該部分

var iconName = /\@/.test(name) ? 'sample' : name;
var pre = iconName.match(/^[\w-]{2}/)[0];
img.src = 'http://www.st-hatena.com/users/' + pre + '/' + iconName + '/profile_s.gif';

が↓

var pre = name.match(/^[\w-]{2}/)[0];
img.src = 'http://www.st-hatena.com/users/' + pre + '/' + name + '/profile_s.gif';

これは意図しての事なのか、バージョン管理の失敗なのか…。こういう場合、もう一度はてなアイデアにお願いすべきなの?

Togetter を読みやすくする(ような気がする)スクリプト

この頃よく Togetter を見るんだけど、普通のサイトを読むより1.6倍くらい疲れるような気がするので、自分で読みやすいフォーマットに書き換えるスクリプトを書いた。
具体的には、同一人物の連続した tweet を一まとめにして、それぞれの tweet がパラグラフのように見えるようにした。
使ってみたところ、効果は微妙だけど一応公開しとく。要 firefox & greasemonkey

// ==UserScript==
// @name           merge_tweet_on_togetter
// @namespace      http://d.hatena.ne.jp/t-sat/
// @description    連続する同一人物のtweetをひとまとめにする。
// @include        http://togetter.com/li/*
// ==/UserScript==

/* tweet_box DOM 操作関連 */
function getTweet(entry, material){
	var tweet = document.evaluate(".//div[@id]", entry, null, 9, material);
	return tweet.singleNodeValue;
}

function getAuthor(entry, material){
    var author = document.evaluate("string(.//img[@class='twttrimg']/@alt)", entry, null, 2, material);
	return author.stringValue;
}

function mergeItem(capital, county, material){
	var message = getTweet(county);
	
	county.parentNode.removeChild(county);
	capital.appendChild(message);
}

function group(entries, index){
	var entry = function(index){
		return entries.snapshotItem(index);
	}
	try{
		var i = 1;
		while(getAuthor(entry(index)) == getAuthor(entry(index + i))){
			mergeItem(entry(index), entry(index + i));
			i++;
		}
		return index + i;
	}catch(e){
		return null;
	}
}

function relocation(contentBox){
	var i = 0;
	do{
		var i = group(contentBox, i);
		i = i;
	}while(i);
}

/*tweet_box stylesheet 関連 */
var T_BOX_STYLE = "\
.list_body{float:right !important}\
.list_body .tweet a[href^='http://twitter.com']{opacity: .2 !important}\
.list_body:last-child .status{display:block !important}\
.list_body .status{display:none !important}\
.list_body:hover .status{display: block !important}"

function setMyStyle(){
	var styleElem = document.createElement("style");
	styleElem.id = "tweetBoxStyle";
	styleElem.textContent = T_BOX_STYLE;
	document.body.appendChild(styleElem);
}

function resetMyStyle(){
	var styleElem = document.getElementById("tweetBoxStyle");
	document.body.removeChild(styleElem);
}

/*UI 関連 */
var UI_STYLE ="\
#UISpace{\
	background: rgba(255,255,100,0.6) ;\
	border-left: 5px solid #FFF;\
	height: 5em ;\
	width: 5em ;\
	top: 40% ;\
	right: -3.5em ;\
	position: fixed ;\
}\
#UISpace:hover{\
	background: #FFA ;\
	right: 0em ;\
}"

var UIStyle = document.createElement("style");
UIStyle.textContent = UI_STYLE;
UIStyle.id = "UIStyle";
document.body.appendChild(UIStyle);

var UISpace = document.createElement("div");
UISpace.id = "UISpace"
UISpace.textContent = "Switch!";
UISpace.addEventListener("click", switchState, false);
document.body.appendChild(UISpace);


var list_boxes = document.evaluate('//div[@class="tweet_box"]//div[@class="list_box"]',document,null,7,null);
var originalTree;
var modifiedTree;
var modified = false;

function switchState(){
	if(!originalTree){
		originalTree = document.evaluate("//div[@class='tweet_box']/ul", document,null,9,null).singleNodeValue.cloneNode(true);
		relocation(list_boxes);
		modifiedTree = document.evaluate("//div[@class='tweet_box']/ul", document,null,9,null).singleNodeValue.cloneNode(true);
		setMyStyle();
	}else{
		var ul = document.evaluate("//div[@class='tweet_box']/ul", document,null,9,null).singleNodeValue;
		var ul_mama = ul.parentNode;
		ul_mama.removeChild(ul);
		if(modified){
			ul_mama.appendChild(originalTree);
			resetMyStyle();
		}else{
			ul_mama.appendChild(modifiedTree);
			setMyStyle();
		}
	}
	modified = !modified;
}

使用前の注意

greasemonkeyスクリプトを書く上での作法をよく分かってないので(無名関数で囲うってのは、もうやらなくて良いんだよね?)、何か悪さをするかも知れない。あとウチでは普段JavaScriptを切っているので、動的な要素と順番が入れ替わったりするかもしれないけど、そこら辺の確認もよくしていない。
現在分かっている変な挙動はfacebookの「いいね!」ボタンの場所が変わってしまうというのがあるけど、私にとって何の問題も無いので原因は調べていない。

使用方法

インストールした後 Togetter に行くと、下の画像内の1のように窓の右側に黄色っぽい四角形が表示される。その四角形にマウスポインタを重ねると四角形が飛び出してくるので、その上でクリック。tweet の表示方式が変わる。もう1度クリックすると元に戻る。

具体的に変更される点は:

  1. 同一人物の連続する tweet を一つのエントリにまとめる(=間に横線が入らない)。
  2. 同一人物の連続する tweet の先頭のみにアイコンを表示。
  3. 同一人物の連続する tweet の末尾のみに元 tweet へのリンクを表示。
  4. エントリ内の twitter.com で始まるリンクの文字色を薄くする。

これらの変更でベストケースでは、結構読みやすくなる。
1,2はリストの構造を根本的にぶっ壊してるので、アイコンの下にあるボタンでリプライとかリツイートできなくなってるんじゃないかと思うが試していない。3は表示されてないけど隠れてるだけなので、見たい tweet の上にマウスポインタを重ねると、元 tweet へのリンクが現れる。上の画像内2の状態(キャプチャの過程でマウスポインタが消えちゃったけど、本当は重なってる)。4は@付きの文字が先頭にあると、読み進めるのにうるさいのでそうしたけど、文脈上重要な情報が読み取りづらくなってる可能性もあり微妙。気に入らないようなら各自で調整、という事で。

作ってみた感想

XPath って便利。でも document.evaluate() の第5引数は鬼門だと思った。

スーパーpre記法は羊頭狗肉

スーパーpre記法が対応するシンタックスハイライトが増えたようである。
スーパーpre記法のシンタックス・ハイライトが Brainfuck, Clojureに対応しました - はてなダイアリー日記
素晴らしい!先端を行く言語もすばやくフォローする辺りに進取の気風、4文字言葉も伏せ字にしない辺りに自由への意志が感じられる。
ところで、スーパープレ記法って一体いくつくらいのシンタックスに対応しているんだろう、と見てみると、
ソースコードを色付けして記述する(シンタックス・ハイライト) - はてなダイアリーのヘルプ
…、うん、「たくさん」だ。

しかし、ここに列挙されているシンタックス全部に本当に対応してるのだろうか。無作為に取り上げた1つの形式xpmについて調べてみよう。

/* XPM */
static char * winInspectorMain16_xpm[] = {
"16 16 6 1",
" 	c None",
".	c #000000",
"+	c #FFAD00",
"@	c #FF6D00",
"#	c #FF8500",
"$	c #FF0000",
"................",
".++++++++++++++.",
".+++++....+++++.",
".+++..@@@@..+++.",
".+++.@@@@@@.+++.",
".++.@@@@@@@@.++.",
".++.@@@@@@@@.++.",
".++.@@@@@@#@.++.",
".++.@@@@@@#@.++.",
".+++.@@@##$.+++.",
".+++..@@@@...++.",
".+++++....+...+.",
".+++++++++++....",
".++++++++++++...",
".+++++++++++++..",
"................"};

おお確かにハイライトされている。
だが、ちょっと待ってほしい。試しに上の文字列をコピペしてfoo.xpmとでもして保存してからvimで開いてみてほしい。
いかがであろう。はたしてあなたは、はてなのスーパープレ記法は本当にxpm形式に対応していると言えると考えるだろうか?


まあ、ちょっと前に知って「vimすげー…、つーか何でもtextのunix文化がすごいのか?」という感動から生まれたお話だったとさ。

新宿駅で寸借詐欺っぽいものが流行っている?

正確には寸借詐欺ではなく、単に「どこそこに行きたいんだが、お金が足りない。金をくれ。」と言ってきます。丁重にお断りしましょう。ヒマなら交番に連れて行って、お金を借りるのを手伝ってあげるのもいいかも知れません。功徳を積む意味でお金を渡しちゃうのもアリ。

先日起こった事

JR東口の売り場で切符を買って、改札口に向かおうとしていたら、じいさんが話しかけてくる。
じいさん: あのー、ちょっとお聞きしたいんですが…
俺: はあ…
じ: あのですね、藤沢に行きたいんですよ。東海道線の。
俺: 藤沢ぁ?(神奈川の方だったよなぁと、料金表を眺める。この時点では乗車ホームを尋ねられてるんだと思ってる。)山手線で…
じ: いや、あの、行き方は分かってるんですけど…
俺: !
--じじい、おもむろに手の平を差し出す。その上には300〜400円の小銭が乗っかっている。
じ: 藤沢に行きたいんですけど、お金が足りないんです。お聞きしたいのは、申し訳ないんですがお金を----

俺:    *      *
  *  無理です  +
     n ∧_∧ n
 + (ヨ(* ´∀`)E)
      Y     Y    * 

詐欺じゃないんじゃね?

これだけだと、大都会のターミナル駅で困っている老人を無碍にする人情の薄い都会人の図、ってな感じに見えない事も無いけど--実際そうである可能性もあるが--俺が即座に断ったのには理由があって…、以前にも全く同じ体験をしてたのである。んで、その時はやられちゃいました。
その時の相手は白髪のばあさんであった。場所は西口、切符を買って釣り銭を財布に入れようと手に握った瞬間に話しかけてきた。そのタイミングが絶妙で、俺は色々な疑念やら対案やらが頭をよぎったものの、操られるように彼女の手の平に500円玉を落としてしまった。刹那、光の速さでその金を券売機に投入するばあさんを見て「ああ、俺は今、騙されたんだな…」と悟った。まあ、アレだよ、どんなことでもやってみて、損をしたって、少し経験値が上がって、抵抗力が高まると。

考えた事

この手口で特徴的なのは、まず、やってる人が見た目弱々しい老人である事。要求する金額がおよそ500円である事。この条件が重なると「ま、いっか…」と思わされてしまう確率が上がる。まあ、そう思わない人もいるんだろうが、10代後半から50代辺りの人間がこんな事を言ってきたら、俺でも普通に「交番行け」と言う。500円というのもイイとこで、抵抗値が跳ね上がる手前ギリギリの線ではなかろうか。それでいて1時間当たり2人がかかれば時給1000円である。多分あのばあさんは、もっと高い撃墜率を誇っていたと思う*1。さらにターゲットは、切符を買った直後の人間=電車に乗る気満々の人間、であってその後の行動を見咎められる可能性が低い。唯一、駅員はその切符を払い戻す際に不審に思うかもしれないが、そう思う以上の行動は起こしにくかろう。
とは言え、ここに書いている事は仮定に仮定、憶測に憶測を重ねただけなので、すべて俺の被害妄想の産物かも知れない。観測された事実は、(それ以前はあった事が無いのに)ここ半年以内に2回、切符を買う金をせびられたという事だけである。
可能性としては

  • 詐欺ではない場合
  • 詐欺である場合
    • ここ最近この手口が流行っている。
      • カジュアル犯罪仲間みたいなコミュニティでお手軽手口として人気。
      • 元締めみたいのがいる、ある程度組織だった犯罪。
    • 昔からの手口で、やってる人数も変わらない。
      • 偶然、遭遇する機会が偏っていた。
      • PASMO等の普及により、現金で切符を買う人間が減ったため、遭遇の確率が上がった。

くらい、かなあ…。どうなんだろ。

*1:というか、そう信じたい。

いろいろないろ、みんなちがってみんないい。…どういうことなの?

画像をいじくる時に、ある元画像の上にレイヤーを重ねて、上のレイヤーの「不透明度」を変えて画像を加工するというのは、よくある事だと思います。GIMPだと「標準」というモードの処理です。んで、数値的にはどんな処理をしてるのかと調べてみたら、意外な結果だったのでメモしておきます。
「不透明度」を変化させて画像を加工する時、元画像の色を I、上にかぶせる画像の色 M、合成された画像の色を E とします*1。M の不透明度を変化させていくと、不透明度 100% の時 E(結果) = M(マスク) で結果の画像は上にかぶせた画像と一致し、0% で E(結果) = I(元) となり元画像と一致します。と、いう事はその中間では、M と I の RGB 成分の差を取って、その差に不透明度の値を掛けたものを元画像の色に足してやる、というのが一番素直だよなと考えて、以下の関数を書いて試してみました。

#Ruby
#a, b は RGB を成分としたベクトルクラスのインスタンス。
#a は元画像の色、b はかぶせる画像の色を表す。n は不透明度。
def blend( a, b, n )
  n = n / 100.0
  ret = a + ((b - a) * n)
end

この関数の n を変化させて出て来た値と、実際に画像を重ね合わせた時に出て来る色の値を比べたところ、「だいたい合ってる」んだけど、微妙に違います。その違いは以下の通り。

不透明度floor()round()ceil()GIMPPictBearFirefoxGoogle Chrome
10%229.5, 235.8, 242.2229, 235, 242230, 236, 242230, 236, 243230, 236, 242229, 235, 242230, 236, 242231, 237, 243
20%204.0, 216.6, 229.4204, 216, 229204, 217, 229204, 217, 230204, 216, 229204, 216, 229204, 217, 229205, 217, 230
30%178.5, 197.4, 216.6178, 197, 216179, 197, 217179, 198, 217179, 197, 216178, 197, 216179, 198, 217180, 198, 217
40%153.0, 178.2, 203.8153, 178, 203153, 178, 204153, 179, 204153, 178, 203153, 178, 203153, 178, 204154, 179, 204
50%127.5, 159.0, 191.0127, 159, 191128, 159, 191128, 159, 191128, 159, 191127, 159, 191127, 159, 191129, 160, 192
60%102.0, 139.8, 178.2102, 139, 178102, 140, 178102, 140, 179102, 139, 178102, 139, 178102, 140, 178101, 138, 177
70%76.5, 120.6, 165.476, 120, 16577, 121, 16577, 121, 16676, 120, 16576, 120, 16576, 120, 16576, 120, 164
80%51.0, 101.4, 152.651, 101, 15251, 101, 15351, 102, 15351, 101, 15251, 101, 15251, 101, 15350, 100, 151
90%25.5, 82.2, 139.825, 82, 13926, 82, 14026, 83, 14025, 81, 13925, 82, 13925, 82, 14025, 81, 139

  • GIMP 2.6.2, PictBear 1.74, Firefox 3.0.10, Google Chrome 2.0.172.28
  • I(元画像)=#FFFFFF, M(マスク)=#003F7F, M の不透明度を 10% 刻みで変化。

表の内「生」は上の関数からの出力の値そのもの。次にその値を floor(切り下げ), round(四捨五入), ceil(切り上げ) の関数を使って整数に変換したもの。最後にそれぞれのアプリケーションで実際に変換された色の値です。floor:青, round:黄, ceil:赤 としてそれぞれの関数が出力した値と一致する場合、背景を一致する関数の色で塗り分けています(但し 50% の時 round と ceil の値は一致します)。
一見して分かるのは PictBear は floor の値と全てが一致しています。が、他のアプリケーションの場合見事なまでにばらばらです。内部の計算では RGB を使ってないのでしょうか。優勝は Google Chrome です。かすりもしません。
単純に不透明度を設定して画像を重ね合わせるってだけでも、こんなに違うもんなんですね。普段から画像や web サイトをあつかってる人にとっては常識なのかも知れませんが、ちょっと驚きました。

テストに使った html

<html> 
  <head>
    <style type="text/css">
      body {
        background: #fff}
	  div {
		margin: 0.5em 0;
        padding: 1em;
        text-indent: 1em;
        line-height: 1.5;
        background: #003f7f;}
      #s_1 {opacity: 0.1;}
      #s_2 {opacity: 0.2;}
      #s_3 {opacity: 0.3;}
      #s_4 {opacity: 0.4;}
      #s_5 {opacity: 0.5;}
      #s_6 {opacity: 0.6;}
      #s_7 {opacity: 0.7;}
      #s_8 {opacity: 0.8;}
      #s_9 {opacity: 0.9;}
    </style>
  </head>
        
  <body>
     <div id="s_1"></div>
     <div id="s_2"></div>
     <div id="s_3"></div>
     <div id="s_4"></div>
     <div id="s_5"></div>
     <div id="s_6"></div>
     <div id="s_7"></div>
     <div id="s_8"></div>
     <div id="s_9"></div>
  </body>
</html>

*1:2. Layer Modesの表記を拝借

男でよかった。…のか?

悪いのは誰? - ある無人島漂流の物語 - (旧姓)タケルンバ卿日記避難所
を読んで、以下のようなブクマコメントをしたけど、もう少し言いたい事が出てきたので。

じじい:「知らんがな」/取引を持ちかけた男が悪くないという立場なら、その男をぶち殺しもせず妻を責めた夫が、より悪いって事になるのかな?

http://b.hatena.ne.jp/t-sat/20090117#bookmark-11694333

あまりの事に反射的にじじいを擁護してしまったが、いく人かの人が指摘してるように、相談を持ちかけられた時に無法者に立ち向かうべく連合を組織しようとしなかったのは、じいさんの落ち度だと思う。
夫はとても不寛容だと思うが、あまり責める気にならない。妻がレイプされて、理性では妻は悪くないと分かっていても、実際には何かしら責めてしまうって事はあるんじゃないか、とか考えてしまう。自分はそんなやつにはなりたくないけど、ならないという確信が今ひとつ持てない。まあ、出題文にそんな事はビタ一書いてないし、現実の私には妻なんぞ影も形も見えないんで、この夫にそんなに感情移入する言われもないんだが。
が、これらの事は実はどうでもよくて、一番書きたかったのは、ブクマコメントで見かけた「誰も悪くない」「悪者探しをしなくてもいいんじゃないか」といったような意見への驚きである。
えええ?
改めて、出題文を引用すると…

ある夫婦、その妻に思いを寄せる男性、この3人とは何の関係もない男性、おじいさん。この5人が乗っていた船が難破し、無人島に流された。
その過程で、夫婦の夫は行方不明となり、島に流れ着いたのは4人だった。この時点で夫の安否はわからない。
夫の安否を確かめるには、船を出して捜索するしかないが、妻には船をつくる能力や、直す力はない。船をつくり、直すことができるのは、夫婦とは縁もゆかりもない男性ただひとりだった。
妻はその男性に頼んだ。「船を直してください。夫を探したいのです」と。
男性は直すと言った。だが、条件をつけた。その条件とは妻と一夜限りの関係を持つこと。
妻は悩み、おじいさんに相談した。おじいさんは「気持ちのままに行動しなさい」と。
妻は結局、その男性と関係を持った。男性は約束を守り、船を直した。
そして船が直り、これからまさに夫を探すというときに、夫が無人島に自力でたどり着いた。
妻は夫に、捜索するため、船を直すために男性と関係を持ってしまったことを告白した。
夫はそんな妻を許さなかった。不貞であると。
夫婦は破局した。
ひとりになった妻の様子を見て、思いを寄せていた男が言った。「あなたが好きです」。

http://d.hatena.ne.jp/takerunba/20090116/p2

うーむ、改めてひどい。それはともかく、この文をお題に「最も悪いと思う人は誰ですか?」と問われるわけだ。
ちょっと寄り道して、「悪い」というのがどういう事かと考えてみる。大雑把に言って、ある行為や、ある考え・態度からなされる行為が、周りの人に好ましくない状況を引き起こす時、その行為・考え・態度は「悪い」。皆幸せで誰も悲しまない悪事というのは存在しない。
んで、出題文を読んでみる。
この中には悲しんでいる人がいるよな?そして、その悲しみは天災のみによって引き起こされたわけじゃなくて、明らかに人によって負わされた悲しみだよな?
なんか、中には「この妻が愚か」というようなコメントも見かけたような気がするけど、こんな目にあってすら害をなされたと考えてもらえないなんて、やっぱ世の中は橋田寿賀子なのか。

立場を入れ替えてみる

ある夫婦、その夫に思いを寄せる女性、この3人とは何の関係もない男性、おじいさん。この5人が乗っていた船が難破し、無人島に流された。
その過程で、夫婦の妻は行方不明となり、島に流れ着いたのは4人だった。この時点で妻の安否はわからない。
妻の安否を確かめるには、船を出して捜索するしかないが、夫には船をつくる能力や、直す力はない。船をつくり、直すことができるのは、夫婦とは縁もゆかりもない男性ただひとりだった。
夫はその男性に頼んだ。「船を直してください。妻を探したいのです」と。
男性は直すと言った。だが、条件をつけた。その条件とは夫と一夜限りの関係を持つこと。
夫は悩み、おじいさんに相談した。おじいさんは「気持ちのままに行動しなさい」と。
夫は結局、その男性と関係を持った。男性は約束を守り、船を直した。
そして船が直り、これからまさに妻を探すというときに、妻が無人島に自力でたどり着いた。
夫は妻に、捜索するため、船を直すために男性と関係を持ってしまったことを告白した。
妻はそんな夫を許さなかった。不貞であると。
夫婦は破局した。
ひとりになった夫の様子を見て、思いを寄せていた女が言った。「あなたが好きです」。

理論派の人はこのような取引・契約も全然 OK なんだろか。

かなり本題と関係ないが

改変版を作ってて思ったのだが、こんな問題文を作って研修なんかしてたら、ゲイの人は怒りそうだよな。実際にここに書かれたような人がいるかどうかはともかく、自分たちの性がこんな行為に結び付けられて語られる事に対して。
で、ノンケの男の私はこんな行為に自分の性が結び付けられている事にほとんど違和感を感じないわけだ。「俺は違うが、中にはそんなやつもいるだろう」と。男が得をしてるってのはこういう事なんだろうな。