13

私は Java 8 とその機能についてさらに多くのことを学んでおり、Java 8 を使ってもっと練習したいと思っていました。たとえば、画面の境界を円で囲むための次の命令コードがあるとします。

if (circle.getPosition().getX() > width + circle.getRadius()){
    circle.getPosition().setX(-circle.getRadius());
}else if (circle.getPosition().getX() < -circle.getRadius()){
    circle.getPosition().setX(width + circle.getRadius());
}
if (circle.getPosition().getY() > height + circle.getRadius()){
    circle.getPosition().setY(-circle.getRadius());
}else if (circle.getPosition().getY() < -circle.getRadius()){
    circle.getPosition().setY(height + circle.getRadius());
}
  1. それを「機能化」しようとするとどうすればよいですか?多分いくつかの疑似コード?可変性と状態は、この例に固有のもののように思えます。
  2. 関数型プログラミングはゲーム開発に適していませんか? どちらも好きなので、合わせてみました。
4

7 に答える 7

3

最初のポイント:コードが何を達成すべきかを考えることで、例を「機能化」します。これは円があり、いくつかの条件に基づいて別の円を計算したいとします。しかし、なんらかの理由で、あなたの強制的な育成により、入力円と出力円が同じメモリ位置に格納されるべきであると思い込んでしまいます!

機能的であるためには、まず、記憶の場所を忘れて価値を受け入れることです。intすべての型を、またはjava.lang.Integer他の数値型と同じように考えてください。

たとえば、初心者が次のようなコードを示したとします。

double x = 3.765;
sin(x);
System.out.println("The square root of x is " + x);

sinそして、うまくいかないようだと不平を言います。その時、あなたはどう思いますか?

これを考慮してください:

Circle myCircle = ....;
wrapAroundBoundsOfScreen(myCircle);
System.out.println("The wrapped-around circle is now " + myCircle);

後者のコードが前者と同じくらいばかげているように見えるとき、関数型プログラミングへの最初のステップを登ったことになります。はい、これは、使用している命令型言語の特定の機能を使用しない、または非常に控えめに使用することを意味します。

于 2015-11-01T22:09:04.530 に答える
2

ここでは、「機能化」はあまり適用されません。しかし、少なくとも私たちは戦うことができmutabilityます。まず第一にpure functions。これは分離するのに役立ちますlogic。わかりやすく、テストしやすいようにします。質問に答えてください: あなたのコードは何をしますか? いくつかのパラメーターを受け取り、2 つのパラメーター newxとを返しますy。次のサンプルは pseudoscala で作成されます。

したがって、x と y の両方の計算で 2 回呼び出される関数が必要です。

def (xOrY: Int, widthOrHeight: Int, radius: Int): Int = {

if (x > widthOrHeight + radius) -1*radius else  widthOrHeight + radius
// do your calculation here - return x or y values.

}

PS> これまでのところ、関数型スタイルを適用する場所に関係なく: ビジネス ロジックを実行する必要があるため、関数型アプローチを使用することをお勧めします。

ただし、役に立たないため、過度に複雑にしないでください。したがって、このサンプルで私がやらないことは次のとおりです (疑似 scala が次に続きます)。

def tryToMakeMove(conditions: => Boolean, move: => Unit) = if (conditions) move()
/// DO NOT DO IT AT HOME :)
tryToMakeMove(circle.getPosition().getX() > width + circle.getRadius(),  circle.getPosition().setX(-circle.getRadius())).andThen()
   tryToMakeMove(circle.getPosition().getX() < -circle.getRadius()), circle.getPosition().setX(width + circle.getRadius()))
).andThen ... so on.

これは、関数型プログラムがどのように見えるかです。高階関数 (他の関数を引数として受け取り、内部で呼び出す関数) を作成しました。この関数を使用して、実行しなければならない操作を 1 対 1 で呼び出しました... しかし、そのような機能的なスタイルは実際には役に立ちません。まったく。コードを単純化する場所にのみ適切に適用する必要があります。

于 2015-11-01T22:36:39.560 に答える
1

関数型プログラミングはゲーム開発に適しています (なぜそうならないのでしょうか?)。問題は通常、パフォーマンスとメモリ消費に関するものであり、機能するゲーム エンジンが既存の機能しないゲーム エンジンよりもこれらの指標で勝てるかどうかです。関数型プログラミングとゲーム開発が好きな人はあなただけではありません。John Carmack もそうしているようです。02 :05 から始まるQuakecon 2013 のトピックに関する彼の基調講演をご覧ください。彼のメモはここここにあり、機能的なゲーム エンジンをどのように構築できるかについての洞察さえ与えてくれます。

理論的な基礎はさておき、通常、関数型プログラミングに固有であると認識されている概念が 2 つあります。それらは、データの不変性状態の不在です。前者はデータが変更されないことを意味し、後者はすべてのタスクが事前知識なしで初めて実行されることを意味します。それを考慮すると、命令型コードには 2 つの問題があります。セッターが円の位置を変更し、コードが と の外部値 (グローバルな状態) にwidth依存していることですheight。それらを修正するには、関数が更新ごとに新しい円を返し、画面解像度を引数として取得するようにします。動画の最初の手がかりを当てはめてみましょう世界の静的スナップショットへの参照と、「更新」されているエンティティへの参照 (単にthisここにあります) を更新関数に渡します。

class Circle extends ImmutableEntity {

        private int radius;

        public Circle(State state, Position position, int radius) {
            super(state, position);

            this.radius = radius;
        }

        public int getRadius() {
            return radius;
        }

        @Override
        public ImmutableEntity update(World world) {
            int updatedX = getPosition().getX();
            if (getPosition().getX() > world.getWidth() + radius){
                updatedX = -radius;
            } else if (getPosition().getX() < -radius){
                updatedX = world.getWidth() + radius;
            }

            int updatedY = getPosition().getX();
            if (getPosition().getY() > world.getHeight() + radius){
                updatedY = -radius;
            } else if (getPosition().getY() < -radius) {
                updatedY = world.getHeight() + radius;
            }

            return new Circle(getState(), new Position(updatedX, updatedY), radius);
        }
    }

    class Position {

        private int x;
        private int y;

       //here can be other quantities like speed, velocity etc.

        public Position(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }
    }

    class State { /*...*/ }

    abstract class ImmutableEntity {

        private State state;
        private Position position;

        public ImmutableEntity(State state, Position position) {
            this.state = state;
            this.position = position;
        }

        public State getState() {
            return state;
        }

        public Position getPosition() {
            return position;
        }

        public abstract ImmutableEntity update(World world);
    }

    class World {
        private int width;
        private int height;

        public World(int width, int height) {
            this.width = width;
            this.height = height;
        }

        public int getWidth() {
            return width;
        }

        public int getHeight() {
            return height;
        }
    }

ここで注意が必要なのは、世界や他のエンティティの状態にどのように影響を与えるかです。ビデオの 2 番目の手がかりをたどり、イベント パッシング メカニズムを使用してそのような変更を行ったり来たりして、ゲームの残りの部分がすべての効果を認識できるようにします。

明らかに、サークルの位置を変更する場合でも、イベントのみを保持し、それらに完全に依存することができます. したがって、エンティティに一種の id を導入すると、渡すことができますMoveEntity(id, newPosition)

于 2015-11-08T13:42:07.440 に答える
1

それを「機能化」しようとするとどうすればよいですか?多分いくつかの疑似コード?可変性と状態は、この例に固有のもののように思えます。

可変性をいくつかの関数に制限し、関数内で final 変数を使用することもできます (これにより、ステートメントではなく式を使用する必要があります)。考えられる方法の 1 つを次に示します。

Position wrapCircle(Circle circle, int width, int height) {
  final int radius = circle.getRadius();
  final Position pos = circle.getPosition();
  final int oldX = pos.getX();
  final int x = (oldX > width + radius) ? -radius : (
        (oldX < -radius) ? (width + radius) : oldX);

  final int y = // similar

  return new Position(x, y);
}

circle.setPosition(wrapCircle(circle, width, height));

さておき、クラスwrapCircleのメソッドを作成して、次を取得します。Circle

circle.wrapCircle(width, height);

または、さらに一歩進んでgetWrappedCircle、新しい円のインスタンスを返すメソッドを定義することもできます。

Circle getWrappedCircle(width, height) {
  newCircle = this.clone();
  newCircle.wrapCircle(width, height);
  return newCircle();
}

.. 残りのコードをどのように構成するかによって異なります。

ヒント: finalJava ではできるだけ頻繁にキーワードを使用してください。それは自動的により機能的なスタイルに役立ちます。

関数型プログラミングはゲーム開発に適していませんか? どちらも好きなので、合わせてみました。

純粋な関数型プログラミングは、データのコピー/クローンを大量に必要とするため、処理が遅くなります。パフォーマンスが重要な場合は、上記のように混合アプローチを試すことができます。

できるだけ多くの不変性を使用し、続いてベンチマークを行い、パフォーマンスの重要なセクションでのみ可変性に変換することをお勧めします。

于 2015-11-08T07:31:44.150 に答える
1

関数型コードはほぼすべてのプログラミング言語で記述できますが、どの言語でも関数型プログラミングを簡単に学ぶことはできません。特に Java は、関数型プログラミングを非常に困難にするため、JVM で関数型プログラミングを行いたいと考えた人々は Clojure と Scalaz を思い付きました。機能的な考え方 (どのような問題が自然にどのように処理されるか、どのような問題がより厄介で、どのように処理されるかなど) を学びたい場合は、関数型またはほとんど機能的なものに時間を費やすことを強くお勧めします。言語。言語の質、関数型イディオムへの固執のしやすさ、学習リソース、およびコミュニティの組み合わせに基づいて、私が一番に選ぶのは Haskell で、次は Racket です。もちろん、他の人は別の意見を持っているでしょう。

于 2015-10-13T19:27:00.740 に答える