感想一覧

▽感想を書く
感想絞り込み
全て表示
1 2 Next >> 
[一言]
もっと短くなった。

using System;
using System.Linq;
using System.Collections.Generic;

namespace ConsoleApp1
{
  class Program
  {
    const int Skipped = -1;

    static int CalcScore2(int[][] frames) =>
      Enumerable.Range(0, frames.Length)
        .Select(x => frames.Skip(x).SelectMany(x => x).Where(x => x != Skipped).Take(3).ToList())
        .Sum(x => x switch
        {
          var y when y[0] == 10 || y[0] + y[1] == 10 => y.Sum(),
          var y => y[0] + y[1]
        });

    static void Main(string[] args)
    {
      var scores = new int[][]
      {
        new int[] {10, Skipped },
        new int[] {10 },
        new int[] {10, Skipped },
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {10,Skipped },
        new int[] {10, 10, 10 },
      };

      Console.WriteLine(CalcScore2(scores));
    }
  }
}

[一言]
で。10フレの話を理解したうえで、効率無視して作成した、多少モダンぽい? c#コード。短さ優先。正しいかどうかは不明。

using System;
using System.Linq;

namespace ConsoleApp1
{
  class Program
  {
    const int Skipped = -1;

    static IQueryable<int> ToRaw(IQueryable<int[]> src, int skip) => src.SelectMany(x => x).Where(x => x != Skipped).Skip(skip);

    static int CalcScore(IQueryable<int[]> frames) => frames.FirstOrDefault() switch {
      null => 0,
      var x when x[0] == 10 => 10 + ToRaw(frames, 1).Take(2).Sum() + CalcScore(frames.Skip(1)),
      var x when x[0] + x[1] == 10 => 10 + ToRaw(frames, 2).First() + CalcScore(frames.Skip(1)),
      var x => x[0] + x[1] + CalcScore(frames.Skip(1))
    };

    static void Main(string[] args)
    {
      var scores = new int[][]
      {
        new int[] {10, Skipped },
        new int[] {10 },
        new int[] {10, Skipped },
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {5,5 },
        new int[] {10, 10, 10 },
      };

      Console.WriteLine(CalcScore(scores.AsQueryable()));
    }
  }
}

[一言]
おお。10フレのシステムというのは、ストライクやスペアの場合の先読みを、そのフレーム内で表記するため、と理解できるのか… もともとがそういう思想での構築だったのかもしれないけれど、一般には「10フレは特別、2投までに全部倒したら、もう一投おまけで投げられる。10フレはストライクやスペアの特殊処理をしないで、フレーム全体のピン数を加算する」と理解されていると思うし、自分もそうだったのでこのアルゴリズムが理解できていなかったのでした。

その意味での計算はあくまでフレーム単位で行う必要があるので、フレームとしてのデータ構造と無投を除いたシーケンスとしてのポイント構造を同時に扱う必要は出てくるのだな。たおしたピン数だけの一次元リストだと、末端の処理がやはりうまく行かないか。

10フレの秘密に気が付けたというのが大きな収穫でした/w
しかし、さすがにこれは、誰得話になっちゃうなあ…
[一言]
なるほど?
10フレの考え方というか、取り扱いが全く違うんだ。
ちょっと考えてみます。
[一言]
あ、これ、インデント用の全角スペースをそのままWhite Spaceと解釈してコンパイルできるということ、です。
変数名のUnicode対応とかは、その流れ、ですね。MSのはそういった点しっかりしていると思う。

Scalaのプログラム、ideoneで実行してみようとしたけれど、あれはインタプリタでないからとりあえずダメだったみたいだけれど、まあともかくそのままコピペしたら全角スペースがエラーとなってしまったようです。
[一言]
あとね。c#の凄いとこ。
このコード、そのままエラーなしでコンパイル・実行できるのだ。他の言語って、大抵だめだと思う。
勿論、漢字関数名・変数名・クラス名も何ら問題無し。
SQL Serverで(Oracleはうまくない)テーブル名・カラム名をみんな漢字にしてEntityFrameworkでマッピング作成すると、物理名の対応表なしできっちりプログラミングできる/w
もともと、Linqは関数型プログラミング言語由来の機能ではあるので、Scala/Haskell/OCamlとかの関数型プログラミング言語方面だと割と似たようなコードがそのまま実行できる気がします。

ユニコード対応(つまり、漢字の識別子が使える)も最近の多くの言語では対応してたりしますね(JavaもScalaもKotlinも、.NET系のほかの言語でも、RubyもPythonも対応してるので、今はあんまり珍しくないです)

RDBMSでどのくらい漢字のテーブル名やカラム名が対応してるのかはわからないですが、MySQLとかは一応できるぽいですね(ただ、文字コード絡みで問題起こりやすいので、あんまり使われることはないですが)
[一言]
あれ、9フレストライクで、10フレが3/5で3投目Skippedにすると、ストライクパターンにマッチしなくない?
最初のLazyList作るフェーズで、

10 : 3 : 5 : null

みたいな感じに前処理で変換されているので、10の次に3と5が来るとちゃんと3と5がストライクパターンとしてマッチして加算される(はず)です。
[一言]
うーん。やっぱり基本的に判りやすいか、というとどうなんだろう、と思っちゃう。

・LazyListであるか、というのは必ずしも本質ではないと思われ。デバッグの段階を考えると、加工済みリストを一度作った方がやりやすいと思う。
・LazyList化は明示的にしないといけないようだけれど、Linqなら暗黙のうちに遅延評価されるので、偉いのだ/w
・結局、10フレの特別扱いは必要。それが_によってなされている、ということはさすがにコメントなしですぐに理解するのは困難
・10フレの1/2投目が最終パターンにマッチするのは理解できるけれど、3投目がどのパターンにマッチするのかは、理解できない。10フレが5/5/5の場合にどうなるのだろう

入力データ形式が他と異なっているので、そのまま比較するのも適当ではないと思う。
きちんと作るなら、Frameクラスも作成して、Strike/Spareの判定もそのプロパティとしてカプセル化するとかしたい。
さらに補足すると、パターンマッチのロジックでは、10フレーム目の「特別扱い」はしてないんですね。10フレームで終わったから、結果的に特別扱いしてるように見えるだけで。無限フレームボウリングをモデリングした結果として、10フレームで終わる通常のボウリングもその中で取り扱えるという感じです。
一般的にわかりやすいかというと議論があると思います。Scala版(というか、遅延リスト使える言語ならどれでも簡単かと思いますが)の特徴は、ボウリングのルールを

・スペアは、「次の一投(あれば)」をボーナスとして加算する
・ストライクのは、「次の二投(あれば)」をボーナスとして加算する
・何フレーム投げるかは事前には決めないでいい

と抽象化して、「10フレームで打ち切った」のが通常のボウリングと考えればそのまま動くことですね。Skippedを入れたのは「投球してない」ということをデータで表現してあるのでした。

あんまり意味がないことですが、15投目までがあるボウリングも抽象化されている(ので、10フレームで打ち切ってもうまく動く)ので面白いところでしょうか。

LazyListが本質かどうかといわれると必ずしも本質でないのですが、LazyListであれば再帰をストップさせなくても、使う側でtake(10)すればいいというのがメリットですね(実用上はLazyListがそこまで使い物になるかは微妙ですが)。

10フレーム目の3投目(で5/5/5、つまりスペアということですよね)がどのパターンにマッチするかというと、2パターン目のスペアのパターンにマッチします。
[一言]
c#のサンプル。ストライクの場合のリスト末尾省略可なパターン。
明確なフレーム数の規定がある(リストが不定長でない)し、フレーム番号に依存する特殊処理がある、ということからリストを加工するよりは普通にインデクサでアクセスするほうが筋な気がするので。
ちょっとなんだけれど、配列にFirst/Sum/Takeが使えるのがミソかな。StrikeでTake(2)を漏らすと、誤りになる。

using System;
using System.Linq;

namespace ConsoleApp1
{
  class Program
  {
    static int CalcScore(int[][] scores)
    {
      var total = 0;

      // 1..9 frames
      for (var i = 0; i < 9; i++)
      {
        total += scores[i].Sum();

        if (scores[i].First() == 10)
        {
          // strike
          total += scores[i + 1].Take(2).Sum();
          if (i < 8 && scores[i + 1].First() == 10)
          {
            // double or more
            total += scores[i + 2].First();
          }
        }
        else if (scores[i].Sum() == 10)
        {
          // spare
          total += scores[i + 1].First();
        }
      }

      // 10 frame
      return total + scores[9].Sum();
    }

    static void Main(string[] args)
    {
      var scores = new int[][]
      {
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {10 },
        new int[] {10, 10, 10 },
      };

      Console.WriteLine(CalcScore(scores));
    }
  }
}

おお。C#版。ありがとうございます。ちなみに、Scala版で9フレーム目と10フレーム目に特殊な処理がないのは、ボウリングでは、「第10フレームに特殊な処理がある」と考えなくても、単純にストライクは「(あれば)次の2投の点数を加算する」、スペアは「(あれば)次の1投の点数を加算する」と考えられるからですね。

それが遅延リストとパターンマッチで表現されている感じです。
[一言]
ああ、流石にこれは理解の範囲外。
パターンマッチング使ってるのかな。
たしかc#にもパターンマッチ導入されたのだけれど、理解してない。
おっしゃる通り、パターンマッチング使ってます。こうするとある種の処理が簡潔にかけるのがいいところでしょうか。

Rubyしかり、C#しかり、Java(予定)しかりとパターンマッチングはメインストリームの言語にドンドン入って来てますね。
1 2 Next >> 
↑ページトップへ