2014年06月29日

C#でシードを固定した乱数を生成する

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

■問題 (出題者:ところてん さん)
シードを固定した疑似乱数を出力してください。 数回実行して、常に同じ結果が出力されることを確認してください。

数回実行して、常に同じ結果が出力されることを確認してください、ということなので、 GetRandomNumbers というメソッドを定義し、 それを繰り返し呼び出し、一致するかどうかを確認するコードを書いてみた。

■C#で書いたコード
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Doukaku.Org {
    class Program {
        static void Main(string[] args) {
            var nums1 = GetRandomNumbers(30).ToArray();
            for (int i = 0; i < 10; i++) {
                var nums2 = GetRandomNumbers(30);
                Debug.Assert(nums1.SequenceEqual(nums2));
            }
            Console.WriteLine("OK");
            Console.ReadKey();
        }

        static IEnumerable<int> GetRandomNumbers(int count) {
            Random rnd = new Random(1);  //シードを固定
            for (int i = 0; i < count; i++) {
                yield return rnd.Next();
            }
        }
    }
}
  

Posted by gushwell at 21:32Comments(0)TrackBack(0)

2014年06月25日

C#で西暦から和暦変換

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

■問題 (出題者:ocean さん)
西暦を和暦に変換するプログラムを書いてください。元号の切り替わる日など、
複数の表記が可能な場合には両方表示し、西暦が無効な日付の場合には「範囲外」と
表示するようにしてください。対応すべき日付は明治元年以降とします。

>a.py 1868/12/2
明治1年12月2日

>a.py 1926/12/24
大正15年12月24日

>a.py 2007/12/01
平成19年12月1日

>a.py 1926/12/25
大正15年12月25日 昭和1年12月25日

>a.py 1868/1/2
範囲外

>a.py 1868/100/2
範囲外

「複数の表記が可能な場合には両方表示し」という要求を満たすコードの部分では、元号を漢字2文字に限定したコードとしています。

■C#で書いたコード
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace Doukaku.Org {
    class Program {
        static void Main(string[] args) {
            // あくまでもJapaneseCalendar を使うことにこだわったコード
            while (true) {
                string s = Console.ReadLine();
                try {
                    CultureInfo culture = new CultureInfo("ja-JP", true);
                    culture.DateTimeFormat.Calendar = new JapaneseCalendar();
                    DateTime datetime = DateTime.Parse(s);

                    string s1 = datetime.ToString("ggy年M月d日", culture);
                    string s2 = datetime.AddDays(-1).ToString("ggy年M月d日", culture);
                    if (s1.Substring(0, 2) != s2.Substring(0, 2))
                        s1 += " " + s2.Substring(0, 2) + s1.Substring(3);
                    Console.WriteLine(s1);
                } catch {
                    Console.WriteLine("範囲外です");
                }
            }
        }
    }
}
  
Posted by gushwell at 23:00Comments(0)TrackBack(0)

2014年06月22日

C#で世界時計

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

■問題 (出題者:sawatさん)
プログラムを実行した端末のロケールと、グリニッジ標準時と、ロサンゼルスの現在時刻を
それぞれ表示してください。 
時刻の表示はリアルタイムでなく、一回限りで構いません。 
時刻のフォーマットは自由とします。

出力例
現在の時刻は、2008年10月29日 11時36分21秒です。
グリニッジ標準時刻は、2008年10月29日 2時36分21秒です。
アメリカ・ロサンゼルスの時刻は、2008年10月28日 19時36分21秒です。

TimeZoneInfoクラスを使って実装。ついでに、TimeZoneInfoの他のプロパティを使った
コードも書いてみた。

■C#で書いたコード
using System;

namespace Doukaku.Org {
    static class Program {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main() {
            //現在のローカル時間を取得
            DateTime nowTime = DateTime.Parse("2008/10/29 11:36:21");
            Console.WriteLine("現在の時刻は、{0:yyyy年M月d日 H時m分s秒}です。", nowTime);

            //現地時間に対応する世界協定時刻の取得
            DateTime utc =
                System.TimeZoneInfo.ConvertTimeToUtc(nowTime, System.TimeZoneInfo.Local);
            Console.WriteLine("グリニッジ標準時刻は、{0:yyyy年M月d日 H時m分s秒}です。", utc);

            var tzi = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
            DateTime pst =
                System.TimeZoneInfo.ConvertTime(nowTime, tzi);
            Console.WriteLine("アメリカ・ロサンゼルスの時刻は、{0:yyyy年M月d日 H時m分s秒}です。", pst);

            // 問題の解答はここまで

            // 以下は、TimeZoneInfo の各プロパティを確認するコード

            //世界協定時刻(UTC: Coordinated Universal Time)オフセットの取得
            TimeSpan baseUtcOffset = System.TimeZoneInfo.Local.BaseUtcOffset;
            Console.WriteLine("BaseUtcOffset:{0}", baseUtcOffset);

            //現在の時間でのUTCオフセットの取得
            TimeSpan utcOffset = System.TimeZoneInfo.Local.GetUtcOffset(nowTime);
            Console.WriteLine("GetUtcOffset:{0}", utcOffset);

            //世界協定時刻に対応する現地時間の取得
            DateTime localTime =
                System.TimeZoneInfo.ConvertTimeFromUtc(utc, System.TimeZoneInfo.Local);
            Console.WriteLine("ToLocalTime:{0}", localTime);

            Console.ReadLine();
        }
    }
}
  
Posted by gushwell at 22:02Comments(0)TrackBack(0)

2014年06月18日

C#で制限時間内のキー入力検査

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

■問題 (出題者:raynstardさん)
ユーザーの入力を検査する関数 InputCheckerを作成して 3回連続実行してください。
関数 InputCheckerは、以下の仕様を満たしてください。

     1. 引数に 時間(秒) Nと文字列 Sを受け取ること。
     2. ユーザからの標準入力とあらかじめ指定された S を比較し、 一致すれば「OK」、
         一致しなければ「NG」を標準出力へ出力すること。
     3. ユーザーの入力がN秒以内に完了しなかった場合は、比較せず 「TIME OUT」を標準出力へ
         出力すること。
     4. 経過時間はユーザーが入力を開始した地点から計測すること。
     5. ENTER キーの入力によってユーザー入力が完了したと仮定すること。
     6. ユーザの入力が完了するまでは、完了を待ち続けること

たとえば、「InputCheker(5, "ABCDEF")」と指定した場合、 出力例はこんな感じです。

     1. input(ABCDEF) =>ABCDEF<ENTER>

          1. result => OK
          2. result => NG
          3. result => TIME OUT


1. input(ABCDEF) =>
と出力して入力待ちをし、ユーザーが「ABCDEF<ENTER>」を入力したとき、 入力開始から5秒以内ならば「OK」、5秒をこえていれば「TIME OUT」を出力します。 このとき、ユーザーがキーを押下しなければ1. を出力してから たとえ10秒たっていても「TIME OUT」にはならないので注意してください。 時間計測はあくまでユーザーが入力を開始してからです。

問題文が長いので、面倒そうな印象を受けますが、実際に書いてみるとそれほどでもないです。
「ユーザーが入力を開始した地点」というのをどうやって調べるかがこのプログラムの肝になりますが、Console.KeyAvailable プロパティを使えば簡単に分かります。


■C#で書いたコード

using System;
using System.Threading;

namespace Doukaku.Org {
    class Program {
        // きれいなコードじゃないけど...
        static void InputCheker(int n, string s) {
            Console.Write("input({0}) => ", s);
            while (!Console.KeyAvailable)
                Thread.Sleep(100);
            DateTime now = DateTime.Now;
            string input = Console.ReadLine();
            string result = (DateTime.Now <= now.AddSeconds(n))
                ? s == input ? "OK" : "NG"
                : "TIME OUT";
            Console.WriteLine("result => {0}",result);
        }
        static void Main(string[] args) {
            InputCheker(5, "ABCDEF");
            InputCheker(4, "Microsoft");
            InputCheker(3, "csharp");
            Console.ReadLine();
        }
    }
}
  
Posted by gushwell at 22:30Comments(0)TrackBack(0)

2014年06月15日

C#でワーカスレッドを安全に終了させるまで待機

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

■問題 (出題者:todogzm さん)
スレッドプールに複数のワーカスレッドが待機しており、メインスレッドはいつでもワーカスレッドに仕事を渡せるような状態になっているとします。
さて、メインスレッドからスレッドプールにいくつか仕事を与え、メインスレッドは与えた仕事すべてが終了するまで待機し、次の処理に行ってはいけない、というようなコードを書いてください。
#現実に書く機会が多そうなコードですね…。
ここでの仕事の内容は、適当に5秒から15秒の間スレッドをスリープする、というもので結構です。 また、ワーカスレッドのスレッドプール自体の使用を終了するか、または残して再利用するかは問いません。できればコメントにスレッドプールを残したかどうかを書いてください。

汎用的なクラスMyThreadPoolクラスを作成しています。
戻り値を得たいときは、MyThreadPoolクラスをちょっと改造すればOK!
使い方は、Mainメソッド見てもらえれば分かると思います。

MyThreadPoolのようなクラスを作れば、仕事が終わったかどうかは、MyThreadPoolクラス側がやってくれるので、仕事をする MyWork メソッドは、仕事そのものに専念できます。
.NET 4.5 の今ならばTaskクラス使ったほうがいいかなと思いますが...

■C#で書いたコード
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;

namespace Doukaku.Org {
    class Program {

        static void Main(string[] args) {
            MyThreadPool tp = new MyThreadPool();
            tp.Enqueue(MyWork,7);
            tp.Enqueue(MyWork,12);
            tp.Enqueue(MyWork,15);
            tp.Enqueue(MyWork,2);
            tp.WaitAll();
            Console.WriteLine("Completed");
            Console.ReadLine();
        }

        static void MyWork(object state) {
            var id = System.Threading.Thread.CurrentThread.ManagedThreadId;
            Console.WriteLine("Stread Start {0} arg={1}",id, state);
            Thread.Sleep((int)state * 1000);
            Console.WriteLine("Stread End {0}", id);
        }
    }

    public class MyThreadPool {
        List<WorkItemInfo> list = new List<WorkItemInfo>();

        public void Enqueue(WaitCallback callback, params object[] args) {
            var workItemInfo = new WorkItemInfo {
                Args = args,
                MethodInfo = callback.Method,
                AutoResetEvent = new AutoResetEvent(false),
            };
            list.Add(workItemInfo);
            WaitCallback wc = new WaitCallback(this.Call);
            ThreadPool.QueueUserWorkItem(wc, workItemInfo);           
        }

        private void Call(object state) {
            WorkItemInfo info = (WorkItemInfo)state;
            info.MethodInfo.Invoke(null, info.Args);
            info.AutoResetEvent.Set();
        }

        public void WaitAll() {
            foreach (var info in list) {
               info.AutoResetEvent.WaitOne();
            }
        }

        private class WorkItemInfo {
            public MethodInfo MethodInfo { get; set; }
            public object[] Args { get; set; }
            public AutoResetEvent AutoResetEvent { get; set; }
        }
    }
}
  
Posted by gushwell at 21:30Comments(0)TrackBack(0)

2014年06月11日

C#で実行時間の測定

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

■問題  (出題者:にしお さん)

ある比較的重たい処理をする関数「func」があるとします。 この関数を呼び出して、その実行にかかった時間を表示する関数「profile」を書いてください。 言語として可能であれば、「profile」が「func」と同じ引数で呼び出せるようにしてください。


なかなか面白い問題ですね。
ここでは、func1, func2 の2つのメソッドを呼び出し、それぞれの時間計測をするコードを書いています。
C#らしく profile関数ではなく、Profileクラスを作成してみました。 引き数の数も自由ですし、戻り値も返せます。 

■C#で書いたコード
using System;
using System.Reflection;
using System.Diagnostics;

namespace Doukaku.Org {
    class Program {
        static void Main(string[] args) {
            {
                var profile = new Profile(new Func<string, int>(func1));
                var r = profile.Call("Hello world");
                Console.WriteLine("結果={0}",r);
                Console.WriteLine("実行時間={0}",profile.Elapsed);
            }
            Console.WriteLine();
            {
                var profile = new Profile(new Func<int, int, string>(func2));
                var r = profile.Call(123, 456);
                Console.WriteLine("結果={0}", r);
                Console.WriteLine("実行時間={0}", profile.Elapsed);
            }
        }

        static int func1(string s) {
            Console.WriteLine(s);
            return s.Length;
        }

        static string func2(int n1, int n2) {
            Console.WriteLine(n1);
            Console.WriteLine(n2);
            return (n1 + n2).ToString();
        }
    }

    // 時間計測クラス
    class Profile {
        private MethodInfo methodInfo;

        // コンストラクタ
        // 引き数には、時間計測したいメソッドを渡す。
        public Profile(Delegate func) {
            this.methodInfo = func.Method;
        }

        // コンストラクタで指定したメソッドを呼び出し、その時間を計測する。
        // 計測時間は、Elapsedプロパティにセットする。
        public object Call(params object[] args) {
            Stopwatch sw = Stopwatch.StartNew();
            var r = methodInfo.Invoke(null, args);
            Elapsed = sw.Elapsed;
            return r;
        }

        public TimeSpan Elapsed { get; private set; }
    }
}
  
Posted by gushwell at 22:00Comments(0)TrackBack(0)

2014年06月08日

C#でメソッドの一覧と呼び出し

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

■問題 (出題者:にしお さん)

「ある与えられたオブジェクトtargetのメソッドのうち、 "test_"で始まるものをすべて呼びだす」というコードを書いてください。 引数に関しては都合のいいように仮定して構いません(全部0個、など)。
 メソッドという概念がない言語の場合は、 「複数の関数への参照を持っているようなオブジェクト(たとえばパッケージとかモジュールとか)から"test_"で始まる関数をすべて呼び出す」と読み替えても構いません。


.NETなので、Test_ (先頭大文字)で始まるメソッドとしました。 この例では、面倒なので、引き数は0個としています。

■C#で書いたコード

using System; using System.Linq; namespace Doukaku.Org { class Program { static void Main(string[] args) { // Sample クラスの Test_xxxx メソッドをすべて呼び出す。 Sample sample = new Sample(); sample.GetType().GetMethods() .Where(m => m.Name.StartsWith("Test_")) .ForEach(m => m.Invoke(sample, null)); } } class Sample { public void Test_Bar() { Console.WriteLine("Test_Bar"); } public void Test_Foo() { Console.WriteLine("Test_Foo"); } public void Test_Baz() { Console.WriteLine("Test_Baz"); } public void Hoge() { Console.WriteLine("Hoge"); } } }
  
Posted by gushwell at 21:30Comments(0)TrackBack(0)

2014年06月04日

C#で横向きのピラミッドを作る

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



■問題 (出題者:takano32 さん)
与えられた自然数 n について、横向きで n 段のピラミッドを作ってください。 たとえば、 n に 4 が与えられた場合は以下のようなピラミッドを作ります。
*
**
***
****
***
**
*
与える自然数についてはリテラルで与える、標準入力で与える、引数で与えるなどは自由とします。

ノーマルな方法とLINQを使った方法で書いてみました。

■C#で書いたコード
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Doukaku.Org { class Program { static void Main(string[] args) { YokoPyramidByLinq(4); } // ノーマルな方法 static void YokoPyramid(int n) { // 上半分 (中心含む) for (int i = 1; i <= n; i++) { Console.WriteLine(new string('*', i)); } // 下半分 for (int i = n - 1; i > 0; i--) { Console.WriteLine(new string('*', i)); } } // LINQで static void YokoPyramidByLinq(int n) { foreach (var star in Enumerable.Range(1, n * 2 - 1).Select(i => i > n ? 2 * n - i: i)) Console.WriteLine(new string('*', star)); } } }
  
Posted by gushwell at 21:57Comments(0)TrackBack(0)