[C#]R.NETを使う(導入)

C#でRを利用しようとした場合の備忘録 導入

  1. PCにRをインストール(64bitOSの場合、32bit / 64bit両方のRをインストールすること)
  2. VSで、参照の「NuGet パッケージの管理」から、"R.NET.Community"をインストール
  3. VSで、以下のコードを記述

REngine.SetEnvironmentVariables(
    rPath: @"C:\Program Files\R\R-3.2.3\bin\i386",
    rHome: @"C:\Program Files\R\R-3.2.3");

using (var engine = REngine.GetInstance())
{
    // Rでの処理を記述
}

SetEnvironmentVarialbesのrPathには、インストールしたフォルダの下にある、32bit版のR.dllが置かれたフォルダを指定する

rHomeには、Rをインストールしたフォルダを指定する

上記のコードは、Rのver.3.2.3での記述

[C#]匿名クラスのIEnumerableオブジェクトから、あるフィールドが最大となる要素を取得する

var max = Enumerable.Range(0, 100)
    .Select(i => new { name = "name_" + i, value = 50 - Math.Abs(i - 50) })
    .Aggregate((now_max, item) => (now_max.value >= item.value) ? now_max : item);
Console.WriteLine(max.name);

3行目、各要素(item)を、ある時点で最大とされる要素(now_max)と、valueフィールドで比較する。
IEnumerable<T>のMaxでは、上記のようなことが出来ないのが残念。

[C#]デザインパターン再考 Strategy Pattern(デリゲートタイプ)

※ ここに書かれていることは、間違っているかもしれないので、ご注意ください

■ なぜ、このパターンを使うのか?

 switchやif - elseで処理の切り替え(アルゴリズムの変更)を記述すると・・・

1.どの処理を適用するかを示す引数や変数が必要になる

  • ほとんどの場合、名前の付け方がいい加減で、コードが読み難い
  • 数が多くなると、さらに読みづらくなる

2.毎回、上記の引数や変数の判定が必要になる

  • 同じ引数や変数なのに、同じ結果を得るための判定処理を行うのはムダだ
  • ひとつひとつの処理時間は短いが、回数が増えると無視できない時がある

 私の少ない経験から思うに、繰り返し処理されるコードで毎回判定を行っていると、処理時間がもったいないことがある。特に、画像表示を行う場合は。

■ 何をしているのか?

 簡単に言うと、処理を選択する箇所と、それを実行する箇所を分けているのだ。

 switchやif - elseで処理を切り替えるコードでは、条件式で処理の選択を記述し、それに続いて実行する箇所を記述している。したがって、処理を適用したい場合は毎回、判定を行うことになる。何度も同じ判定を繰り返すのはもったいない。同じ処理を行う状態なら、判定は状態の遷移時に行えばいいだけだ。

 そう、これは状態遷移で使うパターンだ。(といって、いいと思うのだけれど)

 画像表示のツールを作った際には、表示の更新/停止の切り替えに使った。カメラから画像データを取得し、処理して、表示するという流れを繰り返すのだが、この流れを停止したい場合もある。その2つの状態の切り替えに使用した。

■ デメリットは?

 引数で状態を指定されて、それに応じて行う処理が簡単であって、処理時間が少々かかっても問題ない場合は、このパターンを適用しなくていいと思う。適用した方が煩雑になるからだ。

 処理時間の短さが重要視される場合や、処理のコードが長くなる場合、このパターンを適用してもいいと思う。

 状態の指定をフィールドで行う(フラグってヤツ)場合も、このパターンを使っていいと思う。フィールドを記述するのと同じ手間で書けるからだ。

■ コード例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace StrategyPatternByDelegate
{
    class Program
    {
        static void Main(string[] args)
        {
            var program = new Program();
            program.StartImageView();
            program.StartUserInput();
        }

        private static Action ShowImage = () => Console.WriteLine("Show a new image.");

        private static Action StopImage = () => Console.WriteLine("Now stopping ...");

        private Action NowAction = StopImage;

        private Thread thread = null;

        private void StartImageView()
        {
            this.thread = new Thread(() =>
            {
                foreach (var i in Enumerable.Range(0, 100))
                {
                    this.NowAction();
                    Thread.Sleep(100);
                }
            });
            thread.Start();
        }

        private void SelectAction(ConsoleKeyInfo keyInfo)
        {
            if (keyInfo.Key == ConsoleKey.Enter)
                this.NowAction = (this.NowAction == ShowImage) ? StopImage : ShowImage;
        }

        private void StartUserInput()
        {
            if (this.thread == null) return;

            while (thread.IsAlive)
                this.SelectAction(Console.ReadKey());
        }
    }
}

以上

[C#]デザインパターン再考 Chain of Responsibility Pattern

※ ここに書かれていることは、間違っているかもしれないので、ご注意ください

■ なぜ、このパターンを使うのか?

 switchやif - elseで条件分岐を記述すると・・・

1.読みづらい

  • 処理のコードが長すぎると、読みづらい
  • 条件の数が多すぎると、読みづらい
  • どこまでが1つの処理なのか、分かりづらい

2.他の人(あるいは、未来の自分)が条件を追加、削除しづらい

  • 変更による影響範囲を特定しづらい
  • 試しでコメントアウトするのに、/* ... */や、#if false ... #endif を使わないといけない

3.複雑な判定がやりづらい

  • true / false を判断するのに複雑な処理が必要な場合、どこに書けばいいのか、分からない
  • 処理の途中で行き詰ったときに断念することが難しい

 やはり、読みづらいと変更しづらいというのが、主な理由。

 そもそもオブジェクト指向は、関係する処理をクラスにまとめることで、変更による影響範囲を限定的にするもののはず。複数の条件がかかわるコードを同じクラス内に書くこと自体がおかしい話なのだと思う。

■ 何をしているのか?

 このパターンで行っているのは、

  • 条件ごとの判定と適用する処理を、ひとまとまりのコードとして扱う
  • 呼び出し元は各条件を順次呼び出す
  • 処理ができる条件があれば、呼び出し元は以降の条件は無視する

 つまり、

  • 各条件は、どこから呼び出されているか知らない。ただ、必要な情報をもらって、処理ができるなら処理をするし、できないならできない旨を伝えるだけ
  • 呼び出し元は、各条件が行っている判定や処理の詳細を知らない。ただ、処理ができる条件があれば適切な出力を得られると知っているのみ

 たとえるなら、金属加工の外注だろうか。

  • A社は、自社製品に使用する金属ケースを、非常に薄く、ひずみを小さく、更に表面をピカピカにしたいと思っていた
  • いくつかの金属加工会社に問い合わせたが、要求する品質レベルが高すぎてどこも対応できない
  • 6ヵ月後、16社目でようやく引き受けてくれるところが見つかった。が、実際やってみると非常に難しい加工だったので、ここもすぐに断念した
  • さらに3ヵ月後、23社目で再び引き受けてくれた。ここは加工に成功。十分な品質レベルを確保できることが分かり、採用することになった。

 金属加工にもさまざまはやり方があるだろう。新しい加工方法も開発されていく。それを導入した加工会社が見つかれば、A社の担当が持っている外注先のリストに書き加えておけばいい。次回、同じようなことが起これば、そこにも相談できる。

 A社の担当も金属加工の素人ではないだろうが、すべての加工に精通しているわけでもない。詳細なところの判断は、各加工会社の専門家に聞くのがいい。また、加工の詳細を知らなくても、出来上がった製品が要求している品質を満たしていればいい。

 加工会社からすると、A社の製品の全貌を知る必要はない。秘情報を教えてくれるわけはないし、知ったところで加工にはほぼ関係ない。むしろ、必要な要求をきちんと整理して、そこだけを伝えてもらった方が無駄な作業時間がかからないのでいい。

■ デメリットは?

 条件の数が少なく、判定も単純で、処理の複雑ではない場合、このパターンを使うのはよくない。逆に見通しが悪くなるだけで、読みづらくなる。読みづらいと、変更しづらいコードになる。条件分岐のすべてにこのパターンを適用してはいけない。

■ コード例

 デメリットに書いたとおり、簡単な処理ではこのパターンを使うべきではない。だが、コード例はそれほど複雑にはできない。で、仕方がないので、簡単な処理での例を書く。

    class Program
    {
        static void Main(string[] args)
        {
            var question_result = DialogResult.Ignore;
            var analyzers = new IAnalyzer<string, DialogResult>[]{
                new ConsoleOutAnalyzer(),
                new MessageBoxAnalyzer(),
                new QuestionAnalyzer((result) => question_result = result)
            };

            IEnumerable<string> code = new string[] {
                "print How are you?",
                "message What do you think I say?",
                "print I'm fine, thank you!",
                "ask Are you OK?"
            };

            while (true)
            {
                question_result = DialogResult.Ignore;
                var analyzer = analyzers.FirstOrDefault(d => d.CanAnalyze(code));
                if (analyzer == null) break;
                analyzer.Analyze(ref code);
                if (question_result == DialogResult.Yes)
                    Console.WriteLine("Wow");
            }

            Console.ReadKey();
        }
    }

    interface IAnalyzer<TArg, TResult>
    {
        bool CanAnalyze(IEnumerable<TArg> code);
        void Analyze(ref IEnumerable<TArg> code);
    }

    abstract class ACodeAnalyzer : IAnalyzer<string, DialogResult>
    {
        protected string command = null;

        protected Action<DialogResult> completed = null;

        public virtual bool CanAnalyze(IEnumerable<string> code)
        {
            return command != null
                && code != null
                && code.Count() > 0
                && code.ElementAt(0).StartsWith(command);
        }

        public abstract void Analyze(ref IEnumerable<string> code);
    }

    class ConsoleOutAnalyzer : ACodeAnalyzer
    {
        public ConsoleOutAnalyzer() { command = "print"; }

        public ConsoleOutAnalyzer(Action<DialogResult> completed)
            : this()
        {
            this.completed = completed;
        }

        public override void Analyze(ref IEnumerable<string> code)
        {
            var line = code.ElementAt(0).Replace(command, "").Trim();
            code = code.Skip(1);

            Console.WriteLine(line);
        }
    }

    class MessageBoxAnalyzer : ACodeAnalyzer
    {
        public MessageBoxAnalyzer() { command = "message"; }

        public override void Analyze(ref IEnumerable<string> code)
        {
            var line = code.ElementAt(0).Replace(command, "").Trim();
            code = code.Skip(1);

            Console.WriteLine(line);
            MessageBox.Show(line);
        }
    }

    class QuestionAnalyzer : ACodeAnalyzer
    {
        public QuestionAnalyzer() { command = "ask"; }

        public QuestionAnalyzer(Action<DialogResult> completed)
            : this()
        {
            this.completed = completed;
        }

        public override void Analyze(ref IEnumerable<string> code)
        {
            var line = code.ElementAt(0).Replace(command, "").Trim();
            code = code.Skip(1);

            Console.WriteLine(line);
            var result = MessageBox.Show(line, "Question", MessageBoxButtons.YesNo);
            Console.WriteLine(result == DialogResult.Yes ? "Your answer is YES." : "Your answer is NO.");

            if (this.completed != null)
                this.completed(result);
        }
    }

以上

[C#]条件分岐を使わない方法3

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 条件分岐を使わない方法3
{
    class Program
    {
        static void Main(string[] args)
        {
            var manager = new Manager();

            manager.SelectKey(manager.KeyA);
            manager.DoSomething();

            manager.SelectKey(manager.KeyB);
            manager.DoSomething();

            manager.SelectKey(manager.KeyC);
            manager.DoSomething();

            Console.ReadKey();
        }
    }

    interface IKey{
        string Name { get; }
    }

    class Key : IKey
    {
        public string Name { get; private set; }

        internal Key(string name)
        {
            this.Name = name;
        }
    }

    class Manager
    {
        private Dictionary<IKey, Action<IKey>> dic = new Dictionary<IKey, Action<IKey>>();

        private IKey NowKey = null;

        internal IKey KeyA { get; private set; }

        internal IKey KeyB { get; private set; }

        internal IKey KeyC { get; private set; }

        internal Manager()
        {
            this.KeyA = new Key("A");
            this.KeyB = new Key("B");
            this.KeyC = new Key("C");

            this.dic.Add(this.KeyA, (key) => { Console.WriteLine("Key{0} is selected!  ", key.Name); });
            this.dic.Add(this.KeyB, (key) => { Console.WriteLine("Key{0} is selected!! ", key.Name); });
            this.dic.Add(this.KeyC, (key) => { Console.WriteLine("Key{0} is selected!!!", key.Name); });

            this.NowKey = this.KeyA;
        }

        internal void SelectKey(IKey key)
        {
            this.NowKey = key;
        }

        internal void DoSomething()
        {
            this.dic[this.NowKey](this.NowKey);
        }
    }
}

[C#]条件分岐を使わない方法2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 条件分岐を使わない方法2
{
    class Program
    {
        static void Main(string[] args)
        {
            var manager = new Manager();

            manager.SelectActionA();
            manager.DoSomething();

            manager.SelectActionB();
            manager.DoSomething();

            Console.ReadKey();
        }
    }

    class Manager
    {
        private Action NowAction = () => { };

        private Action ActionA = () =>
        {
            Console.WriteLine("ActionA");
        };

        private Action ActionB = () =>
        {
            Console.WriteLine("ActionB");
        };

        internal void SelectActionA()
        {
            this.NowAction = this.ActionA;
        }

        internal void SelectActionB()
        {
            this.NowAction = this.ActionB;
        }

        internal void DoSomething()
        {
            this.NowAction();
        }
    }
}

[C#]条件分岐を使わない方法1

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 条件分岐を使わない方法1
{
    class Program
    {
        static void Main(string[] args)
        {
            var manager = new Manager();

            manager.SelectControlA();
            manager.DoSomething();

            manager.SelectControlB();
            manager.DoSomething();

            Console.ReadKey();
        }
    }

    interface IControl
    {
        void DoSomething();
    }

    class ControlA : IControl
    {
        public void DoSomething()
        {
            Console.WriteLine("Something A");
        }
    }

    class ControlB : IControl
    {
        public void DoSomething()
        {
            Console.WriteLine("Something B");
        }
    }

    class Manager
    {
        private IControl NowControl;

        private IControl ctrlA = new ControlA();

        private IControl ctrlB = new ControlB();

        internal Manager()
        {
            this.NowControl = this.ctrlA;
        }

        internal void SelectControlA()
        {
            this.NowControl = this.ctrlA;
        }

        internal void SelectControlB()
        {
            this.NowControl = this.ctrlB;
        }

        internal void DoSomething()
        {
            this.NowControl.DoSomething();
        }
    }
}

[C#]ソースコードをコンパイルして、実行可能ファイルを作成する

プログラムから、プログラムを作る、ってことをしてみる。

using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestCompile
{
    class Program
    {
        static void Main(string[] args)
        {
            // コードの読み込み
            var code = File.ReadAllText(@"C:\work\code.cs");

            // コンパイル時のオプション設定
            CompilerParameters param = new CompilerParameters();
            param.OutputAssembly = "test.exe";
            param.GenerateExecutable = true;

            // コンパイルする
            CSharpCodeProvider provider = new CSharpCodeProvider();
            var results = provider.CompileAssemblyFromSource(param, code);

            foreach (var errmsg in results.Errors.OfType<CompilerError>())
            {
                Console.WriteLine(errmsg.ErrorText);
            }
            Console.ReadKey();
        }
    }
}

code.csの中身はこちら。

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace TestCode
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            Console.WriteLine("This is a test code.");
            Console.ReadKey();
        }
    }
}

[C#]コマンドラインを処理するクラス

自分のためのメモ。

コマンドラインを処理するクラス

-----

    /// <summary>
    /// コマンドライン引数を処理するクラス
    /// </summary>
    class CommandLine
    {
        /// <summary>
        /// プログラムの説明文
        /// </summary>
        public string[] Description { get; set; }

        /// <summary>
        /// デフォルト引数(オプションフラグなし)
        /// </summary>
        public Argument Default { get; set; }

        /// <summary>
        /// オプション引数(オプションフラグあり)
        /// </summary>
        public Argument[] Options { get; set; }

        /// <summary>
        /// 説明文をコンソールに表示します。
        /// </summary>
        public void ShowDescription()
        {
            Console.Write("[USAGE]{0}", Path.GetFileName(Utility.GetExecutablePath()));
            Console.Write(" <{0}>", this.Default.ArgName);
            foreach (var option in this.Options)
                Console.Write(" [{0} <{1}>]", option.Option, option.ArgName);
            Console.WriteLine();
            
            Console.Write("説明");
            foreach(var s in this.Description)
                Console.WriteLine("\t{0}", s);
            Console.WriteLine();

            Console.WriteLine("引数");
            Console.WriteLine(Argument.GetColumnName());
            Console.WriteLine(this.Default.ToString());
            foreach (var option in this.Options)
                Console.WriteLine(option.ToString());
        }

        /// <summary>
        /// 実引数を解析します。
        /// </summary>
        /// <param name="args">実引数</param>
        public void AnalyzeArgs(string[] args)
        {
            var input = this.Default.Setup;

            foreach (var arg in args)
            {
                var option = this.Options.Where(a => a.Option == arg);
                if (option.Any())
                {
                    var opt = option.First();
                    if (opt.HasArg)
                    {
                        input = opt.Setup;
                    }
                    else
                    {
                        opt.Setup(arg);
                        input = this.Default.Setup;
                    }
                }
                else
                {
                    input(arg);
                    input = this.Default.Setup;
                }
            }
        }
    }

    /// <summary>
    /// 引数クラス
    /// </summary>
    class Argument
    {
        /// <summary>
        /// オプションフラグを示す文字列
        /// </summary>
        /// <remarks>オプションがString.Emptyの場合、この引数がデフォルト引数であることを示しています。</remarks>
        public string Option { get; private set; }

        /// <summary>
        /// オプションで使う引数を説明する文字列
        /// </summary>
        /// <remarks>フラグの次の引数を使う場合、このプロパティを設定します。使わない場合は空白文字を設定します。
        /// このプロパティはプログラムの使い方の説明に使われます。</remarks>
        public string ArgName { get; private set; }

        /// <summary>
        /// このオプションのために次の引数が必要な場合はTrue、それ以外の場合はFalse
        /// </summary>
        /// <remarks>Trueの場合、次の引数に対してSetupデリゲートが実行されます。
        /// Falseの場合、Setupデリゲートは引数を取らずに実行されます。</remarks>
        public bool HasArg { get; private set; }

        /// <summary>
        /// このオプションに対する処理
        /// </summary>
        public Action<string> Setup { get; private set; }

        /// <summary>
        /// このオプションに関する説明
        /// </summary>
        public string Description { get; private set; }

        /// <summary>
        /// インスタンスを生成し、初期化します。
        /// </summary>
        private Argument() { }

        /// <summary>
        /// インスタンスを生成し、引数名、説明文、セットアップメソッドで初期化します。
        /// </summary>
        /// <param name="argName">引数名</param>
        /// <param name="description">説明文</param>
        /// <param name="setup">セットアップメソッド</param>
        /// <remarks>このコンストラクタで生成したインスタンスは、デフォルト引数を表します。</remarks>
        public Argument(string argName, string description, Action<string> setup)
            : this(String.Empty, argName, description, setup) { }

        /// <summary>
        /// インスタンスを生成し、オプションフラグ、引数名、説明文、セットアップメソッドで初期化します。
        /// </summary>
        /// <param name="option">オプションフラグ</param>
        /// <param name="argName">引数名</param>
        /// <param name="description">説明文</param>
        /// <param name="setup">セットアップメソッド</param>
        /// <remarks>オプションフラグにnullまたはString.Emptyを指定して生成したインスタンスは、デフォルト引数を表します。
        /// それ以外は、オプション引数を表します。</remarks>
        public Argument(string option, string argName, string description, Action<string> setup)
        {
            this.Option = option ?? String.Empty;
            this.ArgName = argName;
            this.HasArg = !String.IsNullOrWhiteSpace(argName);
            this.Description = description;
            this.Setup = setup;
        }

        public static string GetColumnName()
        {
            return String.Format("{0,-12} {1,-24} : {2}", "[Flag]", "[Name]", "[Description]");
        }

        /// <summary>
        /// 引数の説明を取得します。
        /// </summary>
        /// <returns>引数の説明</returns>
        public override string ToString()
        {
            return String.Format("{0,-12} {1,-24} : {2}", this.Option, this.ArgName, this.Description);
        }
    }

-----

利用側は省略。

[C#]メールを送信する

メールを送信するサンプルのメモ

using System.Net.Mail;
using System.Net;

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                this.SendMail(new MailAddress("xxxx@xxxx.com", "display name"),
                    new[] { new MailAddress("xxxx@xxxx.com", "display name") },
                    "test", "test mail");
            }
            catch (Exception ex)
            {
                while (ex != null)
                {
                    MessageBox.Show(ex.Message);
                    MessageBox.Show(ex.StackTrace);
                    ex = ex.InnerException;
                }
            }
        }

        /// <summary>
        /// メールを送信します。
        /// </summary>
        /// <param name="from">送信者</param>
        /// <param name="to">宛先</param>
        /// <param name="subject">件名</param>
        /// <param name="body">本文</param>
        private void SendMail(MailAddress from, MailAddress[] to, string subject, string body)
        {
            using (var message = new MailMessage())
            {
                using (var client = new SmtpClient())
                {
                    // メールの作成
                    message.From = from;
                    foreach (var toAddr in to)
                        message.To.Add(toAddr);
                    message.Subject = subject;
                    message.Body = body;

                    // SMTPの設定
                    client.Host = "smtp.xxxx.com";
                    client.Credentials = new NetworkCredential("user-id", "password");
                    client.EnableSsl = false;

                    // メール送信
                    client.Send(message);
                }
            }
        }

以上です。

2019年10月
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    
無料ブログはココログ