2017年7月30日 星期日

[.NET][Python] 集合處理筆記

Wiki
MapReduce is a programming model and an associated implementation for processing and generating big data sets with a paralleldistributed algorithm on a cluster.
集合之間的轉換,與元素操作是資料處理的日常,第一次認識MapReduce概念其實是從Python來,但發現.NET也有等價的處理(從Stackoverflow發現,搜尋"Python Map Equipvalent in c#"):

2017年7月26日 星期三

[Blogger]文內插入Latex(Mathjax)

紀錄一下如何在Blogger中插入Latex,用到的Javascript來自mathjax,他的說明提供了很簡要快速的步驟:

2017年7月22日 星期六

[.NET] 流(Stream)的理解心得


Wiki說得好:
「流」是一連串從I/O設備讀寫的字符(Byte)

流(Stream)是.NET中非常基礎的型態,存取方式是很單純的位元組依序寫入/讀出(Byte by Byte Writing/Reading),會有一個內藏Counter紀錄住目前存取位置(Position,有點類似座標),存取時都是從目前位置開始存取.

若將整個讀取完畢的Stream攤平開來,也是可以想像成時域信號圖,橫軸是位置,只是要確定好座標原點(Origin)在何處

跟位元組陣列有著類似的概念,只是位元組陣列可以隨機存取:給定INDEX存取,座標原點就是第一個元素,只需一個存取動作:給定INDEX後返回該位置的資料;若要做類似隨機存取操作,Stream必須先透過Seek改變目前位置,才能開始存取,因此需要兩個存取動作:改變目前位置、存取資料

Stream存的個別元素是位元組,也就是0-255之間的一個數值,可以說是資訊系統內超級底層的資料型態之一,也就是說Stream是一種極為基礎的資料儲存結構;任何的高階資料型態(物件、字串)都能轉換成位元組形式去表達(某種映射):

  • 物件-位元組轉換(物件與位元組之間的映射)
    • 序列化與反序列化(Serialization/Deserialization)
    • 而反序列化/序列化則是互為反函式(Inverse)

  • 字元-位元組轉換(字元與位元組之間的映射)
    • 按字碼表Encoding/Decoding
    • 線性映射?
Stream的資料表達方式容易直接轉化為現實上的物理資料傳輸,舉凡磁碟資料寫入(File)、網路資料傳輸、通訊埠資料傳輸,都是出口Stream(Out-Stream)內按位元組取出,轉化成位元(1/0)脈衝信號後在物理層一點一滴傳達;接著在目標系統物理層中收集這些脈衝信號還原成位元組,按位元組一個一個塞進目標系統的入口Stream(In-Stream)中

後續應用就視情況看是要透過反序列化還是Decoding,轉換成更高階的資料表達型態

.NET的類別規劃上,可以在相關的硬體抽象物件找到Stream的身影,好比說以下衍生自Stream的型別:
既然都是Stream,肯定是有共同性質的:)後續研究學習

參考:這系列對於Stream介紹蠻不錯

2017年7月20日 星期四

[無聊的心得] Overhead

Overhead,名詞
https://en.wikipedia.org/wiki/Overhead_(computing)

中文叫做經常開支,為了達成某種目的所必須付出的與目標無關的代價

講到Overhead我就想到酬載(Payload),用貨車載貨來聯想:想像一台20噸重的卡車載了一顆20克的蘋果, 此時20克的蘋果就是酬載(Payload),如果送到目的地可以賺10塊錢,而推動20噸車體到目的地所需的油料消耗是3000塊,屬於經常開支(Overhead),恩...好一門美麗的賠錢生意。

思緒回到資訊世界,標頭會帶來Overhead,以XML格式資料為例,角括號裡面的是屬性(Property)名稱,而屬性間的包覆關係是階層格式描述,無論是角括號本身、頭尾的包覆這些都是經常開支(Overhead),因為其實你只在乎<></>之間的值(Value)資訊

看看以下酬載少於經常開支:
 
<themotherfuckerextremelongpropertyname>
1
</themotherfuckerextremelongpropertyname>

那麼傳輸時所花費的時間、硬體損耗都是花在經常開支而不是酬載(花在傳輸PropertyName)大部分資源都花在傳輸Header,沒意義的Overhead導致效率不彰,就要開始動腦筋改善這問題嚕

比方說每次傳輸時格式都是固定,我能不能不要傳Header,以隱含的方式表達資訊?還是縮短PropertyName?好像都不錯

[.NET] 執行序同步慣用Pattern心得 - I


MSDN說:
需要使用者互動的軟體必須儘快回應使用者的活動,以便提供豐富的使用者經驗。不過,同時它也必須執行需要的計算以儘快提供使用者資料。
在個人目前的工作領域,使用者體驗並非放在第一(甚至是最後一名),大部分時間都花在Coding工作處理常式(Routines),主要實現概念為狀態機(State-Machine)與多工處理(Multi-Task),在Main Thread執行

常式內的多工工作是純粹的商業邏輯,少有牽涉I/O存取,處理頻率穩定近乎週期性,但來源資訊難免要透過外部裝置取得,如網路介面、PCI裝置、使用者輸入...等,幾乎是慢速、頻率較低的資料源,為了避免影響Main Thread的多工輪詢,此時就可考慮用其他Thread分擔低速工作。

整理三個與狀態機搭配的常用分緒招,共通點是在Main Thread輪詢某資料狀態,該輪詢函式必須不能阻塞(Blocking)住Main Thread,否則會影響到其他多工作業

(沒實測過這些方法的具體效能,比較在乎應用上的設計模式(Pattern)是否容易嵌入既有架構)
  • Task
    • 發動:
      • 將要執行的Method裝載到Task物件中後,呼喚Start()開始執行緒作業
      • 也可以利用工廠模式,呼叫共用方法Task.Run()開始執行緒作業,同步狀態透過傳回的Task實例來操作
    • 同步:
      • Worker Thread輪詢Task.IsComplete至狀態為True
    • 心得:
      • Task封裝了Thread與若干執行緒同步元件(如Mutex),因此包含了執行緒操作與同步操作
      • 據說是在執行緒池內作業(待確認
  • ThreadPool
    • 發動:
    • 同步:
      • 無法得知方法最終被放到哪個具體執行緒,也沒封裝同步操作方法,所以必須在Method裡自行設計同步物件來辨識是否方法執行完成
      • 個人慣用Concurrent執行緒安全元件,如ConcurrentQueueConcurrentDictionary傳遞資訊到Worker Thread
      • Worker Thread輪詢Concurrent元件
    • 心得:
      • 最簡單易用赤裸的異步編成模式,不含任何同步方法的純粹執行緒池操作最?似乎懂了些什麼了?
  • MulticastDelegate
    • 發動:
      • 將要執行的Method裝載到Delegate中
      • 呼叫該Delegate的BeginInvoke(),必須帶入回呼方法(Callback)
      • 必須呼叫EndInvoke()作為確認該執行緒執行完成的辨識?
    • 同步:
      • 輪詢呼叫該Delegate時傳回的IAsyncResult.IsCompleted
      • 或是在Main Thread呼叫EndInvoke()(但是可能阻塞)
    • 心得:
      • 與Task一樣,封裝了執行緒操作與同步操作,只是設計模式上透過IAsyncResult或EndInvoke達成同步
      • 據說是在執行緒池內作業(待確認
class Program
    {


        static void Main(string[] args)
        {
            int state = 0;

            Action __delegate = null; 
            Task __task = null;
            IAsyncResult handle = null;
            ConcurrentQueue exchange = new ConcurrentQueue();

            int outValue = 0;
            //the main state machine
            Program.method();
            while (state != 500)
            {
                switch (state)
                {
                    case 0:
                        // use Task 
                        __task = new Task(new Action(Program.method));
                        __task.Start();
                        state +=10;
                        break;
                    case 10:
                        // wait until task done
                        if (__task.IsCompleted)
                            state +=10;
                        break;
                    case 20:
                        // use thread pool
                        ThreadPool.QueueUserWorkItem((object foo)=>{
                            Program.method();
                            exchange.Enqueue(0); // enqueue some meaningfll result
                        },null);
                        state+=10;
                        break;
                    case 30:
                        //wait until method had been scheduled and executed
                        if (exchange.TryDequeue(out outValue))
                            state +=10;
                        break;
                    case 40:
                        // use delegate/IAsyncResult 
                        __delegate =  new Action(method);
                        handle = __delegate.BeginInvoke((IAsyncResult ar)=> 
                            {
                                method();
                                __delegate.EndInvoke(ar); // you had to call this to release ar
                            },null);
                        break;
                    case 50:
                        // wait until method had been executed
                        if (handle.IsCompleted)
                            state = 500;
                        break;
                    default:
                        break;
                }
                Thread.Sleep(0);// yield to other thread to run
            }//while
        }

        static void method()
        {
            //reflect the current thread
            Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
        }

    }

後記1:MSDN的異步編程模式概述,對於非同步作業可有概略且完整的認識,其中APM(
Asynchronous Programming Model,就是Delegate/IAyncResult)已經漸漸式微變成不太推薦的Pattern

後記2:在.NET Core(MAC)上實測Delegate.BeginInvoke是會有例外的,原因在此,大致上是被開發小組視為Deprecated方法,未來可能會被幹掉

2017年7月17日 星期一

[.NET] REGEX 利用 Group 捕捉(Capture)重複字串(Repetitive Pattern)

Regular Expression(正規表達式),是一種抽象語言,用來表達字串的結構,比方[A-Za-z]+能表達所有的英文單字,應用上常用來驗證輸入,或是捕捉(Capture)具有特徵的字串片段,利用Regular Expression可以大幅簡化字串處理的繁瑣作業

這次的問題如下,假設某種字串格式的具體內容是以前綴(prefix)後綴(post-fix)框住,怎麼利用正規表示式驗證並捕捉(Capture)重複的片段?

好比以下類似座標的紀錄檔:
Model:M1
Coordinates:
X1.62 Y1.01
X9.53 Y2.3
X4.94 Y2.1
X8.55 Y3.15
X2.56 Y1.25

X5.57 Y2.72

首先分析一下文本的結構:
  • 標頭如同前兩行,形式與內容固定
  • 重複資料部分按行表達
  • 單筆座標以X<浮點數> Y<浮點數>表示
    • 座標值是我們最在意的部分
    • 希望可以一組一組捕捉分離,才好批次後處理解析數值
模式字串(Pattern)的設計想法如下:
  • 浮點數的表達不用傷腦筋,網路上很容易查到
  • 座標值的部分用()群組住單組X,Y座標(也是一種Symbol),後面以+表達此組Symbol會重複1到N次
  • 單組座標內又再次以()群組住浮點數字串表達
  • 考慮強健性,安插些泛空白字元在各個單元交接處
於是我們交出了以下的模式字串 :

"Model:[\\w]+[\\s]+Coordinates:[\\s]+(X([-+]?[0-9]*\\.?[0-9]+)[\\s]*Y([-+]?[0-9]*\\.?[0-9]+)[\\s]*)+"

其中"[-+]?[0-9]*\.?[0-9]+"就是浮點數的表達

這樣丟下去REGEX跑字串,模式字串中的()可以透過Match.Group(Index)存取,在這裡的模式字串我們共用到三個(),按照()在模式字串裡的順序,Group依序是:

  • Index-0:原始字串
  • Index-1:單組座標的群組
  • Index-2:X座標浮點數字串
  • Index-3:Y座標浮點數字串



String __input = 
"Model:M1 Coordinates: X1.62 Y1.01 X9.53 Y2.3 X4.94 Y2.1 X8.55 Y3.15 X2.56 Y1.25 X5.57 Y2.72";
String __patternUnnamed = 
"Model:[\\\\w]+[\\\\s]+Coordinates:[\\\\s]+(X([-+]?[0-9]*\\\\.?[0-9]+)[\\\\s]*Y([-+]?[0-9]*\\\\.?[0-9]+)[\\\\s]*)+";
List __coords = new List();
Match __matchUnnamed = Regex.Match(__input,__patternUnnamed);
//-----------------------------------
//  Unnamed         
//-----------------------------------
__coords.Clear();
for (int i = 0; i < __matchUnnamed.Groups[1].Captures.Count;i++)
{
// use X,Y group restore each coordinates
float[] eachCoord = new float[]{0,0};
eachCoord[0] = float.Parse(__matchUnnamed.Groups[2].Captures[i].Value);
eachCoord[1] = float.Parse(__matchUnnamed.Groups[3].Captures[i].Value);
__coords.Add(eachCoord);
}
//check all coordinates
Console.WriteLine(\"Unnamed\");
foreach (float[] item in __coords)
{
Console.WriteLine(String.Format(\"{0},{1}\",
item[0],
item[1]));
}
若模式字串裡()太多,或是覺得用INDEX標定群組可讀性太差,此時可以套入具名群組(Named Group)的概念,把()所圈住的Symbol具名化:

Model:[\\w]+[\\s]+Coordinates:[\\s]+(?<coord>X(?<X>[-+]?[0-9]*\\.?[0-9]+)[\\s]*Y(?<Y>[-+]?[0-9]*\\.?[0-9]+)[\\s]*)+

具名化後,便可以用Match.Group(Name)方式存取群組了

String __patternNamed = 
"Model:[\\\\w]+[\\\\s]+Coordinates:[\\\\s]+(?X(?[-+]?[0-9]*\\\\.?[0-9]+)[\\\\s]*Y(?[-+]?[0-9]*\\\\.?[0-9]+)[\\\\s]*)+";
Match __matchNamed = Regex.Match(__input,__patternNamed);
//-----------------------------------
//  Named
//-----------------------------------
//now we get the \"coord\" group
__coords.Clear();
for (int i = 0; i < __matchNamed.Groups[\"coord\"].Captures.Count; i++)
{
// use X,Y group restore each coordinates
float[] eachCoord = new float[]{0,0};
eachCoord[0] = float.Parse(__matchNamed.Groups[\"X\"].Captures[i].Value);
eachCoord[1] = float.Parse(__matchNamed.Groups[\"Y\"].Captures[i].Value);
__coords.Add(eachCoord);
}


後記1:.NET的正規表達式相容於Perl5,也與這裡相容,可以先快速的先驗表達式
後記2:據說.NET的Regular Expression模組是難得可以幫你Capture住群組值(Value)的函式庫

程式碼Repo

2017年7月16日 星期日

[.NET] 記憶體洩漏(Memory Leak)可能原因 - XML Serializer


承接此篇

.NET最爽的就是背後有GC(Garbage Collection)罩著,想配置物件就配置物件(NEW),用完拍拍屁股就走了,等下就會有服務生來收碗盤了,多爽!

可是如果你點到的菜是XML Serializer,可能就沒辦法那麼幸運了,GC是不收這道菜的,亂點到最後...整個餐廳桌上都會是你之前吃剩的XML Serializer

GC無法自動回收XML Serializer,(原因Dynamically Generated Assemblies段落),過於任性大量配置Serializer的話,記憶體可是會爆的

既然如此只能讓自己勤勞點,MSDN推薦的作法是,快取(Cache)住使用過的XML Serializer,每次欲利用XML Serializer時,統一向管理函式索取,若快取庫內沒有,則重新配置一個,若有,則取出既有的參考傳回,如此一來再利用時便不用重新配置,也就避免了潛在的記憶體洩漏問題了

參考以下程式碼:

  • 函式原型:
    • 引數 - 要序列化的物件型別(若物件是衍生自某基底型別,則透過陣列傳入具體衍生型別)
    • 傳回值 - XML Serializer實例
    • 視野(Scope): public static,視為工具函式(Utility)



 
 class utilities
    {
        /// <summary>
        /// Used to store key-serializer
        /// </summary>
        /// <returns></returns>
        private static Hashtable __dictionary = new Hashtable(); 
        /// <summary>
        /// Generate key according to contents of type list
        /// </summary>
        /// <param name="typeList"></param>
        /// <returns></returns>
        protected static int generateKey(List<Type> typeList)
        {
            //hash code summarize
           return typeList.Sum(__type => __type.GetHashCode());
        }
        /// <summary>
        /// Always fetch serializer from this function
        /// (Never allocate by yourself)
        /// </summary>
        /// <param name="mainType"></param>
        /// <param name="extraTypes"></param>
        /// <returns></returns>
        public static XmlSerializer getSerializer(Type mainType,Type[] extraTypes){
            
            //merge the list
            List<Type> __typeList = new List<Type>();

            __typeList.Add(mainType);
            __typeList.AddRange(extraTypes);

            var key = generateKey(__typeList);

            if (__dictionary.ContainsKey(key))
            {
                // this kind of serializer had been cached/used before , 
                // get the cached serializer
                return (XmlSerializer)__dictionary[key];
                }
            else
            {
                // never been used , need to create a new one and push into cache
                XmlSerializer __serializer = new XmlSerializer(mainType,extraTypes);
                __dictionary.Add(key,__serializer);
                return __serializer;
             }
        }//getserializer
    }//utilities

參考程式碼:https://github.com/smandyscom/cached_xmlSerializer

2017年7月8日 星期六

[VS Code x C#] VS Code加入參考


最近用起了VS Code,只是想單純地做一些C#程式範例(Snippet),起初照了美女Demo
(她真的很甜)安裝設定,簡要如下:


  1. 安裝VS Code(只是一個Open Architecture文字編輯器
  2. 安裝.NET Core(就是.NET Framework 核心肌群
  3. 在VS Code下安裝種種必須Plugin
  4. 下指令初始化專案資料結構
    1. $dotnet new 
  5. 開寫C#

可是呢,當我嘗試想用一些命名空間組件時就開始鬼打牆,好比說System.Collections.Hashtable,明明已經在最前面寫Using System.Collections
可VS Code不會讓你Build過,原因是 xxx Type or name could not be found

原因很簡單,你沒替專案加入參考
如同在Visual Studio下,要引用其他組件除了要在程式碼最開頭帶入Namespace(Using xxxxx),也必須在專案下加入參考(歐~想起來了)

以下紀錄一下如何替VS Code C# Project加入參考

首先打開下面的 終端機(Terminal)以以下指令下載Package,好像是可以想成下載函式庫
#dotnet add package <AssemblyName>

為什麼是System.Collections.NonGeneric?參考的Assembly(.dll/動態函式庫)怎麼查詢?
可看API Reference,裡面的Assembly就是組件名稱,以Hashtable為例,必須要加入System.Collections.NonGeneric,所以下
#dotnet add package System.Collections.NonGeneric

系統會跑出一些訊息表示正在Download,最終會處理完成,如果來了一些Error,比較大可能性是"打錯字",好比System.Collections打成System.Collection(少一個s)

下完以後,再下
#dotnet restore
會自動幫你在.csproj內引入dependencies

你的.csproj會多出dependencies片段:

 
<project sdk="Microsoft.NET.Sdk">
  <propertygroup>
    <outputtype>Exe</outputtype>
    <targetframework>netcoreapp1.1</targetframework>
  </propertygroup>
  <itemgroup>
    <packagereference include="System.Collections" version="4.3.0">
    <packagereference include="System.Collections.NonGeneric" version="4.3.0">
    <packagereference include="System.Runtime.Extensions" version="4.3.0">
    <packagereference include="System.Xml.XmlSerializer" version="4.3.0">
  </packagereference></packagereference></packagereference></packagereference></itemgroup>
</project>

這些是原先新專案結構初始化後所沒有的片段
加入組件到此就沒問題了

咦,若是要加入其他參考專案呢?
可以參考這個指令,dot add reference

後記:網路上會查到很多 手動改project.json以加入dependencies的建議,看起來是比較過去的作法,目前版本的.NET Core SDK似乎要嘗試以.csproj做統整(與Visual Studio一致),捨棄project.json作為專案結構的描述,也許是如此一來專案比較好在VS Studio與VS Code兩個平台上操作而不會有轉換的困擾