白猫のメモ帳

C#とかJavaとかJavaScriptとかHTMLとか機械学習とか。

総称型メソッドの型指定

こんにちは。

新年度が始まって二週間がたちました。
新しい環境で頑張っている方はとてもお疲れのことでしょう。
新しくなくても眠たいです。春眠春眠・・・。

この前コードを書いていてふと気づいたこと。
JavaC#で総称型メソッドの挙動が違う。

総称型メソッド

まずそもそも総称型メソッドってなんだって話です。
ジェネリクスメソッドとかジェネリックメソッドとか呼んだりもします。

public static String firstOrDefault(List<String> list, String defaultValue) {
    return list != null && list.size() > 0 ? list.get(0) : defaultValue;
}

例えばこんなメソッドを作ったとして(StreamAPI使えとか無粋なことは言わないこと)、
Listで同じことをしたい場合、メソッドのオーバーロードをしなくてはいけません。

public static Integer firstOrDefault(List<Integer> list, Integer defaultValue) {
    return list != null && list.size() > 0 ? list.get(0) : defaultValue;
}

ひい。めんどくさい。
ということでこんな風にします。

public static <T> T firstOrDefault(List<T> list, T defaultValue) {
    return list != null && list.size() > 0 ? list.get(0) : defaultValue;
}

第一引数に「List<String>」、第二引数に「String」を渡せばちゃんと動きます。
第一引数に「List<Integer>」、第二引数に「Integer」を渡してもちゃんと動きます。
第一引数に「List<String>」、第二引数に「Integer」を渡したらコンパイルエラーになります。
えらい。

引数の前についてる<T>がポイント。
ここでなんかよくわからんけど、なんでも良い型「T」っていうのが定義されます。

public static <T extends Number> T firstOrDefault(List<T> list, T defaultValue) {
    return list != null && list.size() > 0 ? list.get(0) : defaultValue;
}

こんな風にすると、Numberを継承した何か。
になるので、IntegerはOKだけどStringはNGになります。

C#的にはこんな感じ。

public static T FirstOrDefault<T>(IList<T> list, T defaultValue) where T : struct
{
    return list != null && list.Count > 0 ? list[0] : defaultValue;
}

で、このTってコンパイラ的にはどうやって決めてるのっていうのが今日のお話。

明示的に指定

class Util {
    public static <T> List<T> newArrayList() {
        return new ArrayList<>();
    }
}

例えばこんな定義をしたとして、

Util.<String>newArrayList();

こんな風にメソッド呼び出し時に明示的にTの型を指定してあげることができます。

引数の型で指定

class Util {
    public static <T> List<T> newArrayList(Class<T> c) {
        return new ArrayList<>();
    }
}

次にこんな風に定義を変えてみると、

Util.newArrayList(String.class);

ジェネリクスの指定なしに呼び出すことができました。
これは引数の型情報からTの型がわかるからです。

戻り値の型で指定

class Util {
    public static <T> List<T> newArrayList() {
        return new ArrayList<>();
    }
}

メソッド定義を元に戻してみて、

List<String> list = Util.newArrayList();

戻り値側で型を決めてしまえば、明示的に指定しなくてもOKになりました。
やったね。

が、これをC#でやろうとするとエラーになります。

class Util
{
    public static IList<T> newList<T>()
    {
        return new List<T>();
    }
}

こんな定義をして、

IList<string> list = Util.newList<string>();  // OK
IList<string> list = Util.newList();  // NG

試してみると、戻り値側で型が判定できても明示的に指定しないとコンパイルエラーになってしまいます。

いいとこどりしたい

とまぁ、JavaからC#に移ってきた私としてはちょっと不満ではあるのですが、
C#ジェネリックにはJavaにはない素晴らしい機能があります。

class Util
{
    public static T newInstance<T>() where T : new()
    {
        return new T();
    }
}

そう、newができるのです。
これは型削除されてしまうJavaにはできない芸当です。
(可変長引数とか使って無理やり頑張るとできるとかできないとか・・・)

いいとこどりしたいなー。
できるようにならないかなー。なんて。

fastTextで単語のベクトル演算

こんにちは。

随分と空いてしまいました。
3月なのに今年最初のエントリってどういうことじゃい…。

以前、fastTextのインストールをしましたが、実際に使っていなかったので、
今回は適当な文章から単語のベクトルを学習させて、その演算を試してみます。

ちなみにfastTextは分類によく利用されるため、
分散表現を使ってどうこうするのはあんまりポピュラーではないです。
が、せっかく機能としてあるので使ってみます。

何ができるの?

「パリ」 - 「フランス」 + 「日本」 = 「東京」
とか
「王様」 - 「男」 + 「女」 = 「女王」
みたいなやつです。

これはfastText固有の機能というわけじゃなくて、
fastTextの元になっている(元っていうのも違う気がするけれど)、word2vecで有名な機能です。

学習する

詳しい説明は世の中にたくさんあるので、さっくりと端折ります。
今回学習させるデータはWikipedia日本語版の全データです。

https://dumps.wikimedia.org/jawiki/

この辺とかから取れます。

これを分かち書きするのですが、その辺りはお好きな言語とライブラリで頑張りましょうということで、
これについても端折ります。

アンパサンド ( ampersand , &) と は 「 … と … 」 を 意味 する 記号 で ある 。 英語 の " and " に 相当 する ラテン語 の " et " の 合 字 で 、 " etc ." ( et cetera = and so forth ) を "& c ." と 記述 する こと が ある の は その ため 。 Trebuchet MS フォント で は 、 10 px と 表示 さ れ " et " の 合 字 で ある こと が 容易 に わかる 。

こんな感じで半角スペース区切りで分かち書きします。

で、こんな感じで学習させます。

# /fastText/fasttext skipgram -input wakachi.txt -output model

「skipgram」と言うのは単語表現のモードです。
他には「cbow」を指定できます。

github.com

他にもパラメタが色々あるのですが、面倒なのでそのままで試してみました。

・・・が、表現するベクトルの次元を指定する「dim」と、学習の繰り返し回数を指定する「epoch」くらいは
ちゃんと指定しておいたほうが良いと思います。
(ちなみにDockerの設定も特にいじっていなかったので、メモリ2Gしか使わなくてやたら時間が掛かりました)

試す

例として挙げた足し算引き算みたいのを試すには「analogies」と言うコマンドを使います。

# /fastText/fasttext analogies model.bin
Pre-computing word vectors... done.
Query triplet (A - B + C)? 

「triplet」・・・なるほど。
で、対話型で順番に入れると、

Query triplet (A - B + C)? 王様
男
女
魔女 0.704916
奥様 0.665025
待ち遠しい 0.65906
魔法使い 0.655222
花嫁 0.652865
妖精 0.645881
王さま 0.643353
美女 0.642074
ゴチソウノォト 0.640036
シンデレラ 0.639036

ほら完璧・・・ってなんか違くない?
魔女・・・魔女かぁ、あながち間違いでもないかもなー。

Query triplet (A - B + C)? パリ
フランス
日本
東京 0.724273
都内 0.689351
大阪 0.61897
京都 0.612206
ソウル 0.605822
昭和女子大 0.605477
台北 0.603844
横浜 0.60244
渋谷 0.599425
神楽坂 0.596325

こっちはあってそうですね。

まとめ

ある程度の文章量を分かち書きしてfastTextに突っ込むとベクトル演算ができるのがわかりました。
Wikipedia全文とかだと、次元数が足りていない感じなので200くらいにした方がいいのかな?

そのうちに今度はカテゴライズをやってみようと思います。
こっちこそがfastTextのメイン機能なはず!

それではまた。

staticイニシャライザとstaticコンストラクタは同じだと思っていました

こんにちは。

年の瀬ですね。
お仕事は納まりましたか。
あれ・・・なんかこれ去年も書いた気がする。

そもそもイニシャライザとかコンストラクタってなんだっけ

Java

staticイニシャライザはJavaの機能。

class Hoge {
    
    static String fuga;
    
    // staticイニシャライザ
    static {
        System.out.println("static initializer");
    }
    
    // イニシャライザ
    {
        System.out.println("initializer");
    }

    // コンストラクタ
    Hoge() {
        System.out.println("constructor ");
    }
}

Javaにはコンストラクタとは別にイニシャライザという機能があって、
文字通りイニシャライズ(初期化)する時に呼ばれます。

staticイニシャライザはクラスにstaticアクセスしたときに呼ばれるため、
staticフィールドにアクセスしたり、インスタンス生成すると呼ばれます。

一方で、staticでないイニシャライザとコンストラクタはクラスのインスタンスを生成したときに呼ばれます。

// static initializer
String fuga = Hoge.fuga;

// static initializer
// initializer
// constructor 
new Hoge();

C#

で、staticコンストラクタはC#の機能。

class Hoge
{

    internal static string fuga;

    // staticコンストラクタ
    static Hoge()
    {
        Console.WriteLine("static constructor");
    }

    // コンストラクタ
    internal Hoge()
    {
        Console.WriteLine("constructor");
    }
}

C#にはイニシャライザという機能はなくて、
クラスにアクセスした際に処理を実行したい場合はstaticコンストラクタを使います。

// static constructor
var fuga = Hoge.fuga;

// static constructor
// constructor
new Hoge();

ここまでの感じでは同じように使っていいように思えます。

継承してみる

継承のあるパターンを確認してみます。

Java

class Parent {
    
    static String hoge;
    
    static {
        System.out.println("Parent static initializer");
    }
    
    {
        System.out.println("Parent initializer");
    }

    Parent() {
        System.out.println("Parent constructor ");
    }
}

class Child extends Parent {
    
    static String fuga;
    
    static {
        System.out.println("Child static initializer");
    }
    
    {
        System.out.println("Child initializer");
    }

    Child() {
        System.out.println("Child constructor ");
    }
}

子クラスから親クラスのstaticフィールドにアクセスすると、親クラスのstaticイニシャライザだけが呼ばれます。

// Parent static initializer
String hoge = Child.hoge;

子クラスのstaticフィールドにアクセスすると、親クラスと子クラスのstaticイニシャライザが呼ばれます。

// Parent static initializer
// Child static initializer
String fuga = Child.fuga;

親クラスのインスタンスを生成すると、親クラスのstaticイニシャライザ、イニシャライザ、コンストラクタが呼ばれます。

// Parent static initializer
// Parent initializer
// Parent constructor 
new Parent();

子クラスのインスタンスを生成すると、全部呼ばれます。順番は見たとおり。

// Parent static initializer
// Child static initializer
// Parent initializer
// Parent constructor 
// Child initializer
// Child constructor 
new Child();

C#

class Parent
{

    internal static string hoge;

    static Parent()
    {
        Console.WriteLine("Parent static initializer");
    }

    internal Parent()
    {
        Console.WriteLine("Parent constructor ");
    }
}

class Child : Parent
{

    internal static string fuga;

    static Child()
    {
        Console.WriteLine("Child static initializer");
    }

    internal Child()
    {
        Console.WriteLine("Child constructor ");
    }
}

子クラスから親クラスのstaticフィールドにアクセスすると、親クラスのstaticコンストラクタだけが呼ばれます。
ここは一緒。

// Parent static constructor
var hoge = Child.hoge;

子クラスのstaticフィールドにアクセスすると、子クラスのstaticコンストラクタだけが呼ばれます。
あれ、なんか挙動が違うぞ・・・。

// Child static constructor
var fuga = Child.fuga;

親クラスのインスタンスを生成すると、親クラスのstaticコンストラクタ、コンストラクタが呼ばれます。
ここは一緒。

// Parent static constructor
// Parent constructor
new Parent();

子クラスのインスタンスを生成すると、全部呼ばれます。
が、なんか順番が違います。
staticコンストラクタは子が先で、コンストラクタは親が先。ややこしい…。

// Child static constructor
// Parent static constructor
// Parent constructor
// Child constructor
new Child();

深くは掘り下げず、そういうもんだと思ってほかのパターンも見ていきます。

クラスジェネリクスをつけてみる

なんかずいぶん長くなってきてしまったので、いらないとこ除外しますね。

Java

class Parent<T> {
    
    static {
        System.out.println("Parent static initializer");
    }

    Parent() {
        System.out.println("Parent constructor ");
    }
}

class Child1 extends Parent<String> {
    
    static {
        System.out.println("Child1 static initializer");
    }

    Child1() {
        System.out.println("Child1 constructor ");
    }
}

class Child2 extends Parent<Integer> {
    
    static {
        System.out.println("Child2 static initializer");
    }

    Child2() {
        System.out.println("Child2 constructor ");
    }
}

Child1とChild2を連続してインスタンス生成すると、Parentのstaticイニシャライザは1回しか呼ばれません。
これは型削除(イレイジャ)という機能によって、コンパイル時にジェネリクスが消されているからでしょう。

// Parent static initializer
// Child1 static initializer
// Parent constructor 
// Child1 constructor 
new Child1();

// Child2 static initializer
// Parent constructor 
// Child2 constructor 
new Child2();

C#

class Parent<T>
{
    static Parent()
    {
        Console.WriteLine("Parent static constructor");
    }

    internal Parent()
    {
        Console.WriteLine("Parent constructor ");
    }
}

class Child1 : Parent<string>
{
    static Child1()
    {
        Console.WriteLine("Child1 static constructor");
    }

    internal Child1()
    {
        Console.WriteLine("Child1 constructor ");
    }
}

class Child2 : Parent<int>
{
    static Child2()
    {
        Console.WriteLine("Child2 static constructor");
    }

    internal Child2()
    {
        Console.WriteLine("Child2 constructor ");
    }
}

Child1とChild2を連続してインスタンス生成すると、Parentのstaticイニシャライザが2回呼ばれます。
C#では型削除は行われないので(内部実装的にはプリミティブは展開して、それ以外は共有している?)、別のクラスとして扱われるのでしょう。

これは別にstaticイニシャライザ/コンストラクタの機能の違いというわけではなく、
あくまで副次的なものだと思いますが、なかなかにややこしいです。

おまけ 自身のインスタンスをstaticに持つ場合

これはJavaC#も挙動は同じなのですが、シングルトンパターンなんかでよくやる、
自分自身のインスタンスをstaticで保持する場合の呼び出し順が難しいのでついでに見ておきます。

class Parent {
    
    static {
        System.out.println("Parent static initializer");
    }

    Parent() {
        System.out.println("Parent constructor ");
    }
}

class Child extends Parent {
    
    static Child singleton = new Child();
    
    static {
        System.out.println("Child static initializer");
    }

    private Child() {
        System.out.println("Child constructor ");
    }
}

なんでstaticがあとにくるんじゃーい。

// Parent static initializer
// Parent constructor 
// Child constructor 
// Child static initializer
Child child = Child.singleton;

ほかにも違いがあるんですかね

普通にコードを書いている上ではそんなに出会うパターンではないとは思うのですが、
なんだかこの辺りって難しいですよね。

誰にでもわかりやすいコードを書けるようにしたいものです。