Przeglądaj źródła

重構 最佳化

0x0001F36D 5 lat temu
rodzic
commit
702ce05fd0

+ 98 - 3
src/Yuuna.Contracts/Optimization/DefaultStrategy.cs

@@ -3,12 +3,107 @@
 
 namespace Yuuna.Contracts.Optimization
 {
+    using System;
     using System.Collections.Generic;
+    using System.Collections.Immutable;
+    using System.Diagnostics.CodeAnalysis;
+    using System.Runtime.CompilerServices;
 
-    public sealed class DefaultStrategy : StrategyBase
+    using Yuuna.Contracts.Patterns;
+    using Yuuna.Contracts.Plugins;
+    using Yuuna.Contracts.Semantics;
+
+    public class DefaultStrategy : IStrategy
     {
-        public override string Name => nameof(ScoreComparer.Default);
+        public DefaultStrategy()
+        {
+
+        }
+
+        public Match? FindBest(IEnumerable<PluginBase> plugins, IImmutableList<string> feed)
+        {
+            var list = ImmutableArray.CreateBuilder<Match>();
+            foreach (var plugin in plugins)
+            {
+                foreach (var p in plugin.Patterns.ToImmutable())
+                {
+                    var score = this.CalculatePatternMatch(p, feed);
+                    if (score.HasValue)
+                        list.Add(score.Value);
+                }
+            }
+
+            var e = this.SelectBestMatch(list.ToImmutable());
+            return e;
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="matches"></param>
+        /// <returns></returns>
+        protected virtual Match? SelectBestMatch(IImmutableList<Match> matches)
+        {
+            var sta = new SortedList<Match, int>();
+            foreach (var j in matches)
+            {
+                if (!sta.ContainsKey(j))
+                    sta.Add(j, 1);
+                else
+                    sta[j]++;
+            }
+
+            //var tuple = new List<Tuple<Match, double>>();
+            //foreach (var s in sta)
+            //{
+            //    var x = (s.Value) / (double)-(matches.Count * s.Key.SequentialMatches.Count);
+            //    var y = Math.Tanh(Math.Pow(Math.E, x));
+            //    tuple.Add(Tuple.Create(s.Key, y));
+            //}
+
+            //var bestSelect = tuple.OrderByDescending(x => x.Item2).FirstOrDefault();
+
+            //return bestSelect?.Item1;
+
+            var hold = double.MinValue;
+            var bestSelect = default(Match?);
+            foreach (var s in sta)
+            {
+                var y = Math.Tanh(Math.Pow(Math.E, (s.Value) / (double)-(matches.Count * s.Key.SequentialMatches.Count)));
+                if (y >= hold)
+                {
+                    hold = y;
+                    bestSelect = s.Key;
+                }
+            }
+            return bestSelect;
+        }
+
+        /// <summary>
+        /// 評估 <paramref name="pattern"/> 與 <paramref name="feed"/> 的符合程度並給予一個分數,並返回一個 <see cref="Match"/> 物件。
+        /// 若回傳 <see langword="null"/> 則此規則將不列入計算。
+        /// </summary>
+        /// <param name="pattern"></param>
+        /// <param name="feed"></param>
+        /// <returns></returns>
+        protected virtual Match? CalculatePatternMatch(IPattern pattern, IImmutableList<string> feed)
+        {
+            var matches = ImmutableArray.CreateBuilder<ISynonym>();
 
-        protected override IComparer<IScore> Comparer => ScoreComparer.Default;
+            using (var iterator = pattern.ToImmutable().GetEnumerator())
+            {
+                iterator.MoveNext();
+                foreach (var word in feed)
+                {
+                    if (iterator.Current.TryGetSynonym(word, out var s))
+                    {
+                        matches.Add(s);
+                        if (!iterator.MoveNext())
+                            break;
+                    }
+                }
+            }
+            return new Match(pattern, matches.ToImmutable());
+        }
     }
 }

+ 0 - 13
src/Yuuna.Contracts/Optimization/IPair.cs

@@ -1,13 +0,0 @@
-// Author: Orlys
-// Github: https://github.com/Orlys
-
-namespace Yuuna.Contracts.Optimization
-{
-    using Yuuna.Contracts.Semantics;
-
-    public interface IPair
-    {
-        string Key { get; }
-        ISynonym Synonym { get; }
-    }
-}

+ 0 - 20
src/Yuuna.Contracts/Optimization/IScore.cs

@@ -1,20 +0,0 @@
-// Author: Orlys
-// Github: https://github.com/Orlys
-
-namespace Yuuna.Contracts.Optimization
-{
-    using System.Collections.Immutable;
-
-    using Yuuna.Contracts.Patterns;
-    using Yuuna.Contracts.Plugins;
-    using Yuuna.Contracts.Semantics;
-
-    public interface IScore
-    {
-        IPattern Pattern { get; } 
-        bool IsCompacted { get; }
-        IImmutableList<IGroup> Missing { get; }
-        PluginBase Owner { get; }
-        IImmutableList<IPair> SequentialMatches { get; }
-    }
-}

+ 14 - 0
src/Yuuna.Contracts/Optimization/IStrategy.cs

@@ -0,0 +1,14 @@
+// Author: Orlys
+// Github: https://github.com/Orlys
+
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using Yuuna.Contracts.Plugins;
+
+namespace Yuuna.Contracts.Optimization
+{
+    public interface IStrategy
+    {
+        Match? FindBest(IEnumerable<PluginBase> plugins, IImmutableList<string> feed);
+    }
+}

+ 57 - 0
src/Yuuna.Contracts/Optimization/Match.cs

@@ -0,0 +1,57 @@
+// Author: Orlys
+// Github: https://github.com/Orlys
+
+namespace Yuuna.Contracts.Optimization
+{
+    using System;
+    using System.Collections.Immutable;
+    using System.Linq;
+
+    using Yuuna.Contracts.Patterns;
+    using Yuuna.Contracts.Semantics;
+
+    public struct Match : IEquatable<Match>, IComparable<Match>
+    {
+        /// <summary>
+        /// 比較的文法。
+        /// </summary>
+        public IPattern Pattern { get; }
+
+        /// <summary>
+        /// 表示缺少的群組物件清單。
+        /// </summary>
+        public IImmutableList<IGroup> Missing { get; }
+
+        /// <summary> 
+        /// </summary>
+        public IImmutableList<ISynonym> SequentialMatches { get; }
+
+        /// <summary>
+        /// 是否完全符合。
+        /// </summary>
+        public bool IsExactMatch { get; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="pattern">比對的目標</param>
+        /// <param name="matches">從頭逐一比對後符合的項目</param>
+        public Match(IPattern pattern, IImmutableList<ISynonym> matches)
+        {
+            this.Pattern = pattern;
+            this.SequentialMatches = matches;
+            this.Missing = pattern.ToImmutable().Except(matches.Select(x => x.Owner)).ToImmutableArray();
+            this.IsExactMatch = pattern.Count.Equals(matches.Count);
+        }
+
+        public bool Equals(Match other)
+        {
+            return this.Pattern.Equals(other.Pattern);
+        }
+
+        public int CompareTo(Match other)
+        {
+            return this.Pattern.Count - other.Pattern.Count;
+        }
+    } 
+}

+ 0 - 20
src/Yuuna.Contracts/Optimization/Pair.cs

@@ -1,20 +0,0 @@
-// Author: Orlys
-// Github: https://github.com/Orlys
-
-namespace Yuuna.Contracts.Optimization
-{
-    using Yuuna.Contracts.Semantics;
-
-    internal struct Pair : IPair
-    {
-        public string Key { get; }
-
-        public ISynonym Synonym { get; }
-
-        internal Pair(string key, ISynonym synonym)
-        {
-            this.Key = key;
-            this.Synonym = synonym;
-        }
-    }
-}

+ 0 - 38
src/Yuuna.Contracts/Optimization/Score.cs

@@ -1,38 +0,0 @@
-// Author: Orlys
-// Github: https://github.com/Orlys
-
-namespace Yuuna.Contracts.Optimization
-{
-    using System.Collections.Immutable;
-    using System.Linq;
-
-    using Yuuna.Contracts.Patterns;
-    using Yuuna.Contracts.Plugins;
-    using Yuuna.Contracts.Semantics;
-
-    public struct Score : IScore
-    {
-        /// <summary>
-        /// 比較的文法。
-        /// </summary>
-        public IPattern Pattern { get; } 
-
-        public bool IsCompacted { get; }
-
-        public IImmutableList<IGroup> Missing { get; }
-
-        public PluginBase Owner { get; }
-
-        public IImmutableList<IPair> SequentialMatches { get; }
-
-        public Score(IPattern pattern, IImmutableList<IPair> matches)
-        {
-            this.Pattern = pattern;
-            this.Owner = pattern.Owner;
-            this.SequentialMatches = matches;
-
-            this.IsCompacted = pattern.Count == matches.Count;
-            this.Missing = pattern.ToImmutable().Except(matches.Select(x => x.Synonym.Owner)).ToImmutableArray();
-        }
-    }
-}

+ 0 - 38
src/Yuuna.Contracts/Optimization/ScoreComparer.cs

@@ -1,38 +0,0 @@
-// Author: Orlys
-// Github: https://github.com/Orlys
-
-namespace Yuuna.Contracts.Optimization
-{
-    using System.Collections.Generic;
-
-    public sealed class ScoreComparer : IComparer<IScore>
-    {
-        public readonly static IComparer<IScore> Default = new ScoreComparer();
-
-        private ScoreComparer()
-        {
-        }
-
-        public int Compare(IScore x, IScore y)
-        {
-            var f1 = x.Pattern.SequentialKeys.Count == x.SequentialMatches.Count;
-            var f2 = y.Pattern.SequentialKeys.Count == y.SequentialMatches.Count;
-            if (f1 & f2)
-                return x.Pattern.SequentialKeys.Count - y.Pattern.SequentialKeys.Count;
-            else if (f1)
-                return 1;
-            else if (f2)
-                return -1;
-            else
-            {
-                var v1 = x.Pattern.SequentialKeys.Count - x.SequentialMatches.Count;
-                var v2 = y.Pattern.SequentialKeys.Count - y.SequentialMatches.Count;
-                if (v1 > v2)
-                    return -v2;
-                else if (v1 < v2)
-                    return v1;
-                return x.Pattern.SequentialKeys.Count - y.Pattern.SequentialKeys.Count;
-            }
-        }
-    }
-}

+ 0 - 188
src/Yuuna.Contracts/Optimization/StrategyBase.cs

@@ -1,188 +0,0 @@
-// Author: Orlys
-// Github: https://github.com/Orlys
-
-namespace Yuuna.Contracts.Optimization
-{
-    using System;
-    using System.Collections.Generic;
-    using System.Collections.Immutable;
-    using System.Diagnostics.CodeAnalysis;
-    using System.Linq;
-    using System.Runtime.CompilerServices;
-
-    using Yuuna.Contracts.Patterns;
-    using Yuuna.Contracts.Plugins;
-    using Yuuna.Contracts.Semantics;
-
-    public abstract class Plan
-    {
-        public Plan()
-        {
-
-        }
-
-        public void FindBest(IEnumerable<PluginBase> plugins, IImmutableList<string> feed)
-        {
-            var list = ImmutableArray.CreateBuilder<Match>();
-            foreach (var plugin in plugins)
-            {
-                foreach (var p in plugin.Patterns.ToImmutable())
-                {
-                    var score = this.CalculatePatternMatch(p, feed);
-                    if (score.HasValue)
-                        list.Add(score.Value);
-                }
-            }
-
-            var e = this.E(list.ToImmutable());
-        } 
-
-        protected virtual void E(IImmutableList<Match> matches)
-        {
-            var sta = new SortedList<Match, int>();
-            foreach (var j in matches)
-            {
-                if (!sta.ContainsKey(j))
-                    sta.Add(j, 1);
-                else
-                    sta[j]++;
-            }
-
-            var tuple = new List<Tuple<Match, double>>();
-            foreach (var s in sta)
-            {
-                var x = (s.Value) / (double)-(matches.Count * s.Key.SequentialMatches.Count);
-                var y = Math.Tanh(Math.Pow(Math.E, x));
-                tuple.Add(Tuple.Create(s.Key, y));
-            }
-
-            foreach (var s in tuple.OrderByDescending(x => x.Item2))
-            {
-                Console.WriteLine(s);
-            }
-        }
-
-        /// <summary>
-        /// 評估 <paramref name="pattern"/> 與 <paramref name="feed"/> 的符合程度並給予一個分數,並返回一個 <see cref="Match"/> 物件。
-        /// 若回傳 <see langword="null"/> 則此規則將不列入計算。
-        /// </summary>
-        /// <param name="pattern"></param>
-        /// <param name="feed"></param>
-        /// <returns></returns>
-        protected virtual Match? CalculatePatternMatch(IPattern pattern, IImmutableList<string> feed)
-        {
-            var matches = ImmutableArray.CreateBuilder<ISynonym>();
-
-            using (var iterator = pattern.ToImmutable().GetEnumerator())
-            {
-                iterator.MoveNext();
-                foreach (var word in feed)
-                {
-                    if (iterator.Current.TryGetSynonym(word, out var s))
-                    {
-                        matches.Add(s);
-                        if (!iterator.MoveNext())
-                            break;
-                    }
-                }
-            } 
-            return new Match(pattern, matches.ToImmutable());
-        }
-    } 
-
-    public struct Match : IEquatable<Match>
-    {
-        /// <summary>
-        /// 比較的文法。
-        /// </summary>
-        public IPattern Pattern { get; }
-
-        /// <summary>
-        /// 表示缺少的群組物件清單。
-        /// </summary>
-        public IImmutableList<IGroup> Missing { get; }
-
-        /// <summary> 
-        /// </summary>
-        public IImmutableList<ISynonym> SequentialMatches { get; }
-
-        /// <summary>
-        /// 
-        /// </summary>
-        /// <param name="pattern">比對的目標</param>
-        /// <param name="matches">從頭逐一比對後符合的項目</param>
-        public Match(IPattern pattern, IImmutableList<ISynonym> matches)
-        {
-            this.Pattern = pattern; 
-            this.SequentialMatches = matches; 
-            this.Missing = pattern.ToImmutable().Except(matches.Select(x => x.Owner)).ToImmutableArray();
-        }
-
-        public bool Equals(Match other)
-        {
-            return this.Pattern.Equals(other.Pattern);
-        }
-    }
-
-
-    public abstract class StrategyBase
-    {
-        public abstract string Name { get; }
-
-        protected abstract IComparer<IScore> Comparer { get; }
-
-        protected StrategyBase()
-        {
-        }
-
-        /// <summary>
-        /// 評估 <paramref name="plugin"/> 中最佳的
-        /// </summary>
-        /// <param name="plugin"></param>
-        /// <param name="words"></param>
-        /// <param name="set"></param>
-        /// <returns></returns>
-        internal IImmutableList<IScore> Assess(PluginBase plugin, IImmutableList<string> words, IPatternSet set)
-        {
-            var scores = new List<IScore>();
-            var immutable = set.ToImmutable();
-            foreach (var pattern in immutable)
-            {
-                var score = this.Evaluate(plugin, words, pattern);
-                if (score.SequentialMatches.Count > 0) // 過濾掉符合為 0 的文法
-                    scores.Add(score);
-            }
-            scores.Sort(this.Comparer);
-
-
-
-            return scores.ToImmutableArray();
-        }
-
-        /// <summary>
-        /// 評估 <paramref name="pattern"/> 與 <paramref name="words"/> 的符合程度,並返回評估後的分數物件
-        /// </summary>
-        /// <param name="words"></param>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private IScore Evaluate(IImmutableList<string> words, IPattern pattern)
-        { 
-            var matches = ImmutableArray.CreateBuilder<IPair>();
-
-            using (var iterator = pattern.ToImmutable().GetEnumerator())
-            {
-                iterator.MoveNext();
-                foreach (var word in words)
-                {
-                    if (iterator.Current.TryGetSynonym(word, out var s))
-                    {
-                        matches.Add(new Pair(s.Owner.Key, s));
-                        if (!iterator.MoveNext())
-                            break;
-                    }
-                }
-            }
-
-            return new Score(matches.ToImmutable(), pattern);
-        }
-    }
-}

+ 2 - 2
src/Yuuna.Contracts/Patterns/Delegates.cs

@@ -6,7 +6,7 @@ namespace Yuuna.Contracts.Patterns
     using Yuuna.Contracts.Interaction;
     using Yuuna.Contracts.Optimization;
 
-    public delegate Response Incomplete(IScore score);
+    public delegate Response Incomplete(Match m);
 
-    public delegate Response Invoke(IScore score);
+    public delegate Response Invoke(Match m);
 }

+ 1 - 1
src/Yuuna.Contracts/Patterns/IPatternSet.cs

@@ -7,6 +7,6 @@ namespace Yuuna.Contracts.Patterns
 
     public interface IPatternSet : IImmutable<IPattern>
     {
-        bool TryGet(IPattern pattern, out Invoke invoke);
+        bool TryGet(IPattern pattern, out Bag bag);
     }
 }

+ 4 - 4
src/Yuuna.Contracts/Patterns/Internal/Bag.cs

@@ -3,10 +3,10 @@
 
 namespace Yuuna.Contracts.Patterns
 {
-    internal struct Bag
+    public struct Bag
     {
-        internal Incomplete Incomplete;
-        internal Invoke Invoke;
-        internal IPattern Pattern;
+        public Incomplete Incomplete;
+        public Invoke Invoke;
+        public IPattern Pattern;
     }
 }

+ 3 - 15
src/Yuuna.Contracts/Plugins/PluginBase.cs

@@ -14,8 +14,7 @@ namespace Yuuna.Contracts.Plugins
     public abstract class PluginBase
     {
         private readonly PatternFactory _patternfactory;
-        private StrategyBase _strategy;
-
+        
         internal IPatternSet Patterns => this._patternfactory;
 
         public PluginBase()
@@ -23,21 +22,10 @@ namespace Yuuna.Contracts.Plugins
             this._patternfactory = new PatternFactory(this);
         }
 
-        [Obsolete]
-        internal IImmutableList<IScore> Evaluate(IImmutableList<string> cutted)
-        {
-            return this._strategy.Assess(this, cutted, this._patternfactory);
-        }
-        [Obsolete]
-        internal Invoke SelectBest(IScore score)
-        {
-            return this._patternfactory.TryGet(score.Pattern, out var b) ? b : null;
-        }
+      
 
-        internal bool Initialize(StrategyBase strategy, ITextSegmenter textSegmenter, IGroupManager groupManager)
+        internal bool Initialize(ITextSegmenter textSegmenter, IGroupManager groupManager)
         {
-            this._strategy = strategy;
-
             try
             {
                 this.BuildPatterns(groupManager, this._patternfactory);

+ 71 - 45
src/Yuuna/EntryPoint.cs

@@ -24,6 +24,10 @@ namespace Yuuna
     {
         public static void Main(string[] args)
         {
+
+            Send("打開門");
+
+            return;
             // Send("打開門"); Send("開燈");
             try
             {
@@ -63,64 +67,86 @@ namespace Yuuna
         {
             var segmenter = new JiebaTextSegmenter() as ITextSegmenter;
             var allPlugins = new PluginBase[] { new Fake() };
-            var strategy = new DefaultStrategy();
             foreach (var item in allPlugins)
             {
-                item.Initialize(strategy, segmenter, new GroupManager());
+                item.Initialize(segmenter, new GroupManager());
                 Console.WriteLine("已載入模組: " + item.GetType().AssemblyQualifiedName);
             }
 
             Console.WriteLine("我: " + text);
             var cutted = segmenter.Cut(text);
             Console.WriteLine($"來自分詞器 {segmenter.Name} 的分詞結果: [ {string.Join(", ", cutted)} ]");
-             
-
-            var list = new List<IScore>();
-            foreach (var p in allPlugins)
-                list.AddRange(p.Evaluate(cutted));
-            list.Sort(ScoreComparer.Default);
-
-            var sb = new StringBuilder("Bot: ");
-            var best = list.LastOrDefault();
-            if (best is null)
-                sb.Append("我不懂你的意思");
-            else if (best.IsCompacted)
-            {
-                var resp = best.Owner.SelectBest(best).Invoke(best);
-                Console.WriteLine(resp.Mood.ToString());
-                sb.Append(resp.Message);
-            }
-            else
+
+            IStrategy plan = new DefaultStrategy();
+            var m = plan.FindBest(allPlugins, cutted);
+            if (m.HasValue)
             {
-                var choices = list.Where(s => s.Missing.Count <= 1).ToImmutableArray();
-                switch (choices.Length)
+                var match = m.Value;
+                var pattern = match.Pattern;
+                if (pattern.Owner.Patterns.TryGet(pattern, out var bag))
                 {
-                    case 1:
-                        {
-                            Console.WriteLine("#1");
-                            sb.Append("你" + new[] { "是想", "想要" }.RandomTakeOne() + " ");
-                            sb.Append(choices.Select(x => x.Pattern.ToImmutable().Aggregate(string.Empty, (s, g) => s += g.ToImmutable().RandomTakeOne().ToImmutable().RandomTakeOne())));
-                            sb.Append(" 嗎?");
-                        }
-                        break;
-
-                    case 2:
-                        {
-                            Console.WriteLine("#2");
-                            sb.Append("你" + new[] { "是想", "想要" }.RandomTakeOne() + " ");
-                            sb.AppendJoin(" 還是 ", choices.Select(x => x.Pattern.ToImmutable().Aggregate(string.Empty, (s, g) => s += g.ToImmutable().RandomTakeOne().ToImmutable().RandomTakeOne())));
-                            sb.Append(" ?");
-                        }
-                        break;
-
-                    default:
-                        Console.WriteLine("#n");
-                        sb.Append("我不太清楚你想做什麼");
-                        break;
+                    if (match.IsExactMatch)
+                    {
+                        Console.WriteLine(bag.Invoke(match));
+                    }                    
+                    else
+                    {
+                        Console.WriteLine();
+                    }
                 }
+                else
+                    throw new Exception("未知的錯誤");
             }
+            else
+                Console.WriteLine("沒有任何模組相符");
+
+
+            //var list = new List<IScore>();
+            //foreach (var p in allPlugins)
+            //    list.AddRange(p.Evaluate(cutted));
+            //list.Sort(ScoreComparer.Default);
+
+            //var sb = new StringBuilder("Bot: ");
+            //var best = list.LastOrDefault();
+            //if (best is null)
+            //    sb.Append("我不懂你的意思");
+            //else if (best.IsCompacted)
+            //{
+            //    var resp = best.Owner.SelectBest(best).Invoke(best);
+            //    Console.WriteLine(resp.Mood.ToString());
+            //    sb.Append(resp.Message);
+            //}
+            //else
+            //{
+            //    var choices = list.Where(s => s.Missing.Count <= 1).ToImmutableArray();
+            //    switch (choices.Length)
+            //    {
+            //        case 1:
+            //            {
+            //                Console.WriteLine("#1");
+            //                sb.Append("你" + new[] { "是想", "想要" }.RandomTakeOne() + " ");
+            //                sb.Append(choices.Select(x => x.Pattern.ToImmutable().Aggregate(string.Empty, (s, g) => s += g.ToImmutable().RandomTakeOne().ToImmutable().RandomTakeOne())));
+            //                sb.Append(" 嗎?");
+            //            }
+            //            break;
+
+            //        case 2:
+            //            {
+            //                Console.WriteLine("#2");
+            //                sb.Append("你" + new[] { "是想", "想要" }.RandomTakeOne() + " ");
+            //                sb.AppendJoin(" 還是 ", choices.Select(x => x.Pattern.ToImmutable().Aggregate(string.Empty, (s, g) => s += g.ToImmutable().RandomTakeOne().ToImmutable().RandomTakeOne())));
+            //                sb.Append(" ?");
+            //            }
+            //            break;
+
+            //        default:
+            //            Console.WriteLine("#n");
+            //            sb.Append("我不太清楚你想做什麼");
+            //            break;
+            //    }
+            //}
 
-            Console.WriteLine(sb);
+            //Console.WriteLine(sb);
         }
     }
 }

+ 0 - 13
src/Yuuna/Strategy/Policy.cs

@@ -1,13 +0,0 @@
-
-namespace Yuuna
-{
-    using System;
-    using System.Collections.Generic;
-    using System.Text;
-    using Yuuna.Common.Linq;
-
-    public interface IStrategy
-    {
-        void Evaluate(IImmutable<string> cutted);
-    }
-}