« 2014年6月 | トップページ | 2015年1月 »

カリー化が分かるようで、分からない

関数型の資料を読んでいると、カリー化という話が出てくる。
いろいろ調べてもよく分からなかったが、ここの最後の方を読んで理解できた気がする。
部分適用との違いを明確にすることで、カリー化が分かったという感じ。

C#で書くと、こんな感じかな。

            Func<int, int, int, bool> f = (x, y, z) => x + y == z;
            Func<int, Func<int, int, bool>> g = (x) => (y, z) => x + y == z;

            Console.WriteLine("f(1,2,3) is {0}", f(1, 2, 3));
            Console.WriteLine("g(1)(2,3) is {0}", g(1)(2, 3));

ここで、gはfのカリー化された関数ってことになるのだと思う。自信はないけど。
部分適用っていうのは、たぶんg(1)のことなんだろうな。一部の引数に具体的な数値を適用して、残りの引数による関数を作るってことなんだろう。

で、ここからが分からない。なぜカリー化をするのか?

あるページによると、「カリー化のメリットは、部分適用である。ある関数を雛形として、引数をカスタマイズした関数を作り出せる」ということらしい。「高階関数と組み合わせると、その表記の簡潔さは一目瞭然となる」のだそうだ。うむぅ・・・

            var data1 = new[] { 1, 2, 3 };
            var data2 = new[] { 4, 5, 6 };

            Func<int, int, int> plus = (x, y) => x + y;
            var result1 = Enumerable.Range(0, 3).Select(i => plus(data1[i], data2[i]));
            Console.WriteLine("result1 = {0}", String.Join(", ", result1.Select(n=>n.ToString())));

            Func<int, Func<int, int>> curried_plus = (x) => (y) => x + y;
            var result2 = data1.Select(x=>curried_plus(x)).Select((plusx, i) => plusx(data2[i]));
            Console.WriteLine("result2 = {0}", String.Join(", ", result2.Select(n => n.ToString())));

うーん・・・たぶん、C#でやるのが悪いんだろうな。
後の方が分かりにくいコードになっているから。

カリー化することで、実行上(言い方が正しい?)のメリットはないのだろうか?

[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();
        }
    }
}

« 2014年6月 | トップページ | 2015年1月 »

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    
無料ブログはココログ