Quantcast
Channel: Darkthread
Viewing all 428 articles
Browse latest View live

快速將C#型別轉成TypeScript介面定義

$
0
0

使用TypeScript處理AJAX呼叫時,常需要在前端定義與C#物件屬性一致的TypeScript型別,以便將後端傳來的JSON資料還原成強型別物件。針對較正式的資料模型,我會用CodeGen方式同步C#與TypeScript端的型別定義(順便處理多語系問題)。但蠻多時候處理對象只是零散的小型別,不必殺雞用牛刀,針對這類需求,推薦一個好用工具-TypeLITE

在NuGet查關鍵字"typelite",很快就可找到TypeLite套件。

安裝後,專案會加入TypeLite.dll、TypeLIte.Net4.dll兩顆DLL,並在Scripts目錄下新増TypeLite.Net4.tt。接著就可以修改TypeLite.Net4.tt內容,決定為哪些型別產生TypeScript定義。

TypeLITE預設做法是執行.ForLoadedAssemblies()自動尋找組件標註[TsClass] Attribute的型別,但實務上用.For<型別名稱>()列舉要轉換的型別(如紅框所示)比較方便;若型別來自網站專案以外的組件,記得要加上<#@ assembly #>宣告(如黃框所示)。

修改TypeLite.Net4.tt並存檔,T4工具將自動產生或更新TypeLite.Net4.d.ts,在其中可看到我們用.For<型別名稱>()所列舉型別的TypeScript介面定義,若型別有參照到其他型別,TypeLITE會自動包含進來,非常方便。(在範例中用到Reflection的MethodInfo,相關介面就像提棕子一般整串被拉進來。)

TypeScript定義檔僅在開發編譯階段提供強型別約束及提示,不會產生任何JavaScript,不用擔心TypeLite.Net4.d.ts的內容太複雜。就這樣,輕輕鬆鬆幾個步驟,就把C#端的型別定義搬到TypeScript端囉~

又到了呼口號時間:TypeLITE,好用!好用!好用!


善用VS2015 NuGet Manager解決方案管理功能

$
0
0

NuGet已經是Visual Studio寫專案時的柴米油鹽醬醋茶了,但有些人可能沒發現在解決方案(.sln,Solution)層也可以開啟NuGet Manager。如下圖,在Solution Explorer的.sln開右鍵選單,有一個「Manage NuGet Packages for Solution…」:

在解決方案開啟NuGet Manager可以一次檢視所有專案的Package安裝狀況,但在VS2013時代,這功能有點雞肋。如以下例子,解決方案裡總共有三個專案安裝兩種不同版本HelperSharp,於是NuGet Manager的Package清單會看到兩筆HelperSharp,點選其中一項,右側則會顯示它被安裝在哪幾個專案(Currently installed in projects),點下Manage鈕可以批次移除。

Visual Studio 2015對NuGet Manager介面做了大幅改版,解決方案的套件管理介面變得更貼心好用。首先,同一套件的新舊版本被整合成單一項目,在右側以清單方式呈現套件在各專案的安裝狀況,並直接顯示安裝的版本號碼。如果你常常處理元件版本衡突,一定能了解這張彙總清單有多珍貴。:P

而處理元件版本衝突時,更新到最新版有時不是最佳解,降版牽就某些既有程式庫反而比較省事。過去NuGet Manager UI只提供解除安裝或升級到最新版,要安裝特定版本需要使用命令列工具下指令。VS2015的NuGet Manager安裝套件時可由版本清單選取最新版或歷史版本,還可透過勾選一次安裝或更新多個專案,解決版本衡突一次到位。

身為三不五時要處理元件版本衝突的苦手,發現這個功能的當下,我彷彿看到Visual Studio開發人員對我說「你的痛苦,我懂!」XD 再次為地表最強的開發工具按個讚!

CTE應用-將多筆查詢結果合併成逗號分隔字串

$
0
0

一對多關聯是常見的資料庫應用情境,有時我會遇到將多筆關聯資料特定欄立合併成逗號分隔字串的需求。聽起來有點抽象,用個實例說明。

假設有兩個資料表,應用系統清單及負責該系統的工程師名字,用以下指令建立模擬資料:

CREATETABLE AppSystem (
  Name VARCHAR(16),
  CodeName VARCHAR(16)
)
INSERTINTO AppSystem VALUES ('HR', 'Mars');
INSERTINTO AppSystem VALUES ('ERP', 'Jupiter');
INSERTINTO AppSystem VALUES ('POS', 'Venus');
CREATETable AppSupport (
   AppName VARCHAR(16),
   Engineer VARCHAR(16)
)
INSERTINTO AppSupport VALUES ('HR', 'Jeffrey');
INSERTINTO AppSupport VALUES ('ERP', 'Jeffrey');
INSERTINTO AppSupport VALUES ('POS', 'Jeffrey');
INSERTINTO AppSupport VALUES ('ERP', 'Darkthread');
INSERTINTO AppSupport VALUES ('POS', 'Darkthread');
INSERTINTO AppSupport VALUES ('HR', 'SecrectSound');
INSERTINTO AppSupport VALUES ('ERP', 'MouthCannon');

如下圖所示,資料顯示有HR、ERP、POS三個系統,而工程師共四位,每個人各負責1到3個系統。

用簡單的JOIN查詢就能列出每個系統負責的工程師,但有個缺點,每個工程師配一個系統會出現一次,故系統資料會重複,例如:HR、POS各出現兩次,ERP出現三次。

若目的在產生報表,理想的呈現方式是將同一系統的工程師名字合併成以逗號分隔的字串,像這樣:

要轉換成上述格式,我試過幾種做法:

  1. 建Temp Table跑Cursor統整資料
    我用T-SQL寫Cursor的功力很鳥,有馬拉松跑者被抓去游泳的fu,而建Temp Table跑Cursor迴圈的程序感覺有點繁瑣沒效率。
  2. 將JOIN結果拉回.NET端整理
    寫C#彙整資料這類雕蟲小技是我的強項,幾乎是信手拈來,開發跟執行效率都不會太差。但拉到.NET端處理有個缺點,JOIN完的重複資料需透過網路傳輸傳到網站伺服器後才能加工,若主資料的欄位又多又長,同一筆重覆次數又高,浪費的頻寬很可觀。

爬文找到更多做法,包含FOR XML PATH、自訂函式、SQLCLR函式、CTE… 等。之前體驗過CTE(Common Table Expresion)的特異功能,讓我頗為驚喜,當發現CTE也可解決這類需求,便決定動手試試。

CodeProject上有一篇好文章,照方煎藥,這個案例的合併欄位CTE寫法如下:

;WITH SupportCTE (RowNum, Name, CodeName, Engineer,Engineers) AS
(
SELECT 1, A.Name, A.CodeName, MIN(B.Engineer), 
CAST(MIN(B.Engineer) ASVARCHAR(MAX)) AS Engineers
FROM AppSystem A JOIN AppSupport B ON A.Name = B.AppName
GROUPBY A.Name, A.CodeName
 
UNIONALL
 
SELECT A.RowNum + 1, A.Name, A.CodeName, B.Engineer, 
CAST(A.Engineers + ', ' + B.Engineer ASVARCHAR(MAX)) As Supports
FROM SupportCTE A JOIN AppSupport B 
ON A.Name = B.AppName AND B.Engineer > A.Engineer 
)
 
SELECT * FROM SupportCTE

上次一樣,我們要善用 CTE 主查詢 UNION ALL 遞迴查詢的原理解決問題。程式有幾個重點,第一段為主查詢,JOIN AppSystem及AppSupport兩個資料表,除了Name及CodeName、Engineer外,又多加RowNum及Engineers兩個欄位。Engineers存放工程師名字串接結果,RowNum統計Engineers包含的工程師資料筆數。一個系統可能有多位工程師,Engineer要用GROUP BY及MIN()找出名字排序最前者(稍後遞迴時要靠它排除處理過的項目,防止陷入無窮遞迴,這個環節很重要)。UNION ALL接的第二段查詢將被遞迴呼叫(故一定要設計跳出遞迴的邏輯),原理是利用現有結果反覆JOIN AppSupport找出同一系統的其他工程師,將其串接於Engineers欄位後方,同時將RowNum+1,Engineer則換成本次串接的工程師名稱。JOIN條件限定工程師名字排序要在既有資料之後,可排除已串接過的項目,避免無窮迴圈。

直接SELECT SupportCTE可以看到遞迴查詢的結果,比較能體會其運作原理。

如上圖所示,每個系統都有多筆資料,Engineers分別由1-3位工程師名字串接而成,RowNum即為串接資料的個數。如此,我們只需找出每個系統RowNum最大的一筆,即為最終結果。這應該難不倒冰雪聰明的你,用以下寫法就可輕鬆搞定:

SELECT A.Name, A.CodeName, A.Engineers FROM SupportCTE A
JOIN (SELECT Name, Max(RowNum) AS MaxRowNum FROM SupportCTE GROUPBY Name) B
ON A.Name = B.Name AND A.RowNum = B.MaxRowNum

補充:針對找出RowNum最大的一筆,路人乙提供一個更棒的解法,不用JOIN就可以搞定,特此感謝!

SELECT Name,CodeName,Engineers
FROM (
SELECT *,ROW_NUMBER() OVER (PARTITION BY Name ORDERBY RowNum Desc) AS Pos
FROM SupportCTE
) T
WHERE Pos = 1 
ORDERBY Name

薑!薑!薑!薑~

實做一趟的感想:用CTE實現多筆資料串接需要動用遞迴,設計起來要費點腦筋,有點呼應那句「遞迴只應天上有,凡人應當用迴圈」的俗諺,但只要悟透原理,也不到難以駕御的程度,而且試出來的那一刻,還有種自己通過智力測驗的成就感(笑),我想我會開始在專案上試用這種寫法

2016-01-21更新:經過實測,若只是要合併欄位,FOR XML PATH比CTE簡便有效率

欄位合併效能比較:CTE vs FOR XML

$
0
0

前一篇文章介紹用CTE實現SQL一對多關聯欄位合併的方法,找資料期間曾發現另一個替代做法,利用SQL Server的FOR XML PATH('')技巧,可將多筆資料轉成單一字串(參考),看來也相當簡潔。但當下覺得子查詢加FOR XML轉換的做法有效能疑慮,理應不如CTE(事實不然!),加上搞CTE比較有挑戰性,故選了CTE研究嚐鮮。

網友ChoeChin留言提到FOR XML寫法,讓我重新思考:如果要大量重度應用,效能議題不可忽視,CTE效能是否真的比FOR XML方法好?有待實驗證實。經一番測試後,結論是我錯估了CTE與FOR XML兩種做法的效能表現。

原本的測試樣本太小,很難看出效能差異。我改用以下指令生出10萬筆糸統,並將AppSystem的Nam欄位與AppSupport的AppName, Engineer欄位設成Primary Key確保查詢效能。

DECLARE @I INT, @NOVARCHAR(6);
SET @I = 0;
TRUNCATETABLE AppSystem; 
TRUNCATETABLE AppSupport;
WHILE @I < 25000
BEGIN
SET @NO = CONVERT(VARCHAR(6), @I);
INSERTINTO AppSystem VALUES ('HR-' + @NO, 'Mars-' + @NO);
INSERTINTO AppSystem VALUES ('ERP-' + @NO, 'Jupiter-' + @NO);
INSERTINTO AppSystem VALUES ('POS-' + @NO, 'Venus-' + @NO);
INSERTINTO AppSystem VALUES ('MAIL-' + @NO, 'Apolo-' + @NO);
INSERTINTO AppSupport VALUES ('HR-' + @NO, 'Jeffrey-' + @NO);
INSERTINTO AppSupport VALUES ('ERP-' + @NO, 'Jeffrey-' + @NO);
INSERTINTO AppSupport VALUES ('POS-' + @NO, 'Jeffrey-' + @NO);
INSERTINTO AppSupport VALUES ('ERP-' + @NO, 'Darkthread-' + @NO);
INSERTINTO AppSupport VALUES ('POS-' + @NO, 'Darkthread-' + @NO);
INSERTINTO AppSupport VALUES ('HR-' + @NO, 'Alice-' + @NO);
INSERTINTO AppSupport VALUES ('ERP-' + @NO, 'Bob-' + @NO);
INSERTINTO AppSupport VALUES ('MAIL-' + @NO, 'Jeffrey-' + @NO);
SET @I = @I + 1;
END

我設計了四種查詢方式:

  1. CTE寫法的最終改良版,採用路人乙建議的ROW_NUMBER()法挑出最終合併結果
  2. 我最早的CTE寫法,用JOIN找出最終合併結果
  3. 標準FOR XML PATH做法
  4. 將重複出現的FOR XML PATH子查詢轉成CTE,看效能是否因此提升
;WITH SupportCTE (RowNum, Name, CodeName, Engineer,Engineers) AS
(
SELECT 1, A.Name, A.CodeName, MIN(B.Engineer), 
CAST(MIN(B.Engineer) ASVARCHAR(MAX)) AS Engineers
FROM AppSystem A JOIN AppSupport B ON A.Name = B.AppName
GROUPBY A.Name, A.CodeName
UNIONALL
SELECT A.RowNum + 1, A.Name, A.CodeName, B.Engineer, 
CAST(A.Engineers + ', ' + B.Engineer ASVARCHAR(MAX)) As Supports
FROM SupportCTE A JOIN AppSupport B 
ON A.Name = B.AppName AND B.Engineer > A.Engineer 
)
 
--Trail 1
SELECT Name,CodeName,Engineers
FROM (
SELECT *,ROW_NUMBER() OVER (PARTITION BY Name ORDERBY RowNum Desc) AS Pos
FROM SupportCTE
) T
WHERE Pos = 1 
ORDERBY Name
 
--Trial 2
--SELECT A.Name, A.CodeName, A.Engineers FROM SupportCTE A
--JOIN (SELECT Name, Max(RowNum) AS MaxRowNum FROM SupportCTE GROUPBY Name) B
--ON A.Name = B.Name AND A.RowNum = B.MaxRowNum
--ORDERBY A.Name
 
GO
 
--Trail 3
SELECTDISTINCT d.Name ,d.CodeName,
(
SELECT c.Engineer +','FROM
 (
SELECT a.Name, a.CodeName, b.Engineer 
FROM AppSystem a LEFTJOIN AppSupport b ON a.Name = b.AppName
  ) c
WHERE c.Name = d.Name FOR XML PATH('')
) AS Engineer
FROM
(
SELECT a.Name, a.CodeName, b.Engineer 
FROM AppSystem a LEFTJOIN AppSupport b ON a.Name = b.AppName
) d
ORDERBY Name
GO
 
--Trail 4
;WITH SupportCTE (Name, CodeName, Engineer) AS
(
SELECT a.Name, a.CodeName, b.Engineer 
FROM AppSystem a LEFTJOIN AppSupport b ON a.Name = b.AppName
)
SELECTDISTINCT d.Name ,d.CodeName,
(
SELECT c.Engineer + ','FROM SupportCTE c
WHERE c.Name = d.Name FOR XML PATH('')
) AS Engineer
FROM SupportCTE d
ORDERBY Name

執行結果如下。以Total execution time欄位做為比較標的,CTE+ROW_NUMBER()法 2.96秒、CTE+JOIN找結果法7.14秒,而FOR XML PATH法只花了0.59秒,FOR XML PATH+CTE法0.64秒(沒有變快)。

CTE慢了近5倍,顛覆我原本的想像…

由執行計劃推敲效能差異來源。FOR XML PATH法的執行計劃很單純,絕大部分的時間用在JOIN查詢。

CTE做法的執行計劃相對複雜許多,而高達84%消耗在Sort上!由Sort出現位置推測約略發生在用ROW_NUMBER()找最終合併結果時,猜想問題出在CTE結果暫存於記憶體,無法仰賴Index等機制加速,資料量一變大即成瓶頸。

若要以此推論CTE效能不好也有失公允,這次合併欄位的CTE動用了MIN、GROUP BY、ROW_NUMBER(),相對FOR XML PATH笨重複雜許多,而CTE的遞迴邏輯有其強大難以被取代的場合,隨便換個例子就不是FOR XML PATH可以搞定的。只能說,在這個案例用CTE加遞迴有殺雞用牛刀之嫌。如果只是在SQL要將一對多欄位合併成單一字串,FOR XML PATH方法有較好的效能表現。

最後補充一點,前述的FOR XML PATH寫法有個小瑕疵,Engineer欄位結尾會多一個逗號。

這個小問題在C#端可以用TrimEnd(',')輕鬆搞定,若要在T-SQL做掉稍微囉嗦一點,但也不難,例如以下範例。(感謝網友艾里克斯補充STUFF解法)

SELECT Name, CodeName, 
CASEWHEN LEN(Engineer) > 1 THENSUBSTRING(Engineer, 1, LEN(Engineer)-1) ELSE Engineer ENDAS Engineer
FROM (
SELECTDISTINCT d.Name ,d.CodeName,
    STUFF(
       (
SELECT',' + c.Engineer FROM
         (
SELECT a.Name, a.CodeName, b.Engineer 
FROM AppSystem a LEFTJOIN AppSupport b ON a.Name = b.AppName
          ) c
WHERE c.Name = d.Name FOR XML PATH('')
        ), 1, 1, ''
    ) AS Engineer
FROM
    (
SELECT a.Name, a.CodeName, b.Engineer 
FROM AppSystem a LEFTJOIN AppSupport b ON a.Name = b.AppName
    ) d
) X
ORDERBY Name

在SharePoint網站檢視RDLC報表發生錯誤

$
0
0

冷門茶包一枚。在SharePoint網站安裝網站應用程式,在其中使用ReportViewer檢視RDLC報表,發生以下錯誤:

The type 'Microsoft.SharePoint.Portal.Analytics.UI.ReportViewerMessages, Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' does not implement IReportViewerMessages

SharePoint大幅更動預設網站web.config設定,導致掛在同一網站的網站應用程式出現各種奇幻現象,向來惡名眧彰,而錯誤訊息出現SharePoint字樣更是鐵證如山。

以錯誤訊息爬文查到將以下appSetting設定註解掉的做法:

<!–  <add key="ReportViewerMessages" value="Microsoft.SharePoint.Portal.Analytics…… /> –>

不過,想當然爾,問題網站的web.config並沒有這個設定可以註解,猜想是從根網站繼承來的。不想去更動根網站的SharePoint的設定,試了以下這招,appSettings既然可以<add key="…">,自然也可以<remove key="…">,在問題網站的appSettings加入以下設定:

<remove key="ReportViewerMessages" />

問題排除!

【茶包射手日記】IIS不接受TCP連線

$
0
0

公司有一台主機連續兩天出現異象。剛上班的尖峰時間出現IIS網站無回應,使用遠端桌面登入觀察到桌面反應遲緩,但幾個系統服務與網路磁碟機功能正常,IISRESET後問題仍未消除。進一步用 telnet 127.0.0.1 80 測試,得到的結果是TCP連線一建立就被切斷,主機上有另一個走8080 Port的PHP網站,telnet 127.0.0.1 8080 也是一接通馬上斷線,感覺問題在比IIS更底層的程序。時處尖峰不便重開機,而沒回應的網站有替代主機可用,就先按兵不動,約莫過了半小時,IIS自己醒來又一切如常。

不料,同樣的狀況隔天再上演一次,一樣出現 telnet 127.0.0.1 80 跟 8080 一連上就斷線,等了一個小時狀況仍未恢復,感覺狀況不太妙,呼叫同事進場協助。

同事依經驗砍了 Task Manager 中的mssearch程序(Sharepoint的檢索功能),此時發現有個 DW20.exe 執行中,涉嫌重大。

DW20(Dr.Watson)是 Windows 2003/XP 的錯誤報告程序,負責在程式發生嚴重當機時跳出來蒐集資訊(例如:錯誤代碼、呼叫堆疊、當時的記憶體內容等),差不多像是法醫寫驗屍報告。傾印記憶體等程序會耗用大量CPU及磁碟資源,有可能就是導致IIS 異常、甚至 TCP 連線無法建立的元凶。

IIS 與 PHP 在砍除 DW20 後,隨即恢復正常。因此學到一項經驗:DW20或Windows Error Reporting(Vista之後WER取代Dr.Watson)的錯誤報告產生程序可能耗用大量系統資源導致系統運作異常,故偵錯時可優先識別工作管理員中是否有DW20.exe或WerFault.exe出沒,排除記憶體傾印程序導致異常的狀況,特此筆記。

後記:今天剛好在我的PC也遇到一起Windows桌面凍結無反應的狀況,工作管理員顯示CPU不高,但發現WerFault.exe的蹤跡,凍結狀態在WerFault.exe結束後消失,相似案例+1。

2016台北渣打馬拉松

$
0
0

攝氏4度。

曾以為這是跑東京馬或京都馬才有幸體驗的跑馬溫度,今天我們終於能驕傲地說:台灣,做到了!(補聲暗)

報名渣打馬原本期望靠著路平天冷好催速,振作起來挑戰PB。天曉得遇上44年難得一見的霸王級寒流(專業名詞是負北極振盪),全台處處雪花飄,台北市不只陽明山,連木柵附近的猴山岳、阿柔洋、二格山甚至指南宮都下起來,面對此生難逢的寒流,把握氣溫最低的清晨時分,在戶外奔跑徹底體驗4度到底有多冷,好盡興呀~(淚)

4:40趕到總統前,雨勢甚大,濕冷又有風,一整個寒徹骨。實在不想脫掉保暖的大外套,但因起終點不同,5:00前就結束寄物發車,只好摸摸鼻子,脫掉外套早早寄物,穿著排汗衫、短褲,靠著薄風衣在寒風發抖至少半小時。關鍵時刻,跑者們發揮動物本能,不約而同在原地跳抗寒,起跑區數千人原地跳真是壯觀,活像緒勢待發的僵屍大軍來著。XD

5:30準時鳴槍起跑,好久沒參加數千人的大比賽,跑了兩公里人潮才漸漸散開。隨著四肢活動身體漸暖,氣溫低不再是困擾,如果天氣乾爽,這種氣溫跑來倒也舒適,但下雨壞了好事。雨勢大到地面積水,落腳得小心翼翼避開水坑,耗神又無法盡興跨步,但如此仍難逃鞋襪濕透的下場,一路都在提心吊膽會起水泡。而天冷四肢僵硬跑不順,Pace只在540到六分速間游移。

這場比賽,我最不爭氣的器官,不是腿腳膝蓋,不是心臟,而是膀胱!明明寄物後才上過廁所,在起跑區就開始尿急,搞得我一出拱門就沿途張望,眼睛除了要閃積水還要找廁所,好忙。要命的是,一直到5公里水站才看到流動廁所(曾有衝動要不要靠邊就地解放,想想還是當個文明人吧),乖乖排隊,花兩分鐘解決人生大事後,總算一身輕鬆,繼續上路。依照過往經驗,擔心水分流失傷身,依循慣例每個水站兩杯的頻率補充水分,但顯然低溫環境的身體代謝效率大不同,沒將天冷因素納入考量的下場是-整場比賽我上了五次廁所!五次!還創下五公里不到上兩次廁所的記錄,為我的馬拉松生涯再創新頁… orz

跑到16K處,對向跑完30K的第一名選手已折返,再往前行陸續看到的清一色是黑人選手,前十名幾乎被包辦(事後看報導第八名起才是台灣的何盡文)。令我印象深刻的是第三名老兄,沒戴帽子,倒是戴著旅館的拋棄式浴帽,大概是不用就抛想省累贅吧!頭頂透明香菇帽飛奔而來,專業跑姿搭配詼諧造型,害我嘴角忍不住上揚。

決定戴手套是對的,進水站要脫手套拿餅乾剝香蕉頗麻煩,還一度跑出水站才發現手套不見回頭撿,幾度試著脫下來放口袋,都在十分鐘內默默戴回去。雙手位處四肢末梢運動量又低,不太經得起寒風吹,全身要僵硬發痛的地方多的是,手部還是乖乖保暖別多找麻煩。

前30K勉強守住3小時,但天冷腳泡水,姿勢僵硬怎麼都跑不順,酸痛逐漸上身,30K後開始掉速,一路滑到630。估計破PB無望後便自暴自棄起來,陷入跑跑走走的墮落模式,原本捨不得停水站(卻花了好多時間上廁所 orz),不敢停下拍照,破PB夢碎後解禁,盡情吃香蕉亂拍照。

最後4K被430配速列車碾過,激起小小危機感,拖著沈重僵硬的大腿連滾帶爬,勉強保住430。

領回衣保袋趕緊換上乾衣服加長褲,停下來不動一吹風就起雞皮疙瘩,還是早早回家好。踼了約一公里馬路去搭接駁車,途中身旁大哥唸了兩句「搭車的路也太遠了,比跑馬還累」我就說「這是大會用心良苦,讓我們排乳酸呢」,兩個人就這麼開了話匣子聊起來。原來大哥早在1999年就跑完百馬,是跑了150馬的老前輩,半年前脊椎開刀,本次是靜養數月後的復出賽,無奈成績一落千丈,跑得很差,只跑出34X… 聽得我下巴都快掉下來。我使盡吃奶力氣也摸不到SUB 4,在高手眼中34X已上不了檯面,這是一種「打進SBL」對上「國小操場拍皮球」的概念~臨下車前,前輩分享了他的LSD跑法:從大佳河濱出發,向東跑到南港,走南深路越過山頭銜接木柵河濱道,一路往北跑到社子島再接回大佳,總長約70公里,八小時跑完,是很好很盡興的耐力訓練…

拾起掉到地上的下巴,懷著無限崇敬告別前輩,結束今天難忘的冰馬勇戰記。

GUID Primary Key資料庫避雷守則

$
0
0

【聲明】該不該用GUID當Primary Key是可以讓開發人員大戰三百回合的好題材,由標題可知我屬於GUID陣營,這篇文章不打算花時間論證該不該用GUID PK,假設讀者已接受使用GUID當PK,只聚焦如何避免GUID PK導致資料庫效能悲劇。

故事源起MVP James最近寫的幾篇GUID鬼故事(包含一起寫入資料3秒變40秒的案例,也實證了GUID作為叢集索引造成索引破裂現象,值得一看),讓我有所警覺,身為一個偏好GUID Primary Key的開發者,有必要正視這個問題,避免掉進資料庫效能陷阱。

簡單歸納我愛用GUID當Primary Key的理由:

  1. 不仰賴資料庫就能取得唯一鍵值
    建立新資料的當下就能決定PK,放心地使用它衍生相關應用。即使程式執行當下資料庫還沒建好、三個月後才會寫入資料庫,或是一年後來自十個資料庫的資料要合併成一個資料表,都不必擔心鍵值重複的問題。(註:傳說GUID仍有重複的可能,但機率極低,在此忽略)
  2. 無法被猜測,用於參數時內含安全防護效果
    GetTradeData.aspx?no=617ad98a-c010-4cd2-bc07-9a64d907154f 比 GetTradeData.aspx?no=6386 安全,可避免惡意人士修改參數嘗試存取非授權範圍資料的風險。

但使用GUID當Key也有缺點,例如:

  1. 不利於人工查詢或偵錯
    例如:GetTradeData.aspx?no=617ad98a-c010-4cd2-bc07-9a64d907154f,SELECT * FROM MyTable WHERE Id = '617ad98a-c010-4cd2-bc07-9a64d907154f',大多得靠複製貼上處理參數,難以手工輸入。
  2. 增加儲存空間
    每個GUID有16 Byte,相較於整數4 Byte,多耗用4倍的儲存空間。
  3. 衝擊資料庫效能
    GUID的非連續特性,易導致索引破碎(Index Fragement),降低系統效能。

依我的看法,人工查詢不便頂多費工不會致命,12 Byte vs 4 Byte在資料筆數很龐大時才有顯著影響,而第3點可能嚴重影響效能,輕忽不得。

由James提供的案例可知,在資料庫使用GUID PK,稍有不慎便會發生悲劇。所以該好好思索,如果你打算使用GUID PK,要怎麼樣才能避免掉下效能陷阱?

首先,鬼故事裡有一個共同關鍵-「GUID PK被設成叢集索引(Clustered Index)」。我們都知道,資料庫的索引有兩種,Clustered Index與Nonclustered Index,依據MSDN,二者比較如下:

  • Clustered Index
    叢集索引決定資料表儲存資料的實際順序,每個資料表最多只能有一個叢集索引,而叢集索引不需額外空間儲存鍵值。有叢集索引的資料表稱為叢集資料表(Clustered Table)。找到索引鍵時一併找得到資料列,查詢效能比非叢集索引好,用在最常用的鍵值(例如:Primary Key)可以產生最大效益。若資料表沒設叢集索引,資料會以沒有固定排序的方式儲存,這種儲存結構稱之為Heap。
  • Nonclustered Index
    非叢集索引需在實際資料列之外另行建立資料結構,每筆索引除了索引包含欄位外,還需要一個指標(Pointer)指向資料位置,若資料表有叢集索引,指標指向叢集索引的鍵值,若資料表使用Heap結構,指標直接指向資料列本身。
    非叢集索引可以附加非鍵值欄位以突破16欄900Bytes的索引鍵值上限,完全涵蓋查詢條件提升效能。(參考:Create Indexes with Included Columns.)

PK使用頻率很高,設成叢集索引對效能最有利,故慣例上會設成叢集索引以提升效能。然而,因為GUID具有不連續的隨機性,即使循序寫入資料,常常後寫的資料GUID排序較前,依叢集索引特性,實體儲存位置應擺在前段,造成每次寫入資料都需挪動調整既有資料造成索引破碎,拖累寫入與查詢效能。

由此看來,將GUID PK設為叢集索引的缺點蓋過優點。轉念一想,沒人規定PK一定要設成叢集索引呀!只要將PK改為非叢集索引就能避開GUID導致索引破碎的危機。當然,非叢集索引的效能不如叢集索引,但減損幅度不致太嚴重,相較於其降低的風險是划算的。

但這衍生另一個議題,PK不設成叢集索引,資料表就只有非叢集索引可行嗎?前面提到,沒有叢集索引的資料表稱為Heap Table。

依據MSDN,Heap Table只適合資料極少(例如數十筆)的場合,即使不設任何Index,Table Scan也很有效率,或者某些資料架構師會巧妙利用非叢集索引配合Row Identifier (RID,由File Number, Data Page Number, Slot on The Page組成,長度不長),達到比叢集索引還好的效率。但絕大部分的情況下,設定叢集索引有好處:

  1. 循序讀取一段資料時,叢集索引可以節省排序動件
  2. GROUP BY時,分群前必須先排序,叢集索引可以省去排序作業
  3. 記得避免資料多又無非叢集索引可用的狀況,如此永遠只能Table Scan,包準慢死你

而我覺得缺乏叢集索引的最致命點是-Heap Table也會產生破碎現象,一旦出現,依MSDN的建議是建個Clustered Index再砍掉,網路上提到的其他做法還有把資料先搬出來再重新塞回去、匯出到新資料表再更名,不管哪一種做法,聽起來都好沒效率,好可怕。[2016-01-30更新]SQL 2008起,增加了ALTER TABLE … REBUILD指令,背後使用建立Clutstered Index再刪除做法,但省去建增刪過程連動更新非叢集索引的浪費,明顯提升效率。感謝James補充。

至此,我得到兩點結論:1.將GUID PK設成非叢集索引利大於弊 2.資料表欠缺叢集索引就會形成Heap Table,弊大於利。所以,最好的折衷方案就是「GUID PK設成非叢集索引,並另外增設叢集索引」,而這個額外的叢集索引,自動跳號整數會是首選。如此我們降低GUID PK導致索引破碎的風險,自動跳號叢集索引避免新增資料造成索引破碎,而叢集索引讓資料表可以透過索引重建重組改善破碎狀況,同時避開索引破碎及Heap Table陷阱。

綜合以上,來段CREATE SCRIPT示意:

CREATETABLE [dbo].[MiniFlow](
     [SeqNo] [int] IDENTITY(1,1) NOTNULL,
     [FlowId] [uniqueidentifier] NOTNULL,
     [FormCode] [varchar](4) NOTNULL,
     [FormNo] [varchar](16) NOTNULL,
     [Subject] [nvarchar](256) NOTNULL,
CONSTRAINT [PK_MiniFlow] PRIMARYKEYNONCLUSTERED
(
     [FlowId] ASC
)
GO
CREATECLUSTEREDINDEX [IX_MiniFlow] ON [dbo].[MiniFlow]
(
     [SeqNo] ASC
)
GO

有幾個重點:

  1. PK外外增設SeqNo INT,以IDENTITY(1,1)設定自動跳號
  2. FlowId為GUID是MiniFlow資料表的Primary Key,但設定時加上NONCLUSTERED指定為非叢集索引
  3. 利用CREATE CLUSTERED INDEX將SeqNo建為叢集索引

依我的看法,這是可以克服GUID PK負面效應的最佳設計,在Stackoverflow上也獲得印證,大家如有不同看法,歡迎提出討論。手邊一些使用GUID PK的現有系統效能都還OK,不打算積極調整,但日後開發使用GUID PK新系統,我應該會採用這種設計方式。

最後提一下NEWSEQUENTIALID,有不少人建議用它取代GUID避免索引破碎,但為NEWSEQUENTIALID只能用於資料庫INSERT時自動產生,又可以被預測,並不符合我期待GUID PK應提供的隨時可取及防猜保密要求,我認為只適合用在處理跨資料庫合併用的鍵值。


【茶包射手日記】VS2015卡在專案初始化無回應

$
0
0

用Visual Studio 2015開啟最近每天在寫的解決方案,突然卡在「解決方案呈現載入中(loading…),多個專案處於初始化中(initializing…)」狀態,Visual Studio 2015 UI也凍結無回應,只能從工作管理員強行砍掉devenv.exe重新啟動,再次開啟問題依舊。為了對照,用VS2015改載入其他專案,一切正常,縮小範圍,確認問題只出在特定解決方案。改用VS2015安全模式(不載入外掛套件)開啟該解決方案,問題依舊。進一步測試,VS2013開啟該解決方案是正常的,至此,問題再縮小到「VS2015開啟特定解決方案時卡在專案初始化階段無回應」。試了Windows重開機,沒出現奇蹟。

由於同一專案VS2015有問題、VS2013 OK,推論可能與VS設定有關係,試著刪除 .vs\AfaV4\v14\.suo檔案(VS2013時代,解決方案所屬VS設定儲存在AfaV4.v12.suo,VS2015起則改放在.vs資料夾下),問題消失。

心得:若VS2015/VS2013只在開啟特定解決方案發生異常,可試著刪除該解決方案的.suo設定看能否排除問題。(會遺失一些設定,例如中斷點,但總好過專案開不起來)

漫談尾牙抽獎程式的公正性

$
0
0

上週看到以程序員生活為題材的漫畫作者西喬的創作:「年会上的程序员们……」,改編自一個「CTO覺得抽獎程式有點問題,程式作者被迫在旺年會場接受上千人Code Review」的奇妙真實故事,令人不禁莞爾。

很巧地,看到漫畫的前一天剛好才參加完資訊部門尾牙,而永遠的重頭戲-摸彩,自然是跑程式抽獎。(每天寫程式的人做不出電腦抽獎,傳出去像話嗎?)隨著抽獎號碼一一抽出,眾人希望落空,台下沒中獎的程式設計師們開始鼓噪議論:「XX號為什麼出現這麼多次?」「靠!程式絕對有鬼」「Open the source, Luke!」「程式沒做Code Review,肯定有Bug」… 話是這麼說,但大家心知肚明,程式被動手腳或真出錯的機率甚微,都是或然率使然,純粹是拿Coding哏瞎起閧,沒中獎也尋開心唄。

不過,我很無聊地思考,抽獎程式是否可以做到讓程式設計師無法挑剔呢?仔細分析:在大家眼裡,傳統的抽籤、摸彩球還是較有公信力,即使沒做到樂透開獎等級:公證律師監督,準備多組彩球、抽獎機隨機採用,大家還是習慣眼見為憑,寧可相信人手在箱子裡攪和後抽出的號碼,而不是程式不知依據什麼冒出的結果。

針對這點疑慮,我覺得唯一的解決辦法是「讓程式碼公開,演算結果能被反覆驗證」。抽獎程式的核心在於亂數產生器,依前述要求,讓亂數元件抓執行時間當亂數種子的做法是不可行的,每次執行的結果永遠不同,那你如何證明當天當時按鈕就該跑出這個結果?所以,抽獎程式要經得起考驗一定要能反覆驗證,故「以其他具公信力方式決定亂數種子,再依據其產生亂數決定抽獎結果」是較理想做法。至於亂數種子怎麼決定?就抽籤、摸彩球囉… XD

看到這裡,可能會有人想翻桌了,繞了一圈又要抽籤,為什麼不從頭到尾抽籤就好?依我的看法,如果可行,用抽籤、摸彩球取代程式更不易起爭議,因為它原理簡單,不需要專業背景就能懂,如果有人對開獎結果不服氣,就把箱子倒出來,找出他的籤條打他臉,爭議立解。而程式演算法較難理解,把電腦拆開也沒法當場驗票,被人抹黑也只能往肚吞,實在悲情。但抽籤並非萬能,很多情況只有電腦抽獎才能勝任,例如:十萬名員工的抽獎得做十萬枝籤(又不是十萬枝箭可以開草船出去借),包準工作人員忙上好幾天;要當場抽出三百名普獎,從上烏魚子海蜇皮拼盤就要開始抽,抽到水果西米露登場都沒還抽完。抽籤公平,電腦選號有效率,那就「用抽籤結果衍生一組任意數量的隨機結果」,魚與熊掌兼得。

最後,把焦點拉回「如何選一個公平且夠隨機的亂數種子」。不同語言、平台的亂數元件規格不同,以C#為例,Random型別支援亂數種子,參數型別為Int32,故最多產生20億種不同結果,可滿足絕大多數的應用。(註:JavaScript內建的Math.random()不支援亂數種子,但有現成程式庫救援,可比照辦理)

20億種變化夠大,但勞煩長官一連抽十個數字,過程偏長且會冷場。我有個好點子,不妨摻雜一些「大家公認無法人為操控又會隨機跳動的數字」,例如:前期樂透開獎號碼、氣象預報氣溫、當天收盤的道瓊工業/那斯達克/台股指數、日圓/美元/歐元匯率… 做成一堆數字牌(注意:亂數種子取用規則要事前公佈),讓長官從中抽出幾個再洗牌重排順序,就可以得到夠多位數的亂數種子。如果嫌麻煩,買三副撲克牌只留1-9給長官抽出10張再洗牌排序比較簡單(如果長官不介意演一下賭神的話)。總之,方法很多,有辦法湊出沒人質疑的數字即可,數字位數愈多隨機性愈高但愈費工且可能會冷場或讓長官不悅不爽加碼(這個很嚴重),請大會自行拿捏。

還有一種有效率的替代做法,亂數種子不用數字,改取一段文字再用MD5或SHA1雜湊轉成數字,文字到處可得(報紙標題、人名、翻字典),中文字元數目眾多,常用的就有數千個,短短幾個字經過雜湊演算就能產生足夠的變化性。缺點是由字元產生雜湊數字的概念較不直覺,抽獎對象要有相關背景才易理解,需考慮參與者的接受度。

有了亂數種子,就能得到可重複驗算的一連串隨機數字,抽獎候選清單多半儲存於陣列,最直覺的寫法是取 0 到 Array.Length-1的亂數挑選中獎者,將其自陣列移出再用同樣邏輯抽下一個。我個人則偏好另一種做法,為陣列的每一個元素產生一個亂數,再依此亂數由小到大排序,演算法較簡單,一口氣取出數百上千名抽獎者都不是問題。

借用先前500萬人次抽獎的程式當範例,程式差不是長這様,其中的12345就是亂數種子,不用的亂數種子取出的得獎者不同,且可反覆驗證:

class Program
    {
staticvoid Main(string[] args)
        {
string raw = @"1.Jeffrey
2.Darkthread
3.球證
4.旁證
5.主辦
6.協辦
7.全都是我的人";
            List<string> candidates = new List<string>(raw.Split('\n'));
            Random rnd = new Random(12345);
            Console.Write(candidates.OrderBy(o => rnd.Next()).First());
            Console.Read();
        }
    }

順手也寫了一個JavaScript版:Live Demo

<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<metaname="viewport"content="width=device-width">
<title>尾牙抽獎範例</title>
</head>
<body>
亂數種子=<inputvalue="12345"/><inputtype="button"value="抽獎"/>
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.2/seedrandom.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.4.js"></script>
<script>
var candidates = 
"1.Jeffrey,2.Darkthread,3.球證,4.旁證,5.主辦,6.協辦,7.全都是我的人".split(',');
    $(":button").click(function() {
      Math.seedrandom($(":text").val());
var list = $.map(candidates, function(n) {
return { name: n, rand: Math.random() }
                 });
      list.sort(function(a, b) { return a.rand - b.rand; });
      alert("得獎的是:" + list[0].name);
    });
</script>
</body>
</html>

抽獎程式碼先公開,預先公告亂數種子取碼規則,只待當天填入亂數種子就能抽出任意數量的得獎者,抽獎結果可以反覆驗證,在我心中,這就稱得上是公平公正公開的抽獎程式囉~

小技巧-自動刪除App_Data過期暫存檔

$
0
0

寫網站時有時需要產生暫存檔,過去我慣用的做法是透過System.IO.Path.GetTempPath(),將暫存檔寫入Windows系統暫存資料夾,如意算盤是暫存資料夾本來就是放暫存檔的地方,而Windows有機制可在磁碟空間不足時刪除暫存檔釋放空間,如果檔案沒有機密或敏感性,放在暫存資料夾基本上可以Fire and Forget,用過就放著不管,很省事。

後來幾次部署經驗告訴我,「ASP.NET程式不一定有寫入暫存資料夾的權限」(登楞!)。針對Temp資料夾開放ASP.NET寫入權限是一種解法,但每個ASP.NET應用程式的執行身分不同,搞下來Temp資料夾需維護一堆權限有點複雜。相形之下,App_Data隸屬各ASP.NET應用程式,可部署時一併設定權限統一管理,相對簡便。(註:適合放暫存檔是因為App_Data具有隱身特性,請不要將暫存檔放在一般網站目錄下,以免被使用者不當存取)

但暫存檔放App_Data就不像系統暫存資料夾可以靠Windows機制回收空間,得自行清理過期暫存檔。最直覺做法是寫個排程每天定時刪檔,但最近我想出另一個更省事的做法-取得暫存目錄時自動清理過期檔案。程式範例如下,分享兼請大家Code Review:P

//記憶上次清理執行日期
staticstring lastAttTempCleanDate = null;
//非同步鎖定
staticobject cleanLock = new Object();
 
staticstring GetTempPath()
    {
//使用App_Data暫存檔案,避免ASPX沒有寫入系統TEMP權限
string tempPath = Path.Combine(
            HttpContext.Current.Server.MapPath("~/App_Data"), "UploadAtts");
//若目錄不存在,建立之
        Directory.CreateDirectory(tempPath);
//每次啟動網站或每天清理一次過期Cache
string todayDate = DateTime.Today.ToString("yyyyMMdd");
//防止多執行緒同時觸發
lock (cleanLock)
        {
//比對上次執行日期,原則上每天只執行一次
if (todayDate != lastAttTempCleanDate)
            {
//先儲存執行日期記錄,避免重複執行
                lastAttTempCleanDate = todayDate;
//另開Thread執行刪除,以免卡住目前的暫存作業
                Task.Factory.StartNew(() =>
                {
//決定逾期判斷基準
                    DateTime expireTime = DateTime.Today;
foreach (var file in Directory.GetFiles(tempPath))
                    {
//若檔案建立時間早於今天,刪除之
if (File.GetCreationTime(file).CompareTo(expireTime) == -1)
                        {
                            File.Delete(file);
                        }
                    }
                });
            }
        }
return tempPath;
    }

小技巧-為text-overflow: ellipsis增加完整文字顯示

$
0
0

CSS 的 text-overflow: ellipsis 刪節號效果可讓長度不一的文字等寬顯示,遇到版面空間有限又必須整齊排列時很好用,但套用刪節號樣式後看不到完整文字,尤其遇上文字前半截相同時更是難以區別,是一大困擾。為此,我的慣用解法是為套用 ellipsis 的文字元素加上 title Attribute 存入完整文字,將滑鼠移到文字上停留就能看到原始文字,問題迎刃而解。

每次套用 ellipsis 還要額外加 title 有點囉嗦,我寫了一小段  jQuery 讓程序自動化,範例如下提供大家參考:Live Demo

<!DOCTYPEhtml>
<html>
<head>
 
<metacharset="utf-8">
<metaname="viewport"content="width=device-width">
<title>自動為text-overflow ellipsis加上title顯示</title>
<style>
    .a-ellipsis {
      width: 100%;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    div { padding: 3px; }
</style>
</head>
<body>
<divstyle="width: 120px">
<divclass="a-ellipsis">如何讓你遇見我</div>
<divclass="a-ellipsis">在我最美麗的時刻 為這</div>
<divclass="a-ellipsis">我已在佛前求了五百年</div>
<divclass="a-ellipsis">求他讓我們結一段塵緣</div>
</div>
<scriptsrc="https://code.jquery.com/jquery-2.1.4.js"></script>
<script>
    $(function() {
      $("body").on("mouseenter", ".a-ellipsis", function() {
if (!this.title) this.title = $(this).text();
      });
    });
</script>
</body>
</html>

效果展示

LINQ寫法:類SQL查詢語法 vs 方法串接

$
0
0

前陣子跟同事討論到LINQ查詢寫法。

記得在LINQ剛推出時以「可以在C#裡寫SQL語法查資料」為號召,範例裡常看到這種長得有點像SQL語法的LINQ查詢運算式(LINQ Query Expression,我習慣叫它「類SQL查詢語法」):

    IEnumerable<int> scoreQuery = //query variable
        from score in scores //required
where score > 80 // optional
        orderby score descending // optional
        select score; //must end with select or group

不過,我在熟悉LINQ後(甚至演變成「少了LINQ幾乎不會寫程式」),卻很少再用這種寫法,大都是用LINQ方法配上Lambda運算式:

    IEnumerable<int> scoreQuery = //query variable
        scores //required
        .Where(o => o.score > 80) // optional
        .OrderByDescending(o => o.score) // optional
        .Select(o => o.score); //must end with select or group

那一種寫法比較好?好問題,甚至我也想不起來自己選擇第二種寫法的理由。既然聊到了,順手對二者做了粗淺的分析比較。

首先,有一個重要觀念:兩種寫法編譯出來的結果是相同的!

用個範例驗證:

class Run
        {
publicint No;
publicstring Name;
public TimeSpan Time;
publicbyte Road;
public Run(int no, string name, TimeSpan time, byte road)
            {
                No = no;
                Name = name;
                Time = time;
                Road = road;
            }
        }
class RoadType
        {
publicbyte Id;
publicstring Name;
        }
 
static Run[] Runs = new Run[]
        {
new Run(1, "櫻花馬", new TimeSpan(5,50,16), 1),
new Run(2, "櫻木花道馬", new TimeSpan(5,27,50), 1),
new Run(3, "國道馬", new TimeSpan(4,15,38), 2),
new Run(4, "鳳梨馬", new TimeSpan(5,26,03), 1),
new Run(5, "三重馬", new TimeSpan(4,47,32), 2),
new Run(6, "台北星光馬", new TimeSpan(5,30,25), 2),
new Run(7, "海山馬", new TimeSpan(5,42,18), 2),
new Run(8, "石碇馬", new TimeSpan(6,07,54), 1),
new Run(9, "五分山馬", new TimeSpan(5,13,32), 1),
new Run(10, "土地公馬", new TimeSpan(5,26,51), 1),
new Run(11, "觀音山馬", new TimeSpan(5,47,07), 1)
        };
 
static RoadType[] RoadTypes = new RoadType[]
        {
new RoadType() { Id=1, Name="山路" },
new RoadType() { Id=2, Name="平地" }
        };
 
staticvoid Main(string[] args)
        {
            var queryBySql =
                from r in Runs
where r.Time.Hours < 5
                orderby r.Time
                select new { r.Name, r.Time };
 
            var r1 = queryBySql.Skip(1).Single();
 
            var queryByLambda =
                Runs
                .Where(o => o.Time.Hours < 5)
                .OrderBy(o => o.Time)
                .Select(o => new { o.Name, o.Time });
 
            var r2 = queryByLambda.Skip(1).Single();
 
            Console.WriteLine("Test {0} {1}", r1.Name, r2.Name);
            Console.Read();
        }

程式裡我分別用「類SQL查詢」及「方法串接」找出我去年馬拉松成績跑出Sub5的第二筆,使用JustDecompile反組譯可發現兩段程式碼除了變數名稱有別,程式結構一模一樣。(以類SQL查詢呈現是JustDecompile的偏好)

由此可知,不管你使用哪一種寫法,都不會產生效能或結果上的差異,大家可依自己的偏好選擇。

那麼,我為什麼偏好方法串接呢?認真想過,整理出我選擇方法串接的理由:

  1. 符合程式運作原理,複雜應用時較易思考

    類SQL查詢提供熟悉SQL語法開發者另一種較親切的表達方式,背後運作其實是採方法串接的邏輯,先.Where(),再執行.OrderBy(),最後.Select(),每個方法都傳回IEnumerable<T>故可前後串接。在一些較複雜的動態邏輯串接情境,方法串接寫法就顯得較直覺,容易思考,例如動態依條件加上Where篩選:
                var q = Runs.ToList();
    if (filterSub5)
                    q = q.Where(o => o.Time.Hours < 5).ToList();
    if (filterMountain)
                    q = q.Where(o => o.Road == 1).ToList();

  2. LINQ查詢未必與DB有關,對陣列、集合下SQL不如呼叫方法來得直覺

    平日寫專案,LINQ我用得極多,但超過90%是用在一般的IEnumerable<T>與資料庫沒有任何關係。在這種情況下,不太會有動機刻意寫SQL,套用Where()、Select()得到處理過的IEnumerable<T>再繼續處理比較直覺。
  3. 有些邏輯只能靠LINQ方法達成

    類SQL查詢最終還是得串接如 Count()、Any()、SingleOrDefault() 等方法才能得到結果。使用方法串接可以一氣喝成,用類SQL查詢簡潔度差一些,例如:
                var countSub5BySql = 
                    (from r in Runs where r.Time.Hours < 5 select r).Count();
                var countSub5ByLambda =
                    Runs.Where(o => o.Time.Hours < 5).Count();
  4. 統一做法,減少腦力負擔

    承上點,既然寫類SQL查詢免不了動用LINQ方法,乾脆只學只記一種做法,人老堪用記憶體漸少,少記點東西又避免切換做法消耗腦力,省力有效率。 (謎:你的大腦是有多不堪啦?)

但類SQL查詢有一強項可以狂電方法串接-其JOIN語法比.Join()方法簡潔直覺!如下例,差異很明顯吧?

            var queryBySql =
                from r in Runs
                join t in RoadTypes on r.Road equals t.Id
                select new { r.Name, RoadType = t.Name };
 
            var queryByLambda =
                Runs.Join(
                    RoadTypes,
                    r => r.Road,
                    t => t.Id,
                    (r, t) => new { r.Name, RoadType = t.Name });
 
            Console.WriteLine(JsonConvert.SerializeObject(queryBySql.ToArray()));
            Console.WriteLine(JsonConvert.SerializeObject(queryByLambda.ToArray()));
            Console.Read();

我一直覺得類SQL查詢語法在LINQ技術的定位,有點像VB.NET在.NET技術扮演的角色,都是藉由開發者已經熟悉的語法(SQL、VB、VBScript)增加熟悉感與親和力,引領開發者進入全新世界。而在上手後,開發者依語法簡潔性、市場主流趨勢、自己的偏好,漸漸就會自動流向不同陣營,很有趣。

親愛的.NET開發者們,大家慣用哪一種LINQ寫法呢?

讀書筆記-思考的藝術

$
0
0

前陣子跟同事聊到邏輯謬誤,同事提到德國作家的相似作品,談非受迫性的思考邏輯錯誤及行為偏誤,邏輯控對這些知識沒什麼抵抗力,趁著剛好拿到博客來Coupon就入手了,下單隔天書就送抵小七門市,效率令人驚喜。(線上書店品項完整折扣多再加上交貨神速,實體書店只剩能深入試閱及可搭訕知性正妹等優勢,這場仗註定艱辛。)

書薄不及300頁又算淺顯好消化,沒兩下就能翻完,但衡量自己不會有耐性再翻一次,又不想讀過的內容太快隨風而逝,唯有寫成筆記才能長記活用。這篇算是私人筆記,貼在部落格只為方便日後查詢引用,但有興趣的讀者可以加減看,或許也能有所得。

  • 存活者偏誤

    同一件事,成功者獲得的關注與轉述遠遠超過背後數量龐大的失敗者,讓人過分高估成功機率。
  • 游泳選手身材錯覺

    由為游泳選手身材健美推論游泳對於美化身材有神奇的效果,但事實上很多選手是因為擁有那種身材才成為游泳選手。
  • 過度自信效應

    蒲隆地共和國有多少人口?受試者可選擇任意兩個數字當範圍,但必須有把握實際答案超出這兩個數字的機率小於2%(回答到1到10億也可以),有40%的受試者答錯。人們常高估自己在預測方面的知識與能力,某些時候是為了塑造自己專業形象謀取利益。
  • 社會認同

    誤認「大家都在做的事就是對的事」。源於物種演化,在草原上看到其他同類轉身逃跑跟著跑的人類存活率高,基因就被保留下來。
  • 沈沒成本謬誤

    發現是部爛電影,但都已經買了票就只好看完,結果浪費錢還浪費時間。
    男友不斷出軌但每每跪求原諒,預期他未來仍會再犯,卻基於已投入的感情、歲月無法割捨。
    美國的越戰的「我們已經犠牲了這麼多官兵性命,若就此停戰將是錯誤決定…」
    投資出現損失時不是不能加碼,但必須基於「繼續投入會成功」的預期,而非「我們已經投入這麼多」。
    注:若身體狀況不允許,馬拉松已跑了40K也不該是硬撐下去的理由。(知道是對的,但好難做到…)
  • 互惠(基於虧欠感的補償行為)

    非營利組織寄來免費明信片,言明有無捐款都能保留。招待潛在客戶看足球賽,一個月後拿到該客戶的訂單。朋友請客送禮,習慣上要禮尚往來回請回送。
    互惠也來自基因演化,打到獵物時跟伙伴分享,運氣不好時分享伙伴的獵物不致餓死,是種風險分擔。
    注:採行互惠是融入社會的必須手段,如禮尚往來之俗是人之常情,不可能捨棄,唯必須僅防別人藉此架構陷阱。
  • 確認偏誤

    遇到砥觸既有觀念的否定證據,自動將其視為特殊案例不列入考慮,錯失修正既有觀念的機會。
    性善論與性惡論都可以找到支持事證,但主張者常忽略無法解釋的反面例證,最後兩種論述存在並行,各有擁護者。確認偏誤在宗教上尤其明顯,宗教經典裡有許多無法解釋或有反面例證的陳述被信徒無視。
    在臉書封鎖與自己意見相左的朋友,訂閱與自己信仰相同的社群或粉絲專頁,也是向確認偏誤傾斜的行為。
  • 權威偏誤

    面對權威時,我們的獨立思考不自覺婑一截。飛機副駕駛不敢質疑正駕駛的錯誤決策造成空難,無法律專業的大學校長評論食安案件審判結果被大肆報導。
  • 對比效應

    當被擺在反差很大的對照物旁邊,我們會誤判事物的好壞、美醜、大小。
    人們在買車時對於配件、買房子的裝潢花費不手軟,相對於車款房價,偏貴產生的痛覺不明顯。
    願意多走十分鐘買菜便宜一百塊,卻覺得多走十分鐘買三萬塊的筆電便宜一百塊很呆<--完全不理性,但聽起來好有道理
  • 現成偏誤

    我們會選擇一些較易取得的現成事證進行推論。例如:以R開頭的英文字多還是以R結尾的英文字多?R開頭的字比較好想。
    戲劇性原則優於量化原則,因而高估墜機車禍致死率,低估糖尿病、憂鬱症的致死率。
    近期反覆出現的字眼也會在做判斷時優先被拿來參考<--洗腦原理
  • 好轉前會先惡化

    號稱專業者遇到無法掌控狀況,主張情況惡化是轉好前的必經歷程,藉以安撫對方,若運氣好撐到谷底反彈或自己變好,就撿到「妙手回春」之名。
  • 故事偏誤

    以故事解釋事實時扭曲並簡化事實。發生橋斷汽車落河,報導描述苦主生平,訪問歷刧心情,卻未探究重點-事故發生原因。
  • 事後諸葛亮偏誤

    事後解釋分析事物成功的原因,試圖將已存在的事實羅織成一套說辭,看似合理但與實實不符。商業雜誌的企業家報導跟股市名嘴對市場趨勢的詮釋是經典,不管樓起樓塌,上漲下跌都能頭頭是道。
  • 司機的知識

    量子力學大師普朗克的司機耳濡目染,對於量子力學理論朗朗上口,有一天跟普朗克約好交換身分假冒他公開演講,表現還挺稱職,但中途有位物理學家提問,司機不慌不忙地說:「萬萬沒想到居然有人會提這麼簡單的問題,不然這様好了,就讓我的司機來回答…」
    以此典故,用以諷刺那些空有具說服力外表,雖然知識空洞,卻表演得頭頭是道的人。
    巴菲特主張每個人都該知道自己「能力的範圍」,在範圍內成為專家,不要試圖在範圍之外尋求成功。
    知之為知之,不知為不知,具有真才實學的人會很大方地承認我不知道,但司機的知識型的名嘴,什麼都講上兩句(從外太空到內子宮),永遠不會聽到他說「我不知道」。(大家可以在電視談話節目裡找樣本)
  • 控制的錯覺

    我們相信自己可以控制某些事情(事實上無能為力),聊以自慰。 例如:每次打開電視球隊就丟分數、穿紅內褲會贏錢。
  • 激勵過敏傾向

    要撲滅鼠患,鼔勵交死老鼠換獎金,結果民眾開始養老鼠賺獎金。
    人們對激勵會有反應,但反應可能變質未必符合激勵原意。
  • 均質迴歸

    在不干預的情況下,事物本來就有起有伏,人們在遭逢谷底時執行一些「儀式」,隨後剛好轉好,就判定儀式有效。有時醫生、教練、顧問並不如表面上所認為的那麼有效。
  • 公地悲劇

    公用草地免費供農民放牧,農民爭相增加在公地放牧的牛隻數目,終將導致公地牧草源耗盡。對個別農夫而言,多放一隻牛增加的利潤為1,增加1分風險由全部農民承擔,分析後傾向增牛決策。當全部的農民都做出增牛決策,註定走向悲劇。
    近代的二氧化碳排放、濫墾濫伐、太空垃圾議題都有類似特性。
    公地悲劇是亞當史密斯「看不見的手」的反面,在特定情況下市場機制可能導向崩潰。
    唯一的解決之道只有兩條:私有化或管理(針對無法私有化的東西:大氣層、海洋、衛星軌道)。
  • 結果偏誤

    數百萬隻猴子參與股市投資,每日決定做空或做多,一個月後人們發現某隻猴子的操作完全命中漲跌趨勢,被奉為股神,人們開始研究其吃香蕉的習慣,抓癢的姿勢,期望挖掘其成功訣竅。
    人們傾向用結果評斷決定,而不是根據當時的決定過程評斷決定,又稱為史學家謬誤。
    勿單以成敗論英雄,成功有可能是歪打著,失敗可能是非戰之罪。
  • 選擇的吊詭

    太多的選項讓人茫然:提供6種果醬試吃的銷售量比24種果醬試吃多十倍,太多選擇無法決定,索性不買。
    選擇太多導致用無法仔細客觀評量,反而用偏頗的決定原則(電風扇吹考卷?)
    選項太多,做完決定後易存在不確定感及不滿(這個選項在XX上不如A,在OO上不如B)
  • 討喜偏誤

    當別人讓我們有好感或表現得對我們有好感,我們愈容易向這些人買東西或伸出援手。 人正真好/同鄉三分情/恭維灌迷湯
    鏡像-學對方的動作、語調、習慣。廣告要找正妹、小孩、可愛動物當主角。「請投入你神聖的一票」「因為你值得」「我們在地人」
  • 稟賦效應

    沒吃到葡萄說葡萄酸,吃到葡萄後把它吹捧到比實際甜。同一件東西,我們擔任賣方時認定的價格高於當買方時認定的價格。
    當我們擁有某件東西,主觀上會顯著為它加值。甚至準擁有者也會中箭,拍賣時不理性加碼。
    當你該放手時,不要因為稟賦效應遲疑錯失機會。
  • 奇蹟

    十五個唱詩班成員因不同的原因遲到躲過教會爆炸,被認定是神蹟。
    AMD與Intel公司的同名員工同一天入住加州一家飯店,飯店人員將機密文件交錯人,引爆一起專利案。
    看似奇蹟的事,其出現的機率可被計算而得,並非不可能發生。
  • 團體迷思

    一群精英組成團體,因刻意迎合被信以為真的共識,最後做出愚蠢的決定。例:古巴豬儸灣事件
    團隊精神:如果大家都堅信會成功,代表我的憂慮是多餘的,異議是錯的
    註:我想起末日之戰裡提到以色列的「十聖人」理論,政府為了預視危機成立10人小組,一旦有9個人對事件有一致意見,第10個就必須持相反意見去追查以發掘危機的可能性。
  • 輕忽機率偏誤

    告知受試者稍後會有多高機率被電擊,無論機率高低100%-1%,受試者的緊張程度相近,換言之,受試者無視機率因素。
    草率地投資新創公司,無視其成功機率。因墜機事件寧可損失機票也不坐飛機,無視其發生機率渺茫。
    一支突擊隊選擇解救A村,可將村民的傷亡由5%降到2%,解決B村可將傷亡由1%降到0%,全村都會生還。大部分的人會選B,稱之為零風險偏誤。
    除了風險為零的情況外,我們很難辦別不同機率的風險程度。
  • 零風險偏誤

    俄羅斯輪盤,已知彈匣四枚子彈,付錢可以拿掉其中兩顆 vs 已知只有一顆子彈,付錢拿掉唯一的一顆。人們願意為後者付出更多代價,前者降低六分之二死亡率,後者只降低六分之一<--零風險偏差
    人們對99%-1%致命率感受到的恐懼接近,只有零風險可以治好莫名的恐懼,因而不理性付出過高代價。(註:這可以解釋大家在食安議題上的過度反應)
  • 稀少性謬誤

    小孩子去搶一堆彈珠裡唯一的藍色彈珠;大人爭向申請限量的Gmail試用申請。物以稀為貴,餅乾愈少愈可口,跳樓大拍賣最後一天。
    抗拒(Reactance)-得不到的最美。註:白玫瑰與紅玫瑰?
  • 忽視基本比率

    李先生是一名載眼鏡的瘦高中年人,喜歡聽莫扎特。A)李先生是一位計程車司機 B)李先生是一位住在台東的中文系教授
    大部人選B,雖然不限地域計程車司機的數量遠大於台東中文系教授,符合事實的機率高出許多,但精確描述誤導了我們,讓我們不能冷眼面對統計事實。
    醫生的訓練,頭痛可能是感冒也可能是腦瘤,但前者機率高很多,要優先朝感冒方向診斷。如果你在俄懷明州聽到馬蹄聲,又彷彿看到黑白條紋,相信我,大部分的情況下那東西就是一匹馬。
  • 賭徒謬誤

    參見邏輯謬誤
  • 錨定

    錨定:對於未知答案的數字,先找出熟知的事物當參考點,再由該點推算正確答案。例如:101的高度,已知101層,用一層4米推算,推測高度至少400米以上。
    當我們無所憑藉時,會用不相干的數字錨定,例如:轉輪盤後猜聯合國會員國數字,轉到大數字的人猜的數字偏大。
    老師打成績時,會被學生過去的成績錨定;商品貼上原價標籤影響顧客對商品價值的評判。
  • 歸納法

    人們傾向由個別觀察推得普遍有效的確信,但這麼做往往很危險。股票過去漲不代表未來一定漲,極限玩家說極限運動不危險,出一次意外就足以推翻先前上百次證明。
    歸納法是人類預測未來的重要原則,但要辨明原委,小心變異。
  • 損失規避

    相同的獲利及損失規模相同的狀態下(獲得1萬塊跟損失1萬塊),損失造成的情緒反應是獲利的兩倍。
    這點也來自演化,小心翼翼的基因較有機會延續香火。
    DM寫鬼故事恐嚇可能發生的損失,效果比鼔吹效益來得有效。
    壞事對我們的影響總是強過好事。
  • 社會性懈怠

    兩個人一起拉繩的出力只有單人拉繩的93%,三人同拉時每人出力只有85%,當增加到八人,每人出力只剩49%。
    只要個人可以藉團體混水摸魚不被發現,就會出現社會性懈怠。注:濫竽充數效應
    接力賽不會發生是因為每個成員的績效會被單獨檢視。人們只會偷懶不致完全不出力,以免被發現。
    責任分擔帶來社會懈怠,但也讓團體比個人更勇於冒險:「萬一失敗責任不用我一個人扛」
  • 指數成長

    連續30天,每天給一百萬 vs 第一天給1塊、第2天給2塊、第3天給4塊、第4天給8塊…
    人類對線型增長較有概念,對幾何成長(指數成長)的感覺不夠強烈。欠缺原因:遠古時代,自然界觀察不到幾何成長的例子。
  • 嬴家詛咒

    拍賣會的嬴家往往是真正的輸家。招標、網拍。標的物實際價值不確定時,過多競爭者導致不合理出價,嬴家的求勝心造成不理性。
  • 基本歸因謬誤

    解釋事情時,高估人物對事情造成的影響。
    在某某球員或教練帶領下,某球隊封王。業績不佳,某CEO黯然下台。二次大戰是希特勒一手挑起。聽音樂會時討論指揮家或獨唱表現,不討論樂曲。 源起我們思考時需要一張「臉」聚焦,當成標的。
  • 錯誤的因果關係

    頭蝨離開宿主,宿主就會生病發燒,所以生病時要在頭上放頭蝨。(蝨子不耐高溫,人一發燒蝨子就會跑走)
    研究指出,家中藏書多的學生功課較好,引發家長購書潮。(父母的教育程度影響孩子成績,而父母背景形響藏書數量)
    關聯性並非因果關係。
  • 月暈效應

    .COM泡沫前夕的網路設備需求讓Cisco成為全球市值最高的公司,獲得報章雜誌無止盡的吹捧。一年後股價跌至20%,原先吹捧有加的客服、經營策略、行銷技巧、執行長被嫌到一無是處。Cisco其實沒什麼改變,造成截然不同的評價。
    人們迷失在某個觀點上,用它設想事物的全貌。長相好看的人容易事業有成,我們普遍認定他們比較親切、誠實而且聰明。(人正真好,Again)
    月暈效應會障蔽我們的視野,讓我們看不清事物全貌。
  • 替代路徑

    人們常常只看到某個人現在擁有的財富名聲,卻未考慮他是透過極高風險的過程獲得成就(九死一生),過程中替代路徑的後果極為可怕(破產、喪命)。富貴險中求
    注:有一句簡單但傳神的俗諺「只看賊吃肉,沒看賊挨揍」
  • 預測的錯覺

    柏克萊大學研究,檢驗十年內各行各業284位專家做的82,361預測,應驗率與隨機預測相近。
    只有兩種人會預測未來:一無所知的人、不知道自己一無所知的人
    檢驗預測:預測者處於何種激勵機制?(預測錯會丟飯碗?純粹要博知名度,矇對就賺到?)預測者過往準確率有多高?
  • 聯結謬誤

    以下描述何者可能性較高? A)松山機場關閉,所有班機取消 B)因天候不佳,松山機場關閉,所有班機取消
    很多人會選B,但關閉原因不只天候,A限制少,顯然成立的機率更高。
    原因:理性思考運作前,直覺思考先啟動,它無法抗拒合情合理的故事。
  • 框架效能

    同一件事,因陳述方式不同,產生不同反應。「喂,垃圾埇滿了」vs「寶貝,要是你趕快去倒垃圾,我會愛死你的」
    六百個人命在旦夕。對策A預估可救回200條性命 vs 對策B有1/3機率讓600人生還,但有2/3機率無人倖免<=大家選A
    對策A預估會犠牲400條性命 vs 對策B有1/3機率無人犠牲,但有2/3機率全部死亡=>大家選B
    美化修辭:重挫是修正、撤退是轉進、被開除是重新出發…
    沒有框架,你什麼都沒法陳述,而日常生活裡則要小心框架建構的陷阱
  • 行為偏誤

    足球PK時,對方射左中右的機率各為1/3,但守門員不是向左撲就是向右撲,很少會站著等中路來球。即使行動根本沒用,先做了再說,至少別人覺得我很努力。
    遠古時代,看到黑影就跑的基因存活率高,當我們遇到情況不明,內心會泛起馬上採取行動的衝動,也算原始本能。
  • 不作為偏誤

    登山同伴落崖,未伸手援,最後同伴喪命 vs 將同伴推下懸崖致死。何者較惡劣?大部人覺得見死不救比較沒那麼嚴重,此為不作為偏誤。當作為與不作為都會導致損失,便會出現不作為偏誤。
    新藥會引發20%絕症病患立刻致死,但有80%機率治癒,大部分的人會選擇不核准。(出事官位不保)
    即使病患有強烈求死意願,協助安樂死仍被判刑。
  • 自利偏誤

    成功全是自己的功勞,失敗統統是外在因素影響。將失敗原因推到其他事物身上,才能舒緩內咎怠。
    詢問五個同住學生負責倒垃圾的比例,總和為320%,大家高估自己的貢獻。
  • 享樂跑步機

    中樂透的喜悅只能維持三個月,事業更上層樓的人三個月後幸福感消失,忘記豪宅的好,只記得每天通勤時間變長。這些人必須不斷換新車,學界稱這種效應為「享樂跑步機」-不斷工作,追求升遷,藉此享有更多更美好的事物,但仍無法感到幸福。
    處在不幸時,也會高估情緒持續的長度與強度。失戀時覺得天崩地裂,世界毁滅。
  • 自我選擇偏誤

    為什麼我最倒楣,我排隊的櫃檯處理速度最慢、一路都遇到紅燈。抽樣來源不客觀:排隊快時、一路綠燈時不會留意
    沒來的舉手。電訪每個家庭電話+手機的支數,發現沒有家庭是沒有電話的。
  • 聯想偏誤

    把無關的東西聯想在一起,因經驗相信穿紅內褲球運較佳、喝可樂時聯想到廣告裡年輕活力的形象。因瓦斯漏氣按電鈴被炸傷的推銷員,再也無法按電鈴。貓跳過一次熱爐子後就不會再犯了,但也再不敢上冷卻的爐子。一朝被蛇咬,十年怕井繩
  • 新手的運氣

    新手嚐到甜頭只是運氣或大勢使然,卻高估了自已的實力。如何辨別?把時間拉長,與其他參與者比較。
  • 認知失調

    吃不到葡萄的狐狸就說葡萄是酸的不屑吃。原本所想所做跟最後結果不同,是一種失調。
    買到的車引擎吵座位不舒服,安慰自己絕不會開到睡著,很棒。
    人會對自己撒點小謊,讓自己好過一點。
  • 雙曲貼現

    一年後拿一萬 vs 13個月後拿1萬1千 <= 大部分的人選後者,都等一年,晚一個月沒什麼差
    今天拿一萬 vs 一個月後拿1萬1千 <= 很多人選前者
    雙曲貼現:做決定時點與獲利時點愈近,「情緒利率」就愈高。(注:24小時到貨超邪惡 XD)
    雙曲貼現是本能,延遲享樂是種被教育訓練出來的自制能力

讀書筆記-行為的藝術

$
0
0

前情提要第二本「行為的藝術」讀書筆記來了。相較於「思考的藝術」聚焦於邏輯,這本書較偏心理學層次,原本不太引起我的興趣,沒想到讀來驚喜連連,常有「對耶,好像真的是這様」「慘,完全被說中」的感嘆。人類有許多古怪的行為習慣(有些說是劣根性也不為過),不少來自遠古以來的物種演化,由於不具某些「劣根性」的人活不久,留存的基因仍繼續主宰現代人類的行為,非常有趣的邏輯。

  • 理由的正當性

    我只有5頁,可以讓我先印嗎?因為我趕時間
    我只有5頁,可以讓我先印嗎?因為我想影印
    令人驚訝地,以上兩種說法都能成功插隊。說出理由就得到諒解與協助,理由是否有意義經常不是最重要的。說出「因為BlahBlah」即使理由很爛,也會有潤滑劑效果。
  • 決策疲勞

    要跟老闆報告一件鳥事,8AM,11:30,18:00三個時段哪個最好?
    密集做決策會消耗意志力,一連串比較或評估將導致精疲力竭 —> 決策疲勞。IKEA在賣場中間設餐廳,讓客戶休息吃飽再戰
    連續審理多起案件,法官的大膽判決比率從65%降到0(血糖不夠用),休庭後再審則又回到65%
  • 傳染性偏見

    人無法忽略某些已無實際連結的人事物,例如:裝過尿的杯子徹底洗淨,就科學衡量與全新杯子無異,仍令人覺得噁心。
  • 無意義的平均值

    非常態分配下的平均值,國民平均財富,夫妻平均一顆睪丸
    M型分佈,長尾分佈,冪次法則
  • 誘因排擠

    在當地設置核廢料儲存場民調,在承諾政府提供補償金後贊成率下降–>人們非出於金錢動機的行為因報酬瓦解(感覺被羞辱) 幼稚園收取延時托兒費用後,父母遲接比例反而上升—>人際關係轉為金錢關係,降低虧欠感。
  • 廢話潮流

    請參考相聲段子「語言的藝術」。例:名嘴、明星、運動員賽後訪問、經濟學者分析預測、營運不善的執行長… 說八道可以掩飾無知。馬克吐溫:如果無話可說,就閉上嘴巴
  • 威爾 羅傑斯現象

    分期遷移:管理A,B公司,A公司出色,B公司很慘,將A公司表現低於平均的產品移到B公司,A公司平均表現上升,B公司業績上揚,但實際沒有多賺錢。
    腫瘤診斷技術進步,更早發現初期患者(過去無法被發現,視為健康未歸類為患者),即使無藥可醫,病患平均存活年限拉長。
  • 資訊偏移

    更多訊息自然而然會導致更好的決定是一種迷思。網購爬文,可選擇的項目愈多,愈難做決定。
    試著與最少的資料共存,不需要知道的資訊即使知道了也沒有任何意義。
  • 群聚錯覺

    烤土司出現聖母臉孔、烤玉米餅出現耶穌形象、火星岩石浮現人臉、機車濺起泥漿出現骷髏頭。
    大腦會尋求模式及規則,遇到分散的信號(無規則的雜訊)找不出模式規則,大腦就會自己發明。
    從過去一段時間的股票指數與黃金價格整理出鐵律,但很快就會破功,不然人人都是千萬富翁。坊間很多對樂透數字關聯的公式推敲亦是一例。
    純屬巧合的情境比你想像得多,不要過度揣測。
  • 勞力辯證

    特種部隊結訓授階插入胸肌的胸針被裱框珍藏 、缺錢出售辛苦修整的哈雷,遇到市價兩倍的出價仍不肯賣… 勞力辯證的實例:當人對一件事投入大量精力後會誇大其價值,屬於一種認知失調。
    兄弟會、幫派噁心暴力的入會儀式、極嚴苛的入學考試也是團體利用勞力辯證箝制成員的手法。
    溫和形式的勞力辯證-IKEA效應,DIY家俱比名牌家俱更有價值,自己織的圍巾破舊過時也捨不得丟。
    需要依程序操作打一個雞蛋的半成品蛋糕粉比完全調配好的蛋糕粉銷量更好-適度的勞動及挑戰性。
    陷阱:當你對事物長期投入心力後,將無法客觀地評估它。
  • 小數法則

    統計失竊率(失竊金額佔總銷售額的比例)發現鄉村分店數字高,所以要針對鄉村全面加強防盗設備?事實上鄉村分店遭竊案件少,但因分店銷售額低顯得突出,而報表失竊率最低的也是鄉村分店。
    新創公司擁有較聰明的員工?員工少,平均智商變化幅度遠大於大型企業。發現統計上有突出表現的案例時,請先排除受小數法則影響的可能性。
  • 期待

    Google發佈2005Q4財報,營業額成長97%,淨利成長82%,股價卻在數秒內重挫16%觸發熔斷,恢復交易後再跌15%,只因其低於華爾街的預測。事前有過度期待,殘酷懲罰隨之而至。
    期待導致不合理的刺激,但也能激發正向動力。1965心理實驗,隨機挑選20%學生,謊稱其大器晚成具有高度潛力,結果被挑選學生的智商增加幅度大於平均值-羅森塔爾效應/比馬龍效能。(老師對這些學生有額外的期待而加強關注)
    醫學實驗接受安慰劑的患者有1/3產生療效—>改變大腦生物化學,進而擴大到全身。(對阿茲海默患者無效,因其腦部負責期待的區域受損)
    人類不可能沒有期待,請善用之,對於自己和所愛的人更多期待,增加他們的動力;對於無法掌控的事物(如股市)請降低對它的期待。
  • 簡單的邏輯

    1) 球拍與桌球合計1010元,球拍比球貴1000元,各為多少錢? 2) 5台機器5分鐘可完成5件毛衣,100台機器完成100件毛衣要幾分鐘? 3) 池塘裡繁殖快速的睡蓮每天覆蓋多一倍面積,完全覆滿池塘要48天,覆蓋半個池塘要幾天?
    答案1005 vs 5元、5分鐘、47天。以上稱為認知反射測試(Cognitive Reflection Test, CRT),測試成績:MIT學生>普林斯頓>密西根大學。CRT值低的人傾向依直覺行事,一鳥在手勝過二鳥在林,CRT高的人較擅於以意志力駕馭直覺,採理性斟酌。CRT較高的美國人多無神論者,CRT較低則傾向信仰上帝(愈容易依直覺行事者,愈無法以理智挑戰宗教觀念)
    如何提高CRT值:對簡單的邏輯問題保持懷疑態度。
  • 福勒效應

    心理學家福勒借用星座運勢解說常用手法,將對一般人的性格描述(說穿了是廢話,誰都嘛是這樣)偽稱是為受試者量身訂製,有86%受試者相信它是在描述自己且認為準確。
    原因:其中描述適用大部分的人、帶有恭維奉承性質讓人不想否認、正面特點效應(不涉及任何人格缺陷)、確認偏誤(接受符合自己的自我描述,不自覺排除不符部分,留下和諧形象)
    如何讓星座專家的呼嚨破功:在小卡片寫下對20名受測者的性格預測,請受測者從中挑出給自己的卡片。
  • 志工的愚蠢

    名攝影師該花六小時參加鳥保協會做鳥屋,還是花一小時拍照賺錢請木匠做鳥屋並將多出的錢捐給鳥類保護協會?(創造木匠工作機會又投入更多保護資金)
    做志工並不是無私奉獻,人往往能從中獲得一絲滿足,是一種期待回饋的付出。除非自己是真正的名人,參與活動能增加曝光並帶來關注,否則捐錢比投身志工能產生更大貢獻。
  • 情意的捷徑

    該不該反對基改?理性分析:列出所有優缺點,以其利益乘上發生機率得到正負期望值,依加總結果正負決定該贊成或反對。
    實際上不可行:我們沒有足夠想像力列出所有得失,難以提供精準的計算數值,更重要的,想太久會被野獸吃掉,人類演化成快速決策者,使用很精簡的思考方式-捷思法(Heuristics)。
    情意的捷徑,飛機噪音產生負面情緒,豪華帶來正面感覺,促使人類用情緒考量風險與利益,而非獨立理性評估。
    重機車擁有者看到重機危險高的報導,心中用「它可以帶來更多自由感」去中和。
    看似不起眼的東西會影響情緒:以0.01秒時間投影笑臉、憤怒臉及中性圖像,詢問受測者是否喜歡中國字,看到笑臉的受測者絕大多數表示喜歡。早晨太陽普照,股市上漲機率高。
    面對複雜情境:以「我對此有何看法」取代「我覺得…」
  • 內省錯覺

    維他命廠老闆對吃維他命有益健康深信不移,他人懷疑其主張源於自身利益考量。換個立場,每個人卻很少懷疑自己某項深信的觀念,因為評估自已信念是否可信是內在觀察,但內在觀察真的坦率無偏見嗎?
    實驗:快速出示兩張人像照片請受測者擇一,接著用很相似的照片調包請受測者看著照片說明選擇理由,大部分受測者仍可解釋得頭頭是道。內省不可靠,我們常會虛構某些事情以求合理。
    內省錯覺又稱為「選擇性的盲目」,當別人不認同我們的信念時,有三種反應:1.無知的看法:別人是因缺乏某些必要資訊,否則一定會支持我,政治人物常認定只要宣傳得宜,就能說服別人 2.白痴假設:不同意的人已知必要資訊,但因人笨所以無法得到正確結論 3.惡意假設:對方已可理解但選擇惡意對抗。例:不信我教者必為魔鬼
    愈是相信某樣東西,愈應該以批判的角度看待它。聰明人不需要信條,應該自己當自己的異教徒。相信自己太久太深,覺醒時的真相將會很殘酷。
  • 無法關上機會之門

    同時交往三個女人,總無法決定該娶誰,因為一旦決定就要放棄其他兩個。在大學雙修或三修,為未來創造更多就業選擇,是否真的有利?
    大部分人會盡力保留選擇的權利,即使理性分析保留選項無利可圖且需付出成本。
    值得敬佩的特例:項羽破斧沈舟,西班牙科爾特斯在登陸後把船弄沈。
    每個選擇都要付出成本(精力、思維、生活空間、金錢),即使成本很不明顯,也會減損成功的機會,保留太多選項有可能導致一場空。上策:忽略某些可能性,以做出明智決定。
  • 嗜新狂

    塑膠發明之初,預測50年後人類會生活在塑膠房屋裡;登月探索期,預測50年會出現火星校外教學。我們系統性地高估新發明的作用,瘋狂熱衷於新事物-嗜新狂。最新熱潮退色的事物,比我們想像得還要快。那些存在幾百到幾千年的老東西(皮鞋、椅子、叉子、書架),其中必有原因,不會被輕易取代。
    早期採用者:沒有最新一代iPhone就活不下去的人,某種程度上是患了某種病的非理性人類,新發明的實質好處一點都不重要,重點是要新,要潮。
  • 睡眠效應

    二戰期間愛國宣傳影片在播放時並未立即激起士兵對戰爭的熱情(知是官方宣傳,對其不齒),但九個月後,看過影片的士兵對戰爭的同情明顯較高。科學家百思不得其解,以為論據說服力應隨時間遞減。
    宣傳剛好相反,對於論點來源遺忘的速度比論點本身快,忘了是官方宣傳(來源),只記得戰爭是必要的(論點),因此「從不可信來源得來的訊息,時間一久會變得可信」,稱之睡眠效應。
    如何對抗?不要接受不請自來的訊息、遠離不良來源、盡量記清楚訊息來源。
  • 選擇盲從性

    MBA廣告給人付出昂貴學費獲得MBA會有巨大收益的分析說服它是值得的投資。盲點:1.念MBA者收入較高非因MBA課程,而是游泳選手身材錯覺 2.除了學費,還有因唸書減損的收入 3.分析引用未來30年收入折現,變異性極高 4.有其他成本更低的投資,效益更高<-選擇盲從性
    理財專員:買債券比定存利息高,所以值得買債券->忽略其他更佳的投資選項
    政客:建體育場 vs 草皮 –> 還存在學校、醫院等其他選項
    腫瘤手術切除50%致死率,不動它五年內有50%死亡率 -> 也許有另一種風險更低的手術選項,未來幾年出現新療法…
    面臨A與現況,直覺就在現況與A做決擇是種錯誤,應考量比較A、B、C、D、E… 避免落入陷阱。
  • 社會比較差異

    不想幫助別人,以免對方成為自己的競爭對手,即便其可能性極低。例:科學家特別嚴審年輕學者的突破性文章、諾貝爾得主阻止年輕有為同科學者進入同一大學任教。
    新創公司:A咖延攬A+咖同事加入公司造就成功。B咖找來C咖、C咖聘請D咖… 最後公司只剩Z咖。聘用能力更強的人,否則公司很快就滿是輸家->井蛙現象,能力差的人無法認知自己的無能。
    支持比你優秀的人,短期或使自己陷入危險,長期而言終將獲益,那些人遲早會超越你,在此之前請與之和睦相處並從中學習。
  • 首因效能和時近效應

    張三很聰明、勤奮、衝動、頑固、好嫉妒;李四好嫉妒、頑固、衝動、勤奮、聰明。你較願意跟誰一起被困在電梯裡?(註:抗議!此為「選擇盲從性」陷阱,我可以選郭雪芙嗎?)
    大腦評價第一個形容詞會比後續詞彙來得強 –> 首因效應 或 第一印象效應
    富麗堂皇的Lobby、律師穿著光高的名牌牛津鞋。對完美回答第一題的考卷特別有好感影響其他題目評分—>先改全部考卷的第一題,再改全部考卷的第二題。在討論議題時,第一個發表的意見容易左右結論,有意見要搶著講,若求公平主席應隨機詢問意見。
    「時近效應」則相反,大腦記不住太多資訊,後面得到的較容易記住。若項目多又要一段時間才做決定,時近效應明顯,例:演講常只記得結論。
    結論:處於中間位置,無法引人注意。面試避免偏見:每隔五分鐘評分一次再取平均。
  • 放血效應

    十九世紀前西方以四大體液說解釋疾病醫學,並以荒謬的放血療法治百病,而這種主張稱霸兩千年。我們不會因為一個理論是錯的而揚棄它,只會在有更好的選擇後才放棄它。
    人無法忍受「自己不知道某事」的感覺,慣於創造理論並選擇相信之,即便明知其存在不合理的部分。
  • 非我發明症群(Not Invented Here)

    這在軟體工程上也很常見,看別人的Code萬般不爽,永遠存在把舊系統翻掉重寫的衝動。
    人們喜歡並陶醉在自己的想法裡,有時需從遠處回頭審視自己的想法才能清醒。想想,過去十年裡,你有哪些想法是真正優秀的?
  • 黑天鵝效應

    幾個世紀以來歐洲人為天鵝都是白色的,直到1697年在澳洲第一次看到黑天鵝,推翻了公認的事實,造成震憾。
    黑天鵝效應指對生活造成重大影響的出人意料事件。美前國防部長倫斯斐的哲學主張:已知的事實、已知的未知、未知的未知,其中未知的未知一旦揭露,影響巨大。
    石器時代人類很少遇到改變人生的大事,網路時代一個轉彎收入就可能增加一萬倍(例:Google創辦人、哈利波特作者)。
    因應思維:假設看似乎不可能發生的事,其機率仍略高於零。
    留在正面黑天鵝效應區,能夠成為藝術家、發明家或擁有可複製產品的企業家。以出售時間為業(職員、牙醫、記者),不會出現黑天鵝效應。
    防止負面黑天鵝效應,保守儲蓄,成功時不要過於安逸,做最壞的打算。
  • 領域依賴性

    用醫學例子解釋「基本比率」比用商業活動舉例更能讓醫生秒懂其義。對投資者說明,用金融案例說明會比生物學例子更容易被理解。理解力很難從一個領域跨到另一個領域—>領域依賴性。以投資組合選擇理論得到諾貝爾獎的馬可維茲卻未在自己理財時活用該理論,學術知識未能跨到私人領域。行銷達人轉任CEO、3C超級業務轉任軟體銷售後失色無光。成功CEO家庭失敗、醫生抽菸者比例高於其他職業、全職執法人員家暴比例高、文學批評家寫的小說超乏味、婚姻治療師的婚姻狀況比其客戶還糟。在某領域有卓越表現的人,很難將其成功複製到其他領域。
  • 假共識效應

    我們常高估別人跟我們的一致性。實驗:徵求學生穿上庸俗廣告牌在校園招攬生意,願意的學生認為有62%的學生願意這麼做,不願意的學生認為有67%的學生會拒絕。政黨高估自己關注問題的緊迫性(覺得自己才是主流民意)、選民高估所屬政黨的支持率。產品開發部門對自己的產品充滿信心,覺得消費者肯定買單。
    假共識效應讓我們為與自己意見相左的人貼標籤,前例中穿廣告者認為不穿者缺乏幽默感,不穿廣告認為另一方是「白痴」「愛出風頭」。
    假共識效應不同於社會認同,後者來自於無意識的同儕壓力,前者源於演化,假共識讓人類更勇敢且具說服力,獲得更多資源,提高存活率。
    當你的觀點與眾人不同,不要把觀點不同的人當成傻瓜,不要懷疑別人,先質疑自己。
  • 歷史修正

    實驗:請三千人寫下對爭議性議題的看法,十年之後,請其回想十年前的答案並寫下現在的看法,二者幾乎一致,但與實際十年前填的答案相去甚遠。人類會不自覺調整過去的意見以配合現在的想法,藉此逃避自己錯誤的尷尬,認錯很困難,當我們意識到「自己錯了」,該為自己鼔鼔掌。
    有些記憶會深入人心,細節精準猶如照片-閃光燈記憶。但它也會犯錯,是修正過的結果。三年前後兩次要求寫下對挑戰者號太空梭爆炸的印象,只有7%前後一致,50%有2/3不同,25%完全錯誤。
    第一次見到初戀情人的那一幕多半能歷歷在目,但有可能超過一半的細節都是錯的,我們的記憶有缺陷,包含閃光燈記憶。有些錯誤可能致命,即使目擊者發誓能確認罪犯,仍需詳加查證。
  • 圈內圈外偏見

    為什麼會瘋狂地支持國家代表隊?團體認同。演化由來:被團體排擠後難以自力更生,死亡率高。
    隨機分配成小組,謊稱某小組因共同喜愛某種陌生畫風而編組,莫名提高該組成員對彼此的好感。(基於微不足道的基礎也能形成團體)
    高估圈外團體的同質性,科幻電影人類有不同文化,外星人卻如一個模子刻出來的。
    團體基於共同價值基礎,成員個別意見獲得不成比例的高度支持,形成失真-日常工作盲點。
    為隨機組合的團隊犠牲生命的思考錯誤-上戰場。哄騙手法:效忠祖國、同袍兄弟
    對陌生人存在偏見與反感是生物學事實,認同團體會讓人對事實產生扭曲,為他人而戰並非勇敢,而是愚蠢。
  • 無法忍受模糊

    兩個箱子,A箱放入紅黑球各50顆,B箱放入不知比例的紅黑球,摸到紅球得一千塊,大部人選A。
    艾斯伯格矛盾,人類傾向選擇已知,而非未知的機率,換句話說:人類無法忍受模糊。
    風險可以用機率評估,模糊不確定則無從預估。如果不希望過於倉促或判斷錯誤,必須容忍模糊性。
  • 預設值效應

    大多數人選擇預設值(招牌餐、手機預設設定)。政府如何在不限制自由不違憲的狀況下引導國民?在多個選擇方案外,增加一個標準選項。
    萬一沒有預設值,就用過往經驗。人們喜歡熟悉的事物,比起選擇、嘗試新事物,更愛維持現狀—>安於現狀偏誤。
    損失規避:損失造成的不悅是等值獲利帶來快樂的兩倍,重新談判各退一步也讓人覺得吃虧。
    我們強烈安於現狀,即便它讓我們處於不利位置。
  • 對懊悔的恐懼

    張三原想將賣A股票改買B股票,但後來沒做,B股上漲,等同損失10萬元。李四賣掉B股票改買A股票,B股上漲,等同損失10萬元。誰比後悔。92%的人覺得李四比較後悔。張三較符合一般人的行為,把錢放著不動,李四行為較異於常人,故感到更懊悔。
    最後一分鐘決定改搭失事班機的罹難旅客,格外令人同情。
    對懊悔的恐懼讓人不理性,阻止你丟掉不再需要的東西。(害怕有一天要用到時後悔你丟了它)
    最後機會式廣告「現場只剩最後一件,賣完了就沒有了」(你其實沒有很喜歡這件商品)
    最後機會讓我們陷入恐慌,對懊悔的恐懼導致不理性的決策。
  • 顯著性效果

    記者依據車禍現場發現大麻,未經查證就訂出新聞標題「大麻肇禍,駕駛喪命」-顯著性效果。顯著特性受到過度觀注,引誘人做出沒有根據的推論。例:對新任女性CEO的評論聚焦在其性別而非其能力、犯罪者的外藉移民身分被放大(未來在談及新移民時聯想到犯罪)。
    投資者對重大消息(CEO被解雇)的敏感度大於較不顯著卻重要的訊息(公司長期獲利表現)。顯著訊息過度影響我們的思維與判斷,讓我們輕忽隱藏性、緩慢發展的微小因素,我們應避免被奇特性蒙蔽。
  • 知識的另一面

    讀過上千本書沒開過刀的醫生 vs 只讀過一本書但做過千次手術的醫生。萊特兄弟沒讀過任何科學報告就做出飛機,飛機建造理論一直到30年後才出現。自動織布機、蒸汽機、汽車、燈泡全來自發明家而非理論家,我們高估知識分子、學者、理論家、作家、作者,低估實踐者與創造者。理念、產品及技術大多透過試誤成形,很少經由閱讀和思考。
    知識有兩種:一種可以用語言表達,另一種無法言傳。我們常高估語言可以表達的知識範圍。
    依賴語言知識的危機:學術理論與實務的差距、忽略沒寫下來的知識、不擅於自我表達者的意見被輕視
  • 私房錢效應

    撿到的錢被拿去買奢侈品。努力工作多賺十萬塊會存起來或投資,買彩券得到十萬則常拿來旅遊。賭場梭哈一萬元輸光卻覺得沒損失,因為那一萬元是之前嬴來的。
    相對賺來的錢,我們會輕率地使用嬴來、撿到、繼承的錢—>私房錢效應,人們較勇於將投機贏來的獲利花在高風險的地方。同理,樂透得主常常在幾年後變得潦倒。
    應用於行銷:賭博網站開戶送100美元、申請信用卡送100美元紅利、加入航空公司飛行俱樂部送數千哩里程…
    結論:當你嬴錢或獲得企業贈送某些東西時,請小心因不知節制,反而損失更多。
  • 拖延症

    拖延症指人類會拖延不愉快但重要的事(重要但不緊急的事):轉個彎去健身房、換個更划算的保險、寫張感謝卡。
    拖延是不理性的,因為計劃不會自動完成,麻煩永遠都在,大家心知肚明。但因付出與回報有時間上的差距,需要耗費大量心力才能跨越差距。
    心理實驗:學生解數學題時被餅乾香氣圍繞,一組被規定解完前不准吃餅乾,另一組則無限制。禁吃餅乾組多花了一倍時間才解完題目,因需花費精力自我控制,削弱了解題的意志力。
    防止自我放逐:專心做事時關掉網路,消除雜念。別人訂的時限比較有約束力,自己擬訂時程表最好分成小步驟各自訂出期限較易成功(注:蕃茄鐘工作法),把自訂的期限公告天下也是個好方法。
  • 嫉妒

    A.朋友平均收入都增加,只有你不變 B.朋友平均收入不變,只有你減少 C.朋友跟你的平均收入都減少。A最易讓人惱怒。
    俄羅斯故事:農民向神燈精靈許願,鄰居有牛我沒有,請讓鄰居的牛死掉
    財產、地位、健康、青春、技能、受歡迎、美麗都能引發嫉妒。我們特別嫉妒年齡、職業或生活方式與我們相仿的人。
    嫉妒感一旦出現很難消除,如何避開?停止與人比較、尋找競爭優勢圈(比上不足比下有餘,山中無老虎,猴子當大王)
    嫉妒的演化來源:其他人分到較大塊獵物,嫉妒引發行動,增加存活機率。但現今嫉妒多與生存無關,鄰居買了保時捷你不會少塊肉
  • 擬人化

    過去美國禁止媒骿刊登陣亡士兵照片,以防引發反戰思想。傷亡數字一直是公開的,但照片造成更大的震憾。
    過去十萬年來,人類基於團體生活的需要,發展出一種對他人想法與感受的敏銳感受力,稱之「心智理論」
    面對統計數據時我們較為冷漠,面對人則不然。哀求眼神的兒童照片,比受饑荒威脅兒童的嚴重比例數字募得更多捐款。
    故事比數字有效,每篇報導都要有人,股價消息要配CEO,國家事務由總統出面,地震要有受害者。
    應用:進攻時善用故事打動人心,防守時用事實與統計檢驗聽到的故事。
  • 「那殺不死我的,將使我更強壯」謬論

    「那殺不死我的,將使我更強壯」是尼采的名句,呼應我們的「愈挫愈勇」「打斷手骨顛倒勇」。
    在危機中存活者,往往只是幸運,危機就是危機,不是強大自我的過程。若危機讓人改變,那麼即使沒有危機人也可以做出相同的改變,相信可怕之事是有益的是一種錯覺,大病之後的人體一定不會比之前更健康。
    注:這是少數我不完全認同的論點,危機能刺激人體的防衛機制,激發團體憂患意識,荒唐度日者歷經喪親才清醒,生活習慣不佳的人在大病後開始注重養生。
  • 注意力錯覺

    只看導航沒注意警示牌跟路面的駕駛開車開進淹水路段、大猩猩影片實驗(要求計算白衣球員傳球次數,受測者幾乎都無視影片裡曾出現大猩猩)
    開車講電話降低的反應速度與酒駕相近,肇因於注意力錯覺。
    我們注意某些特定事物,對部分事物視而不見,而最危險的地方在於:我們錯以為自己已注意所有重要事物,請時時提醒自己是否忽略某些細節。
  • 策略性不實陳述

    面試時承諾自己並無把握的目標以爭取工作、承諾不合理的時程以爭取案子(屆時再用優秀的「藉口管理」逼迫對方妥協)。
    在一次性冒險上最常見(應徵面試、手術成效)。大型計劃總是延遲完成且超出預算,拿到案子的往往是最會吹牛的人(保守者提出的時程及成本通常不討喜)。人們有系統地忽略謊話,容忍策略性不實陳述。當事關重大,除了對方的承諾,請參酌其過往成績以為借鏡,並加上違約懲罰並要求保證金。
  • 想太多

    高爾夫名將在奪冠前一刻無法決定如何出桿,最後一洞打得荒腔走板。請學生評比54種果醬,原本結果與專家評比一致,在要求詳細陳述評分理由後排名大亂,公認冠軍反而拿下低分。
    想太多會遏阻情感智商。涉及熟練事物如運動技能或已回答過數千次的問題(巴菲特稱之競爭優勢圈),最好不要多想,想太多會妨礙以直覺尋求解決辦法,石器時代以來就存在的類似情境,用捷思法較有利;演化裡從未出現的複雜問題(例如投資策略)宜冷靜思考,此時邏輯勝過直覺。
  • 規劃謬論

    每天排訂當日作計劃卻很少如期完成,我們會高估自己的辦事能力,即使自知以往預測都過於樂觀,每次預測還是真心相信這次的計劃很實際肯定成功—>規劃謬論。
    要求學生為研究報告訂出「保守務實」及「諸事不順」兩種版本的時程,只有30%在務實時程內交付,平均時間比務實時程晚了一倍,甚至比諸事不順版多了七天。
    計劃執行速度及收益常被高估,成本與風險則相反。雪梨歌劇院1957年完成規劃,原本應在1963年完工,耗費七百萬美金,最後1973年才完成,花掉一億兩萬百美金,預算多了14倍。
    無法做好規劃的原因:1.妄想自己很優秀,能夠完成所有擬定工作 2.忽略計劃以外的因素(意外狀況)
    解決方法:參考過往經驗及外部案例,事前推演最糟的狀況。
  • 專業曲解

    馬克吐溫:「一鎚在手,萬物皆釘」 是「專業曲解」的最佳註解。
    同一個故事的不同觀點:某人貸款創業,不久後破產罹患憂鬱症,最終自殺身亡。企業管理家:管理不善?策略不佳?市場不利?行銷專家:目標族群設定錯誤。財務專家:財務槓桿操作不當。報社記者:自殺才有報導價值,讚。作家:如何改寫成動人的悲劇故事。銀行家:信貸部門有錯。社會主義者:哼!資本主義,不意外。虔誠教徒:這是上天的懲罰。心理醫生:悲劇源於血清素濃度過低。
    當你詢問一個問題,多數人會以自己的專案為思考基礎,不要成為自己狹隘思維模式的囚犯,試著多增加幾種工具,不同情境使用適合的觀點,思維才會靈活。
  • 蔡加尼克效應

    服務生不用紙筆正確無誤記下所有顧客點餐要求,但事後回頭請他代尋遺落的圍巾,服務生對當時客人所坐位置都毫無印象,蔡加尼克以此發表論文,稱之「蔡加尼克現象」-我們會緊記未完成的任務,並時時感受到壓力,直到完成則立即從記憶消除。
    而另一項研究指出,未完成的任務會不斷折磨我們,直到我們清楚知道如何與之相處,而為任務訂定計劃有很好的緩和效果。
    面對繁瑣雜事,擬訂完整書面計劃後我們才會心安。
  • 能力錯覺

    人們常會低估運氣對於成功的重要性,覺得辛勤努力是最大關鍵。研究指出,隨機比較兩家公司找出表現較佳的公司,只有60%的狀況,勝出公司的CEO能力也較強,有40%的狀況是勝出公司的CEO能力反而較差,只比隨機多10%。巴菲特:「執行長如果是划船手,其表現主要取決於他坐的船,而非他的划船技術」
    在某些領域能力幾乎無用武之地,統計公司多位投資顧問十年間的年度績效排名,幾乎呈現隨機分佈,同一位投資顧問有時名列前茅有時墊底,換言之,其表現來自運氣而非能力。有人確實以能力謀生,如飛行員、水電工、律師,請對其寄予尊敬,至於創業家、經理人、投資顧問就…(注:作者很輕視金融服務業呀)
  • 正面特點效應

    存在事物對我們的意義遠超過不存在的事物,健康時很少意識到可能生病,和平時不會想到戰爭,飛機降落時不會因為沒墜機感到訝異。偶爾想想不存在的事物,我們將會更快樂。
    膽固醇過高的沙拉標註其內含的20種維生素,不提膽固醇。科學期刊上很少出現成功否定某個假設的論文。我們傾向接受正面建議「去做X」,不是負面的提議「不要做Y」,但忽略了建議是否有意義。
  • 撿櫻桃

    吃泡麵時不會期望看到與包裝照片相同的內容物,看房子時不會期待跟DM或網站照片一樣的佈置,因為我們已就熟知賣方只會突顯美化好的一面而自動降低期待,這種行為稱為「撿櫻桃」(Cherry Picking)
    領域愈專門,愈容易掉入撿櫻桃陷阱。學術研究擅長告訴我們它們所做的貢獻,對於沒做的事三緘其口。自我美化是闖蕩職場的基本功,破解之道:追查失敗計劃與未實現目標,可以發現人們常隨著時間修改目標,射箭畫靶。
  • 單因謬誤

    為什麼會發生九一一攻擊事件?金融大海嘯誰該負責?入侵伊拉克的真正原因是什麼?
    許多問題絕非單一原因造成,但人們總不放棄追求單一的簡單答案。解決之道,畫魚骨圖(要因分析法)。
    單因謬誤歷史悠久,將成功或天災人禍歸因於一人,將其標為負責人/代罪羔羊,是權力運作的重要手法。
  • 治療意向錯誤

    銀行統計公司客戶,債台高築公司的營利高於無貸款公司:1.不賺錢的公司借不到錢,自動歸為未負債 2.還不起利息的公司被銀行接管抛售,不再是客戶
    時速150開完指定里程的駕駛為「賽車手組」,反之為「理性駕駛組」,賽車手肇事率低,開車比較安全?:一旦肇事就無法在時限內開完,一律被歸為理性駕駛組。
    未定期服用新藥的患者死亡率較高:因副作用或病況嚴重無法定時服用,都被歸為未定期服用
    克服之道:應將所有曾參與治療的樣本都納入分析,以防止結果偏差。
  • 新聞錯覺

    建議遠離新聞,純粹浪費時間而無益處。理由:1.大腦易被腥羶色吸引,為求點擊/收視率,智慧性、複雜和抽象的消息與背景知識被刻意淡化。 2.回想過去一年內看過的一萬則消息,是否有任一則對生活、事業有幫助?3.全球人類因觀注某則消息可能損失數十億小時的生產力。
    請拒絕吸收即時新聞,選擇深入探討背景的文章與書籍。

在PowerPoint投影片剪貼Visual Studio程式碼

$
0
0

最近在準備開發教學投影片,有大量從Visual Studio剪貼程式碼範例到PowerPoint的需求,遇到一個大問題:從Visual Studio複製程式碼再貼到PowerPoint時,語法顏色有被保留,但換行消失,排版全亂,讓人極度困擾。

想起VS2010推出時也有類似的剪貼簿亂碼問題,當時寫了個小工具自力救濟。這次面對的情況類似,理應可如法炮製,便花了點時間研究,發現Visual Studio複製到剪貼薄的內容包含HTML格式,使用Regex將原始碼中的\n換成<br />,在PowerPoint就能忠實反應原有的換行與排版。以修正版HTML換掉原本的剪貼薄內容,惱人的換行問題便可迎刃而解。

修正剪貼簿內容的核心邏輯只有短短幾行,如下所示:

privatevoid btnAddLineBreaks_Click(object sender, EventArgs e)
        {
            IDataObject dataObject = Clipboard.GetDataObject();
if (!dataObject.GetDataPresent(DataFormats.Html)) return;
string html = dataObject.GetData(DataFormats.Html) asstring;
string endFragmentTag = "EndFragment";
if (!html.Contains(endFragmentTag)) return;
 
 
string pattern = endFragmentTag + ":(?<n>[0-9]{8})";
            var findLen = Regex.Match(html, pattern);
if (!findLen.Success) return;
            var len = int.Parse(findLen.Groups["n"].Value);
 
            var htmlStart = html.IndexOf("<HTML");
            html = Regex.Replace(html, "\n", (m) =>
            {
if (m.Index < htmlStart) return m.Value;
                len += 6;
return m.Value + "<br />";
            });
//change EndFragment and EndSelection
string origLenText = findLen.Value;
            html = html.Replace(
                        origLenText, 
string.Format("{0}:{1:00000000}", endFragmentTag, len))
                   .Replace(
                        origLenText.Replace("Fragment", "Selection"), 
string.Format("EndSelection:{0:00000000}", len));
 
            DataObject newDataObject = new DataObject();
            newDataObject.SetData(DataFormats.Html, html);
            newDataObject.SetData(DataFormats.Text, 
                dataObject.GetData(DataFormats.Text) asstring);
            Clipboard.SetDataObject(newDataObject, true);
        }

以下是動畫展示。原本從VS2015複製程式碼,貼到PowerPoint時換行符號會不見,叫出自製小工具,複製程式碼後先按「Add Line Breaks」再貼到PowerPoint,換行及排版便能完整保留。註:以上程式碼丟進WinForm按鈕事件就可運作,如果嫌麻煩,我在FB社團放了一個現成版本

另外,將程式碼貼到內容區塊的做法雖然能保留換行,但原有底色及文字顏色會被PowerPoint置換,若想讓Visual Studio程式碼樣式在投影片原汁原味重現,我找到一個好方法:不要將程式碼貼入中央的內容區塊,改將滑鼠移到投影片的空白處透過右鍵選單貼上(或將焦點自內容區塊移開再按Ctrl-V),程式碼會被貼到投影片最上方,程式碼樣式被完整保留,但高度未依內容自動調整。此時只要隨意更動區塊寬度,高度就會依內容自動縮放,接著Visual Studio程式碼就在簡報上完美重現囉!

在IIS設定SVG與字型檔MIME Type

$
0
0

開始試用docfx產生API文件(docfx是微軟程式庫文件產生器SandCastle的接班人),編譯專案時可一併產生API文件網頁,非常方便,大推!(docx的安裝使用可參考網友霧隱虎的文章,VS2015用NuGet在專案安裝docfx.msbuild,以後每次編譯時可在_site目錄找到API文件網頁。)

直接從檔案總管瀏覽文件網頁大致正常,只有左側目錄總表因本機網頁JavaScript受跨網域存取限制無法顯示,將文件丟到IIS,目錄總表正常了,但出現另一個小問題,docfx的Logo「頭文字D」變成圖檔破損圖示。使用F12開發者工具偵察,問題出在瀏覽無法下載.svg檔案。

檢查logo.svg檔案確實存在,我想起類似問題在使用FontAwesome時也遇過,woff等字型檔對IIS是未知檔案類型,基於資安原則IIS一律回應HTTP 404,需加入MIME設定才能解決。一直沒為此寫過筆記,在此補上。

IIS7以上的版本可在web.config加入設定:參考來源

<system.webServer>
<staticContent>
<mimeMapfileExtension=".eot"mimeType="application/vnd.ms-fontobject"/>
<mimeMapfileExtension=".ttf"mimeType="application/octet-stream"/>
<mimeMapfileExtension=".svg"mimeType="image/svg+xml"/>
<mimeMapfileExtension=".woff"mimeType="application/font-woff"/>
<mimeMapfileExtension=".woff2"mimeType="application/font-woff2"/>
</staticContent>
</system.webServer>

而我這次遇上的是一台古老IIS6,MIME對應要用IIS管理員設定:

設定妥當,頭文字D就順利出現囉~

MVP十年有感

$
0
0

廉頗老矣,尚能飯否?

收到微軟寄來MVP 10年環,驚喜之餘帶點感觸,遙想十年前首獲MVP時還是個對技術充滿狂熱的熱血男兒,轉眼十年過去,人生走進另一塊領域,中年大叔心所嚮往的已是不同風景,常自嘲自己已是一尾準鹹魚(鹹到什麼地步呢?連跟同事朋友聊天都怕自己會散發無為不爭的灰色思想,害熱血青年跌進消極深淵,罪過罪過),差別只在於此鹹魚看見茶包還會激動,遇到邏輯謬誤會憤慨,對累積多年的開發技能微微自豪,不捨放棄而繼續經營,一撐十年還沒死在沙灘上,連自己都意外。說來,得謝謝各位讀者朋友支持捧場,對資深技術老人不離不棄,人間有溫暖,四季皆如春。

年過40好幾,不是顧問講師卻硬要留在技術職的道路是冷僻而孤獨的,常有黑暗力量誘惑或將你推向管理職務,需要幾近耍賴的堅持跟很好的運氣才能如願,非常幸運一把年紀之際卻有份只靠技術不必管人的工作並樂在其中。(換個角度,深知自己的長處短處,這才是發揮最大價值的角色,但很多類似條件的技術咖卻不一定能如願。參考:50歲的IT人都到哪裡去了呢?)說起這幾年的角色,有點像部隊裡的老士官長,三行四進狙擊刺槍奪刀(破解、想解法、射茶包)難不倒我,體力雖然下滑,憑著豐富實戰經驗與技巧在戰場還可以撐很久死不了,但只要談到帶兵攻防戰略(專案管理、軟體工程)就皺眉(XD)。部隊總是需要將軍領兵打仗,企業誰不希望找到將領帶更多的兵攻城略地,迅速壯大?不往管理職發展很容易被上司同事親朋好友誤解為不知上進不成材(笑),這大概可以解釋為什麼很多技術好手最終被推上不擅長或沒興趣的管理職,變成彼得原理活生生的悲情鐵證,在我看來是暴殄天物,在台灣職場卻是常態。(彼得原理:在組織或企業制度下,人會因其某種特質或技能被擢升到不能勝任的職位,變成組織的障礙物(冗員)及負資產)

雖然才智與成就不及大神的1/100,對Anders Hejlsberg在去年一場座談的談話心有戚戚焉:

我非常幸運我的愛好和工作是結合在一起的,但不是人人都像我這麼幸運,最重要的是你要找到你的熱情所在,而這個又能帶給你快樂,這就是帶來更好的工作成就,如果你面臨一個選擇,一方面是工作能多給你錢,另一方面是你很熱愛的,這樣的話才能工作得非常帶勁,你也能夠成長,同時也能學到非常多的東西。

我是自主選擇做技術方面的工作,實際上我不管人,也沒有人向我彙報,但在技術方面我負的責任是非常大的。因為對我的工作描述是我必須要創作出在全球開發者社區都非常有影響的產品,所以我是不管人的,我更願意從事我非常擅長的技術方面的工作而不是管理人,這是我的專長所在,也是我的熱情所在。

文章開頭是10 年來的MVP環合體照。我應該是全世界唯一湊出12枚拍照的MVP(XD),理由是微軟2009年10月才推出可堆疊MVP環的獎座,所以沒有2007/2008環這種東西,我的第一枚則從2010年開始。我特別托人翻模搞來三枚替代環,再修圖補上年份(謎之聲:這麼搞剛又假掰,不愧是黑大呀!),讓合體照的加法運算不觸發Assert.AreEqual()。:P

MVP十年,期許自己在這條路上還能再戰十年,懇請舊雨新知繼續支持愛護。

我的.NET舊版相容情結

$
0
0

前陣子有篇文章提到微軟中止.NET 4.0/4.5/4.5.1技術支援,結論是:1) Windows預設會自動升級到.NET 4.5.2,2) 用.NET 4.0/4.5/4.5.1寫的程式不需修改就可直接執行(僅有極少例外)。

很妙的是,每回建立新專案,我下意識總是挑選較低版本,例如:開.NET 4專案若沒用到async、await等.NET 4.5專屬特色,我會優先選.NET 4.0,心裡總覺得:萬一使用者的電腦的.NET版本比較舊,程式也能跑。

其實,依目前.NET的版本更新政策,使用者機器都該升級到.NET 4.5.2,唯一的例外是Windows XP/Windows 2003,而我程式部署對象全部都是Windows 7以上,換言之,選.NET 4.0根本不會有任何好處!但我總是沒來由的偏好舊版,已然變成一種「舊版相容情結」。

但,也不致於有壞處吧?我以前也這麼想,直到我膝蓋中了一箭…

上週分享的Visual Studio程式碼剪貼修正小工具,依照自己不成文的慣例,我又選了.NET 4.0。而在處理剪貼簿讀取時,發現取出的HTML內容,中文變成亂碼…

起初以為是單純中文編碼問題,試著用Big5解成byte[]再以UTF8轉回字串,由於某些字元被吃掉,研判是底層解析階段已發生不可逆的資料遺失,不可能靠轉碼還原。我可是打不死的小強老士官長耶,豈有被這種小問題難倒的道理?開始動腦筋想如何直接回取回原始資料,不走.NET元件,莫非要呼叫Windows API?

爬文找到一篇文章,針對同樣問題做了剖析,確認這是.NET 4.0的已知問題,已經在.NET 4.5+修正。

將專案版本切到.NET 4.5.2,什麼都沒改,問題消失!

由這次經驗,我決定調整自己的.NET版本選擇原則:除非有很明確且無法抵抗的版本限制(例如:幫田僑仔阿土伯寫「地契管理系統  for XP」),否則一律選用最新版本並要求使用者升級。新版本排除掉一些Bug、修正安全漏洞,基本上要比舊版好,像本次案例,用新版的話當場少踩一個坑。

別跟自己過不去,新專案請用.NET 4.5.2/4.6.1!

註:該選擇.NET 4.5.2還是.NET 4.6.1?我沒找到權威解答,猜想差別在於部署廣度,目前只有Widows 10內附.NET 4.6,其他版本Windows需要另外下載安裝。(如有錯請指正)

Google Cardboard體驗與Viewer Profile 調校心得

$
0
0

Google Cardboard是 Google 發明的窮人版觀落陰3D立體眼鏡,用瓦楞紙板、兩塊凸透鏡、魔鬼氈折成將手機固定在眼前的小紙箱,執行特殊App對左右眼分別顯示有視差的影像產生立體感,再感測手機的移動隨頭部轉動改變視角,營造逼真的虛擬實境感。另外,Cardboard 在側面還加了一對隔紙板相吸的磁鐵,手機如有 NFC 功能,移動磁鐵改變磁場還可控制 App,十分巧妙的點子。

收到好心人贈送 Cardboard 一付,三兩下組好正想體驗,「休士頓,我們有個麻煩…」

Cardboard 設計的對象是 6 吋以下的手機,我的手機 Lumia 920  4.5 吋,大小沒問題,但它是 Window Phone 呀,啊啊啊啊啊~ 幸好家裡還有一支退役的 iPhone 4 可供把玩,但 Cardboard App 要 iOS 8 才能跑,啊啊啊啊啊~ 無機可測的困境直到跟女王借測 Xperia Z3+ 才算搞定,試玩效果挺不賴。App 還有個巧妙設計,可以掃 Cardboard 上印的 QR Code 針對不同 Cardboard 尺寸規格最佳化,我拿到的 Cardboard 沒附 QR Code,但用 App 預設值就跑得很好。

借來的手機怎能玩得盡興,想起我的 Nexus 7 來了,30 公分 7 吋尺寸大了點,蓋子合不上用手抓可以湊和,但畫面太大超出可視範圍是個問題。不想動手改造硬體,想起 App 可透過 QR Code 針對規格最佳化這招。Google 很貼心地提供輸入尺寸產生 QR Code的網站,輸入鏡片到手機距離、兩眼孔距、高度等資訊,可產生 QR Code,讓 App 產生最佳化的影像。

摸索一陣子抓到訣竅,以下是我的心得:

    1. Viewer Profile 製作網頁有教你怎麼量尺寸,基本上照量照填即可。
    2. 若出現重疊影像,可增減 Inter-lens distance 直到疊影消失。
    3. 由於我想在 6 吋規格硬套 7 吋平板,有個作弊方法是短報 Screen to lens distance,數字愈小,App 產生的影像也愈小,縮小只顯示在螢幕中下半截(如照片),終於看到全景了。 不過,App只有部分螢幕能用,解析度較差。
    4. Distortion coefficients 用來修正凸透鏡形成的邊緣變形,只能靠試誤法,調整讓物體移到視野邊緣時不要嚴重變形,我試了k1/k2設0.3看起來OK就沒再細調。
    5. 不知是內附磁鐵磁力不足還是離NFC感應器太遠,磁鐵得拿到平板背後滑動才能被感應到,所幸 Nexus 7 螢幕有一大截露在紙箱外,可用手指點螢幕也能操作代替。XD(註:將手機/平板豎直可退回上一步)
    6. 人臉 T 字部位出油量超乎你的想像,不想用幾分鐘紙箱就變得油油髒髒,除了花大錢買化妝品改善膚質,便宜解法是在接觸 T 字部位的表面貼一層包水果的泡綿,當然出油還是照樣出油,但看不出來不影響心情就無所謂了。

Cardboard 的虛擬實境需要轉頭控制視角或方向,手機的重量讓人不敢鬆手,在旁觀者眼裡,只見某個人抓著便當盒貼在眼前搖頭晃腦嘴角上揚,傻氣指數爆表,呵~ (玩久頭還會暈,Nexus 7 意外有防暈效果,因為在頭暈之前手會先酸,哈)

前陣子一張集體 VR 設備體驗大會跟觀落陰對照圖引人會心一笑,不知道幾年之後,一家人自顧自滑手機的冷漠場面會不會變本加厲,人人頭戴著VR頭盔各自沈醉在自己的副本,小孩有沒有回家?爸爸人在客廳還是在房間?不重要 XD

Viewing all 428 articles
Browse latest View live