2014年08月17日

C#で隣り合う二項の差を求める

   このエントリーをはてなブックマークに追加 Clip to Evernote
どう書く?orgに感謝を込めて」シリーズ その33

■問題 (出題者:にしお さん)
整数のリストがxsが与えられたときに、隣り合う2要素の差のリストを作る関数diffを作ってください。
サンプル入力
[3, 1, 4, 1, 5, 9, 2, 6, 5]
サンプル出力
[-2, 3, -3, 4, 4, -7, 4, -1]  
どの問題もそうですが、いろんな書き方が考えられます。
この問題では4つのバージョンを書いてみました。

最初は、IList<int> を受け取って、添え字を使って各要素にアクセスするもの。 配列など他のコレクションを受け取れないのが欠点です。

■C#のコード(その1)
using System;
using System.Collections.Generic;
using System.Linq;

namespace Doukaku.Org {
    class Program {
        static void Main(string[] args) {
            var nums = new List<int> { 3,1,4,1,5,9,2,6,5 };
            var diff = Diff(nums);
            PrintList(diff);
            Console.ReadLine();
        }

        // IList<int>を受け取り、IList<int>を返す例
        static IList<int> Diff(IList<int> xs) {
            List<int> result = new List<int>();
            for (int i = 0; i < xs.Count() - 1; i++) {
                result.Add(xs[i + 1] - xs[i]);
            }
            return result;
        }

        static void PrintList(IEnumerable<int> nums) {
            string s = string.Join(", ", nums.Select(x => x.ToString()).ToArray());
            Console.WriteLine("[{0}]", s);
        }
    }
}


2つめは IEnumerable<int>を受け取り、 yield return で結果を列挙する例 (MoveNext、Currentを使用) やっぱり、MoveNext, Currentは面倒くさいですね。

■C#のコード(その2)
        static IEnumerable<int> Diff(IEnumerable<int> xs) {
            var ite = xs.GetEnumerator();
            if (ite.MoveNext())
                for (int prev = ite.Current; ite.MoveNext(); prev = ite.Current) {
                    yield return ite.Current - prev;
                }
        }

次は、IEnumerable<int>を受け取り、 yield return で結果を列挙する例 (foreachを使用) ループの中で、最初か最初でないかを判断しているところが気に入らないです。

■C#のコード(その3)   
        static IEnumerable<int> Diff(IEnumerable<int> xs) {
            bool first = true;
            int prev = int.MinValue;
            foreach (var n in xs) {
                if (!first)
                    yield return n - prev;
                prev = n;
                first = false;
            }
        }

そして最後が、再帰を使ったもの。 メソッドが2つに分かれてしまうのが、玉に瑕ですね。

■C#のコード(その4)
static IEnumerable<int> Diff(IEnumerable<int> xs) {
    if (xs.Any())
        return Diff5(xs.First(), xs.Skip(1));
    return new int[] { };
}

static IEnumerable<int> Diff(int prev,IEnumerable<int> xs) {
    if (xs.Any()) {
        int b = xs.First();
        yield return b - prev;
        foreach (var x in Diff5(b, xs.Skip(1)))
            yield return x;
    }
}

どれも一長一短ですね。あなたならどう書く?


 

この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/gushwell/52379839