2

これは複雑ですが、Haskell と C++ テンプレートのメタプログラミングをやりすぎた人からの興味深い質問です。我慢してください

関数の特定の代数的プロパティをチェックするための一般的な Java コードを書いていますが、それらのいくつかに適切な型を考え出すのに苦労しています。

機能する例として、関数が交換可能であることを確認する関数を次に示します。

<E, R>
boolean checkCommutative( Binary<E,E,R> f
                        , Binary<R,R,Boolean> eq
                        , E a, E b)
{
    return eq.ap(f.ap(a,b), f.ap(b, a));
}

この関数fは次Eのように表示されます。_ReqRabEf(a,b)f(b,a)

次に、次のようにして、特定の関数C.plus(Integer,Integer)が交換可能であることをテストできます。

class Plus implements Binary<Integer, Integer, Integer> {
    Integer ap(Integer a, Integer b) { return C.plus(a,b); }
}

class Eq implements Binary<Integer, Integer, Boolean> {
    Boolean ap(Integer a, Integer b) { return a.equals(b); }
}

checkCommutative(new Plus(), new Eq(), rand(), rand());

そして、すべてが順調です。

今、私はもっと複雑なものを実装したいと思っています。Group<E>plus メソッドを持つジェネリック インターフェイスがあるとします。

interface Group<E> {
    E plus(E,E);
}

と の 2 つの実装があるTheIntegersとしTheRationalsます。

class TheIntegers  implements Group<Integer> { ... }
class TheRationals implements Group<Fraction> { ... }

Fここで、整数から有理数への一般的な関数が のgような関数と交換するという考えを捉えられるようにしたいと考えていGroup.plusます。最初のカットとして、次のようなものを書きたいと思います。

<E, R>
booleanCheckCommutesWith( Unary<E,R> f
                        , ?? g
                        , Binary<R,R,Boolean> eq
                        , E a, E b)
{
    return eq.ap(f.ap(g.ap(a,b)), g.ap(f.ap(a), f.ap(b));
}

class F implements Unary<TheIntegers, TheRationals> {
    Fraction ap (Integer x) { ... }
}

checkCommutesWith(new F(), new Plus(), new Eq(), rand(), rand());

ここでの質問は、型は何であるべきかというgことです。問題は、 が と の2gつの異なるタイプに適用されることです。具体的な例では、 と の両方を表現したいと考えています。ERgGroup<Integer>.plus(Integer,Integer)Group<Fraction>.plus(Fraction,Fraction)

checkCommutesWith2 つの呼び出しの唯一の違いg.apは消去されるジェネリック型であるため、上記の実装は機能しない可能性があります。そこで、ドメインと範囲をオブジェクトとして追加して、少し変更します。

boolean checkCommutesWith( Unary<DE,RE> f
                        , ?? g
                        , Binary<RE,RE,Boolean> eq
                        , S domain, S range
                        , E x, E y)
{
    return eq.ap( f.ap(g.ap(domain, x, y)),
                , g.ap(range, f.ap(x), f.ap(y))
                );
}

class Plus implements ??
{
    <E, G extends Group<E>>
    E ap (G gp, E x, E y) { return gp.plus(x,y); }
}

??では、インターフェース ( ) はどのように見えるべきでしょうか? これが C++ の場合、次のように記述します。

interface ?? <T> {
    <E, S extends T<E>>
    E ap(S, E, E);
}

しかし、AFAICT Java には template-template パラメーターに相当するものはありません。それが私が立ち往生しているところです。

checkCommutesWithこのコードを他の構造でも使用できるようにしたいので、 Group を の署名の一部として含めたくないことに注意してください。一般的な定義が可能であるべきだと思います(「すべき」の定義について:))。

更新/明確化ここでの重要な点は、セット間のマップの定義プロパティは、それらが eq() で交換されるということです。群準同型の特徴は、それらが plus() で交換できることです。環準同型の特徴的な性質は、それらが times() で通勤することです。私は、この考えを捉える一般的な commutesWith 関数を定義しようとしています。また、eq、plus、および times (およびその他の構造も) をカプセル化するための適切な抽象化を探しています。

4

3 に答える 3

1

Javaタイピングシステムは、チェックされていないキャストなしでこれを行うのに十分なほど強力ではないのではないかと心配しています。ただし、その影響を最小限に抑えることができます。

これを圏論的な用語で定義することを検討してください。これはすべてについて表現するのに十分一般的であり、グループ準同型はカテゴリオブジェクト間の射の具体的なケースです。

以下のコードに関する注意:1。私はあなたcheckCommutesWithmorphismTester関係にカレーしました。2.は、単なるパラメータではなくeq、のプロパティになりました。CategoryObject3. castToConcreteObjectofCategoryは、そのチェックされていないキャストを作成するために使用されます。GroupsCategoryこの例では、forのみGroupObjectが使用され、の異なる実装がない場合、これは安全ですCategoryObject<GroupsCategory, E>

public interface Category<C extends Category<C>>
{
    <E> CategoryObject<C, E>
    castToConcreteObject(CategoryObject<C, E> abstractObject);

    < DOM_E, COD_E
    , DOM_CO extends CategoryObject<C, DOM_E>
    , COD_CO extends CategoryObject<C, COD_E>
    >
    Binary<DOM_E, DOM_E, Boolean>
    morphismTester ( final Unary<DOM_E, COD_E> f
                   , DOM_CO domainObject
                   , COD_CO codomainObject
                   );
}


public interface CategoryObject<C extends Category<C>, E>
{
    Binary<E,E,Boolean> eq();
}


public interface GroupObject<E>
         extends CategoryObject<GroupsCategory, E>
{
    E plus(E a, E b);

    E invert(E a);

    @Override
    Binary<E,E,Boolean> eq();
}

public class GroupsCategory
  implements Category<GroupsCategory>
{
    @Override
    public <E>
    GroupObject<E>
    castToConcreteObject(CategoryObject<GroupsCategory, E> abstractObject)
    {
        return (GroupObject<E>) abstractObject;
    }

    @Override
    public < DOM_E, COD_E
           , DOM_CO extends CategoryObject<GroupsCategory, DOM_E>
           , COD_CO extends CategoryObject<GroupsCategory, COD_E>
           >
    Binary<DOM_E, DOM_E, Boolean>
    morphismTester ( final Unary<DOM_E, COD_E> f
                   , final DOM_CO abstractDomainObject
                   , final COD_CO abstractCodomainObject
                   )
    {
        final GroupObject<DOM_E> domainGroup
            = castToConcreteObject(abstractDomainObject);
        final GroupObject<COD_E> codomainGroup
            = castToConcreteObject(abstractCodomainObject);

        return new Binary<DOM_E, DOM_E, Boolean>()
        {
            @Override
            public Boolean ap(DOM_E a, DOM_E b)
            {
                return codomainGroup.eq().ap
                    (
                        codomainGroup.plus(f.ap(a), f.ap(b)),
                        f.ap(domainGroup.plus(a, b))
                    );
            }
        };
    }
}

おそらく、より多くのプロパティをチェックするには、各プロパティのカテゴリを定義する必要があります。次に、他のいくつかを拡張するカテゴリが存在する可能性があります。これは、オブジェクトのプロパティとオブジェクト間の射を正確に表すものになります。

于 2012-10-26T13:42:11.367 に答える
1

あなたの目的に十分かどうかはわかりませんが、JScienceライブラリで定義されている型構造を調べることができます。特に、 で指定されたインターフェイスはorg.jscience.mathematics.structure、有用な出発点となる場合があります。

于 2012-10-26T03:42:22.907 に答える
1

最初の概算として、次のコードを参照してください。

https://github.com/rfqu/CodeSamples/blob/master/src/so/SoFun.java

少なくとも、警告やエラーなしでコンパイルされます:)

于 2012-10-26T18:19:19.223 に答える