m2

Roslyn Analyzer でコメントをコンパイルエラーにする

日付っぽい文字列と「START」って書いてあるコメントをエラーにする Roslyn アナライザを書きました。
コンパイルエラーといってもビルド&実行できるので、見た目エラーっぽい警告くらいの感じです。

ソースはこちら
https://github.com/miya2000/CommentAnalyzer

つまりはコードレビューがプログラミングできるということですね!
こんな感じで ウンコードマニア のいくつかはアナライザで実装できるかなーと思います。書こう!Roslyn アナライザー

  • -

8/23
nuget に登録しました。
https://www.nuget.org/packages/CommentAnalyzer/

今日のコミットでテストを実装してたり、日本語環境の場合はメニューが日本語になったりしています。
これをベースにまた別のを書く予定。

Visual Studio のひどい訳について

はてな記法が忘却の彼方。


さて、善意のひどい訳についてに刺激を受けて、Visual Studio のひどい訳をさらしてみます。

まず「プレビューおよび変換」です。

英語版では「Preview Transform」となっています。

この機能は環境に合わせて設定ファイルの内容を変換する機能の変換結果をプレビューします。こんな感じ。

なんですが、この画面は「プレビュー」機能であって「変換」機能ではありません。「および」はどこから出てきたのでしょうか(私は「変換」ボタンを必死に探しました)。機能そのものはとても素晴らしいだけに残念です。


ちなみに Visual Studio の規定の言語を変更するには 対象の言語パックをインストールして、Devenv コマンド ライン スイッチ/LCID を指定します。

devenv /LCID 1033

日本語に戻す場合は「1041」です。
頻繁に切り替えるならショートカットを作っておくと便利。


面倒なので次で最後になりますがマージツールの「右へ」。

もうこの時点でおかしいと気付くでしょうか。
英語版では「Take Right」。

左右に並べられたファイルの異なる部分のうち、右使う、という意味ですが、「右へ」だとどちらかというと逆の意味に思えます。
おそらく WinMerge なんかの「右へコピー」と勘違いしちゃったのですかね。どちらにしろ意味は逆ですが。


以上です。
きっと一度もその機能を使ってない人が翻訳しているのだし、翻訳元も文章でなく「Take Right」みたいな数単語では誤訳も致し方のないことなのでしょう(テストも書けないし)。
気付いたら「ヘルプ」→「フィードバック」→「問題点、改善点の報告」するとよいかもしれません。元記事にあるように Github で管理して pull request が送れる形になればより良いですね。
あと Visual Studio の新機能を日本語で紹介するような方であれば、記事の投稿時にちょっとだけこのあたり意識していただけるとありがたいな、と思います。


蛇足ですが、このマージツールは「横表示」で縦に、「縦表示」で横に並べて表示することができるので、もはや「右」とか「左」とか関係ありません。訳を直すにしてもいったいどうすれば...

認証情報付きアドレスに対するブラウザの挙動の比較 (location.href の話)

先日、夏のちょっと怖い話としてlocation.hrefの盲点という文章が公開されました。

デスクトップでこの影響を受けるのは Safari5 (Windows または Snow Leopard 以前の Mac) くらいのもので、対象者は少ないんじゃないのかなーと予想しますが、

残念なお知らせがあります。Safari Mobile/Androidの標準ブラウザもこの動きをするのです。
さらに、Androidの標準ブラウザは@付きURLにアクセスする時に警告も出しません。

今この情報を出した一番の理由として、このモバイルの問題をどうやっても早急に解決できないことがあげられます。モバイル端末のアップデートは機種によりまちまちで、提供されるかもわかりません。そんな現状で、このような潜在的な問題が早く修正されるとは思えません。アプリケーション開発者で対応するしかないでしょう。

http://masatokinugawa.l0.cm/2012/08/safari-location.href.html

とのことなので、モバイル利用者の方々には Opera Mobile などの、Mobile Safari を使用していないブラウザをお勧めする*1とともに、開発者の方々には影響の調査と修正をお願いしたいところです。
たいていは同じオリジンのリソースの読み込みにはホスト名以降の相対パス(../js/hoge.js とか /static/hoge.js とか)が使われるかと思いますけれども、location.href からアドレスを組み立てるなどしている箇所があれば、そこに今回の問題があるかもしれません。
以前であれば外部リソースを読めるのは SCRIPT 要素を使った js (JSONP) 位のものでしたが、XHR level2 によってサーバーが許可していれば XHR でも読み込めるようになりました。「XHR で読み込めたんだから同じオリジンのはず」という状況ではなくなっていますので、SCRIPT にしろ XHR にしろ、外部リソースを読み込む場合はそのサーバーがリクエスト時に意図したサーバーなのか、というのは注意が必要でしょう。
(詳しくは jQuery MobileのXSSについての解説 - 金利0無利息キャッシング – キャッシングできます - subtech 等)


さて、本稿では今回問題になった「認証情報付きアドレス」を各ブラウザがどのように扱っているのかというのを比較してみます。
テストページとして http://jsrun.it/miya2000/2Lyx を用意しました。jsdo.it を使用させていただいています。
[ユーザー名] と [パスワード] を入力して実行すると、http://[ユーザー名]:[パスワード]@jsrun.it/miya2000/2Lyx?dummy というアドレスを生成して iframe に設定し、その location.href を確認します。ついでに a 要素の href プロパティについても確認しています。


私のほうで確認できる環境のテスト結果は上記のテストページに貼り付けました。各ブラウザで違いがあって面白いですね。
以降はテスト結果に対するまとめになります。

「認証情報付きアドレス」についておさらい

(後で書く)
(もう書かない)

*1:Sleipnir は Mobile Safari を使用しているようです

Java の List の indexOf, contains, remove がなぜかタイプセーフじゃなくて軽く死ねる件 (Map#get(Object) も)

いやーそんなはずはないと思って今まで確認すらしてませんでした。
http://java.sun.com/javase/ja/6/docs/ja/api/java/util/List.html#contains(java.lang.Object)

どういうことかというと、これがコンパイルエラーにならないということです。

import static java.util.Arrays.*;

public class Hoge {
    public static boolean isHogehoge(Integer hogeType) {
        return asList("1", "3", "5").contains(hogeType);
    }
}

コンパイルエラーにするにはジェネリックなメソッドを作って、そちらを使用します。

import static java.util.Arrays.*;

public class Hoge {
    public static <E> boolean contains(E value, java.util.List<E> list) {
        return list.contains(value);
    }
    public static boolean isHogehoge(String hogeType) {
        return contains(hogeType, asList("1", "3", "5"));
    }
}

これで isHogehoge のパラメーターを「Integer hogeType」にすると、ちゃんとコンパイルエラーになります。

しかしまたつまらぬ static メソッドが増えてしまった。
Object#equals(Object) もそうだけど、基本的に Java プロジェクトでは一つ static メソッド集的なものを作って、オブジェクトのメソッドでなくそちらを使うようにすると問題が減らせると思います。

こんなの絶対 OOP じゃないよ!

  • -

(追記)
Map#get(Object) も同じようです。

java List contains typesafe」なんかで検索してもそれっぽい情報が見つからなかったのですけど、なるほど Map の方が問題になるケースが多そうで、「java Map get typesafe」で検索するとそういった記事や議論が見つかりました。

Recently I have encountered a bug in a production code when dealing with a simple (really simple) usage of a Map.
...

import java.util.HashMap;
import java.util.Map;

public class EmployeeDataLookup2 {
    // A map storing relation between employee ID and name.
    private Map<Long, String> employeeIdToName;
...
    // Lookup method for finding employee name for given employee ID.
    public String findEmployeeName(int employeeId) {
        return employeeIdToName.get(employeeId);
    }

...
The problem is that after introducing generics, probably due to backward compatibility, some of the methods in Collection and Map interface have not been changed and still do not perform type checking. One of them is Map.get(Object) which have caused the bug in the code above.

JavaBlogging » Type safety in Java Set and Map

As mentioned by people above, the reason why get(), etc. is not generic because the key of the entry you are retrieving does not have to be the same type as the object that you pass in to get(); the specification of the method only requires that they be equal. This follows from how the equals() method takes in an Object as parameter, not just the same type as the object.
...

  1. Then why is V Get(K k) in C#?
    -> Differents specifications.
java - What are the reasons why Map.get(Object key) is not (fully) generic - Stack Overflow

an object of one type can be .equals() to an object of another type. get() only requires that the object that you give it be .equals() to the key you are getting

Java Generics: Why Does Map.get() Ignore Type? - Stack Overflow

A popular myth is that it is stupid and evil, but it was necessary because of backward compatibility. But the compatibility argument is irrelevant; the API is correct whether you consider compatibility or not. Here's the real reason why.
...
Let's say you have a method that wants to read from a Set of Foos:

    public void doSomeReading(Set<Foo> foos) { ... }

The problem with this signature is it won't allow a Set to be passed in (where SubFoo is, of course, a subtype of Foo).
...

    public void doSomeReading(Set<? extends Foo> foos) { ... }

Perfect!

But here's the catch: if Set.contains() accepted type E instead of type Object, it would now be rendered completely unusable to you inside this method body!
...
Static analysis plays an extremely important role in the construction of bug-free software.

smallwig: Why does Set.contains() take an Object, not an E?

最後に引用した Kevin Bourrillion さんの post に全部書かれてますね。
僕が書いた contains の実装はワイルドカードに対応できません。そのまま使うとコンパイルエラーになります。

    static class Foo {
    }
    static class SubFoo extends Foo {
    }
    public void doSomeReading(List<? extends Foo> foos) {
        if (foos.contains(new SubFoo())) { // API仕様通り
            // 
        }
        if (contains(new Foo(), foos)) { // <- コンパイルエラー
            // 
        }
    }

えー、じゃあ contains をこんな感じにすればどうかなーと思うわけですが。

    public static <E> boolean contains2(E value, java.util.List<? extends E> list) {
        return list.contains(value);
    }

こんな結果に。元々何がやりたかったかわけわかんなくなります。

    public void doSomeReading(List<? extends Foo> foos) {
        if (contains2(new Object(), foos)) { // えっ
            // 
        }
    }


(あとで書く。結論としては FindBugs でOK。)

Twitter のユーザータイムラインからリンクを抽出して RSS にする Yahoo Pipes を作りました

暇なときによく 1470.net注目URLRSS リーダーで読んでいたのですが、今月の頭あたりから 1470.net のホストから RSS が配信されなくなりました。代わりに Twitter@recenturl へのリンクがありましたので、今後は Twitter で配信するようです*1

そうすると Twitter から提供されるユーザータイムラインの RSS を購読すればいいように思いますが、それだと RSS のリンクが TwitterTweet へのリンクになってしまうので、読みたい記事があった時に、

  1. TwitterTweet のページを開く
  2. Tweet に含まれる短縮URLをクリックする

と二度手間になってしまいます。
また、見たいページのサーバーは問題ないのに Twitter サーバーが落ちていると閲覧できないなどといったことも起こりえますから、非常に不便です。

というわけで、Twitter のユーザータイムラインの RSS からリンクを抽出して別の RSS に仕立て上げる pipes を作成しました。

細かい仕様は pipes にも書いていますが以下のような感じです。

  • 本文の先頭がアカウント名と同じ場合、それを削除します。
  • 本文の末尾もしくは先頭が t.co の場合、t.co を展開したアドレスを RSS のリンクにします(末尾の方を優先します)。
  • t.co の展開には http://ux.nu/ を使用しています。
    ux.nu が「安全でない」と判断した場合は RSS のタイトルに [unsafe] を付けます。
  • デフォルトでは URL を含まない Tweet はフィードに含めません。「ignore no url tweet」を「on|yes|true」以外にすると含めます。

@recenturl は先頭に URL がありますが、末尾の URL にも対応しているので、同じように bot が URL を Tweet するようなものにも使えるんじゃないかと思います。



いい感じになりました。

感想

TwitterRSS ではすべてのアドレスが「t.co」になるので、短縮URL展開部分はそれほど複雑にならずに済みました。
それと ux.nu は素晴らしいですね。untiny.me が t.co を bit.ly に展開した時はズッコケたんですが、ux.nu はリダイレクトが多段になっていても最終的なアドレスを返してくれるので、とても助かりました。ありがとうございます。


http://ux.nu/hugeurl?url=http%3A%2F%2Ft.co%2FKxXrbCV2&format=plain
が、なぜかドメイン部分が含まれずに
/post/15835992847/fotoshop-by-adobe-by-jesse-rosten-brilliant?aa5fb790
を返すみたいです。

2012/1/17 追記

http://1470.net/recenturl.xml 1470.netの話題のURLフィードを復活させました。@recenturlにも流していますが、RSSリーダーとかでチェックしたい人はこちらをどうぞ。

http://twitter.com/#!/ishinao/status/159129373503520769

あらあら。ありがとうございます。

*1:@ishinao さんに Twitter で聞いてみたんですが、回答いただけませんでした。。。

重みをつけて乱択する (バイナリサーチ版)

せっかく書いたのでバイナリサーチ版のっけときます。

http://jsfiddle.net/t58Vv/6/

// algorithm - 重みをつけて乱択する http://blog.livedoor.jp/dankogai/archives/51761113.html
// バイナリサーチ版
var make_random_picker = function(picks){
    var i, l = picks.length, t = 0, choices = picks.concat();
    for (i = 0; i < l; ++i) { choices[i][1] = (t += choices[i][1]); }
    for (i = 0; i < l; ++i) { choices[i][1] /= t; }
    return function() {
        var r = Math.random(), head = 0, tail = l - 1;
        while (head <= tail) {
            var sum = head + tail;
            var mid = (sum <= 0x7fffffff) ? (sum >> 1) : Math.floor(sum / 2); 
            if (r < choices[mid][1]) {
                if (mid == 0 || r >= choices[mid-1][1]) {
                    return choices[mid][0];
                }
                tail = mid - 1;
            }
            else {
                head = mid + 1;
            }
        }
        return null; // never.
    };
};