2012年4月3日

用戶界面設計中「狀態」和「動作」的表達




用戶界面設計中「狀態」和「動作」的表達

簡介

一、問題引發思考
前陣子與同事探討一個小需求時又遇到了按鈕表示「動作」和表示「狀態」間矛盾問題。想想這個問題多年前已經開始討論了,所以在此整理一下思路,與大家共享。
taobao1
具體案例是這樣的:如上圖所示,在「啟用」和「停用」搜索定製功能時,這個按鈕到底是表示「狀態」還是表示「動作」呢?簡單的說,上圖中 1表示當前是停用狀態,還是表示點擊後為停用操作呢?答案顯然是不清晰的。
怎樣能夠清晰的表達兩者的差異,減少用戶的誤解呢?我們首先要從「動作」、「狀態」的定義和兩者的關係入手。
二、什麼是動作,什麼是狀態
動作是指具有一定動機和目的並指向一定對象的運動。
狀態指人或事物表現出來的形態。
結合來講,動作是促使人或事物改變某一狀態的運動,而狀態是動作造成的結果表現。因此兩者經常是緊密關聯的,有時甚至是互為條件的。
比如「操作收音機」這件事,我需要扭動旋鈕或按鍵等「打開」的動作來打開收音機,當動作完成後收音機的狀態是「打開的」。而「關閉」收音機這個動作的前提,是一個「打開的」狀態的收音機。
」聽起來好複雜阿!「很多人都會這麼認為。為什麼我們在生活中天天面對類似的事卻很少有這樣的感覺呢?

下面來看看生活中的例子我們或許就能理解了。
三、生活中的例子
1、電燈開關


kaiguan1
如果你想要查看更多的細節與內容,請至原文出處查看 本文章簡短節錄於 - 有一點 - 阿里巴巴交互設計部 原文網址 : http://www.aliued.cn/2010/11/07/expression-of-state-and-action-in-ui-design.html

Sonic html5 canvas loader


Sonic html5 canvas loader

簡介

Sonic是一款極小的js類(JavaScript Class),它可以使用來創建客製化的讀取標誌動畫(custom loading animations),它執行一個不中斷的迴圈,就好像一條蛇不斷追著自己的尾巴。 

Sonic使用HTML5的Canvas API來完成的,它在很短的間格中部段的繪製圖形到畫面上,而這個圖形決定於你定義給它的path,首先看一下官方的DEMO

http://padolsey.net/p/Sonic/repo/demo/demo.html

範例一 - 正方形

<!doctype html> <meta charset="utf-8" /> <title>Sonic</title> <style> </style> <body> <script src="../src/sonic.js"></script> <script> var square = new Sonic({ width: 100, height: 100, fillColor: '#000', path: [ ['line', 10, 10, 90, 10], ['line', 90, 10, 90, 90], ['line', 90, 90, 10, 90], ['line', 10, 90, 10, 10] ] }); square.play(); document.body.appendChild(square.canvas); </script> </body>
在這個範例中,我們創建了一個Sonic類別,並給予高度、寬度、顏色以及最重要的路徑屬性,path是一個二微陣列,我們將要移動的路近以這樣的格式寫入
['line', startX, startY, endX, endY]


詳細模式可以參考HTML5相關解說了解
http://www.html5canvastutorials.com/tutorials/html5-canvas-lines/


創建完畢後,我們將Sonic放置到我們的畫面上,並且啟動(play)。



範例二 - 繞圓圈


在Canvas中,如果要畫曲線(橢圓、圓、拋物線等),使用的是arc這個屬性,下面範例中會產生一個不斷繞著圓圈的讀取標示
var circle = new Sonic({ width: 50, height: 50, padding: 50, strokeColor: '#000', pointDistance: .01, stepsPerFrame: 3, trailLength: .7, step: 'fader', setup: function() { this._.lineWidth = 5; }, path: [ ['arc', 25, 25, 25, 0, 360] ] }); circle.play(); document.body.appendChild(circle.canvas);

附註 : 額外參數說明


Path 參數

['line', startX, startY, endX, endY]

['bezier', startX, startY, endX, endY, cp1x, cp1y, cp2x, cp2y]

['arc', cx, cy, radius, startDegree, endDegree] (degrees, not radians!)

Create 參數

Options that you can pass to new Sonic({...}) include:

path: 

An array which defines the path taken by sonic. Look at the square example above. Each array item is an array with the format [methodName, arguments...], with the available methods specified above (line, bezier and arc).

width: 

The pixel width of the canvas (note: not including padding)

height: 

The pixel height of the canvas (note: not including padding)

padding (default: 0): The pixel padding.

fillColor: 

The canvas' context's fill color (e.g. red, rgb(255,0,0), #F00)

strokeColor:

The canvas' context's stroke color (e.g. green, rgb(0,255,0), #0F0)

fps (default: 25): 

How many frames per second. More fps means smoother animation and sonic will progress through the specified path quicker. Less frames means the opposite. There shouldn't be much need to change this.

stepsPerFrame (default: 1): 

This integer specifies how many steps of an animation should occur on each frame.

pointDistance (default: .05):

 The distance between points (0..1) in each path (that is, each sub-path under the main path). If you draw a line, and set the pointDistance to 0.1, then there will be ten steps in the animation (ten points).

trailLength (default: 1): 

The length of sonic's trail (0..1). A length of one means that it's like a snake trying to eat its own tail.

step (default: "square"): 

A function OR a name of a function found in Sonic.stepMethods. This function will be called on every step of the animation. See below (under "more control") for more info on this function.

domClass (default: "sonic"): 

A class to be applied to the  element.


Java 判斷檔案格式




Java 檔案格式判斷

簡介

檔案格式判斷雖然可以直接從副檔名直接查看,但對於一些無副檔名或是副檔名錯誤的檔案卻無得知實際的檔案型態,下面範例為使用Java來判斷檔案的標頭,方法就是將檔案bytes轉換成hex(十六進制)格式,在加以判斷檔案的編碼。


範例程式碼
public class test { /** * byte陣列轉換成16進制字串 * * @param src * @return */ public static String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder(); if (src == null || src.length &lt;= 0) { return null; } for (int i = 0; i &lt; src.length; i++) { int v = src[i] &amp; 0xFF; String hv = Integer.toHexString(v); if (hv.length() &lt; 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); } /** * 根據檔流讀取圖片檔真實類型 * * @param is * @return */ public static String getTypeByStream(FileInputStream is) { byte[] b = new byte[4]; try { is.read(b, 0, b.length); } catch (IOException e) { e.printStackTrace(); } String type = bytesToHexString(b).toUpperCase(); if (type.contains("FFD8FF")) { return "jpg"; } else if (type.contains("89504E47")) { return "png"; } else if (type.contains("47494638")) { return "gif"; } else if (type.contains("49492A00")) { return "tif"; } else if (type.contains("424D")) { return "bmp"; } else if (type.contains("494433")) { return "mp3"; } return type; } public static void main(String[] args) throws Exception { String src = "2NE1"; FileInputStream is = new FileInputStream(src); String type = getTypeByStream(is); System.out.println(type); } }


檔案編碼列表
'jp2'=>'0000000C6A502020', '3gp'=>'0000002066747970', '3gp5'=>'0000001866747970', 'm4a'=>'00000020667479704D3441', 'ico'=>'00000100', 'spl'=>'00000100', 'vob'=>'000001BA', 'cur'=>'00000200', 'wb2'=>'00000200', 'wk1'=>'0000020006040600', 'wk3'=>'00001A0000100400', 'wk4'=>'00001A0002100400', 'wk5'=>'00001A0002100400', '123'=>'00001A00051004', 'qxd'=>'00004D4D585052', 'mdf'=>'010F0000', 'tr1'=>'0110', 'rgb'=>'01DA01010003', 'drw'=>'07', 'dss'=>'02647373', 'dat'=>'A90D000000000000', 'db3'=>'03', 'qph'=>'03000000', 'adx'=>'80000020031204', 'db4'=>'04', 'n'=>'FFFE0000', 'a'=>'FFFE0000', 'skf'=>'07534B46', 'dtd'=>'0764743264647464', 'db'=>'D0CF11E0A1B11AE1', 'pcx'=>'0A050101', 'mp'=>'0CED', 'doc'=>'D0CF11E0A1B11AE1', 'nri'=>'0E4E65726F49534F', 'wks'=>'FF00020004040554', 'pf'=>'1100000053434341', 'ntf'=>'4E49544630', 'nsf'=>'4E45534D1A01', 'arc'=>'41724301', 'pak'=>'5041434B', 'eth'=>'1A350100', 'mkv'=>'1A45DFA393428288', 'ws'=>'1D7D', 'gz'=>'1F8B08', 'tar.z'=>'1FA0', 'ain'=>'2112', 'lib'=>'213C617263683E0A', 'msi'=>'D0CF11E0A1B11AE1', 'vmdk'=>'4B444D', 'dsp'=>'23204D6963726F73', 'amr'=>'2321414D52', 'hdr'=>'49536328', 'sav'=>'24464C3240282329', 'eps'=>'C5D0D3C6', 'pdf'=>'25504446', 'fdf'=>'25504446', 'hqx'=>'2854686973206669', 'log'=>'2A2A2A2020496E73', 'ivr'=>'2E524543', 'rm'=>'2E524D46', 'rmvb'=>'2E524D46', 'ra'=>'2E7261FD00', 'au'=>'646E732E', 'cat'=>'30', 'evt'=>'300000004C664C65', 'asf'=>'3026B2758E66CF11', 'wma'=>'3026B2758E66CF11', 'wmv'=>'3026B2758E66CF11', 'wri'=>'BE000000AB', '7z'=>'377ABCAF271C', 'psd'=>'38425053', 'sle'=>'414376', 'asx'=>'3C', 'xdr'=>'3C', 'dci'=>'3C21646F63747970', 'manifest'=>'3C3F786D6C2076657273696F6E3D', 'xml'=>'3C3F786D6C2076657273696F6E3D22312E30223F3E', 'msc'=>'D0CF11E0A1B11AE1', 'fm'=>'3C4D616B65724669', 'mif'=>'56657273696F6E20', 'gid'=>'4C4E0200', 'hlp'=>'4C4E0200', 'dwg'=>'41433130', 'syw'=>'414D594F', 'abi'=>'414F4C494E444558', 'aby'=>'414F4C4442', 'bag'=>'414F4C2046656564', 'idx'=>'5000000020000000', 'ind'=>'414F4C494458', 'pfc'=>'414F4C564D313030', 'org'=>'414F4C564D313030', 'vcf'=>'424547494E3A5643', 'bin'=>'424C4932323351', 'bmp'=>'424D', 'dib'=>'424D', 'prc'=>'424F4F4B4D4F4249', 'bz2'=>'425A68', 'tar.bz2'=>'425A68', 'tbz2'=>'425A68', 'tb2'=>'425A68', 'rtd'=>'43232B44A4434DA5', 'cbd'=>'434246494C45', 'iso'=>'4344303031', 'clb'=>'434F4D2B', 'cpt'=>'43505446494C45', 'cru'=>'43525553482076', 'swf'=>'465753', 'ctf'=>'436174616C6F6720', 'dms'=>'444D5321', 'adf'=>'5245564E554D3A2C', 'dvr'=>'445644', 'ifo'=>'445644', 'cdr'=>'52494646', 'vcd'=>'454E545259564344', 'mdi'=>'4550', 'e01'=>'4C5646090D0AFF00', 'evtx'=>'456C6646696C6500', 'qbb'=>'458600000600', 'cpe'=>'464158434F564552', 'flv'=>'464C56', 'aiff'=>'464F524D00', 'eml'=>'582D', 'gif'=>'47494638', 'pat'=>'47504154', 'gx2'=>'475832', 'sh3'=>'4848474231', 'tif'=>'4D4D002B', 'tiff'=>'4D4D002B', 'mp3'=>'494433', 'koz'=>'49443303000000', 'crw'=>'49491A0000004845', 'cab'=>'4D534346', 'lit'=>'49544F4C49544C53', 'chi'=>'49545346', 'chm'=>'49545346', 'jar'=>'5F27A889', 'jg'=>'4A47040E000000', 'kgb'=>'4B47425F61726368', 'shd'=>'68490000', 'lnk'=>'4C00000001140200', 'obj'=>'80', 'pdb'=>'ACED000573720012', 'mar'=>'4D41723000', 'dmp'=>'504147454455', 'hdmp'=>'4D444D5093A7', 'mls'=>'4D563243', 'mmf'=>'4D4D4D440000', 'nvram'=>'4D52564E', 'ppz'=>'4D534346', 'snp'=>'4D534346', 'tlb'=>'4D53465402000100', 'dvf'=>'4D535F564F494345', 'msv'=>'4D535F564F494345', 'mid'=>'4D546864', 'midi'=>'4D546864', 'dsn'=>'4D56', 'com'=>'EB', 'dll'=>'4D5A', 'drv'=>'4D5A', 'exe'=>'4D5A', 'pif'=>'4D5A', 'qts'=>'4D5A', 'qtx'=>'4D5A', 'sys'=>'FFFFFFFF', 'acm'=>'4D5A', 'ax'=>'4D5A900003000000', 'cpl'=>'DCDC', 'fon'=>'4D5A', 'ocx'=>'4D5A', 'olb'=>'4D5A', 'scr'=>'4D5A', 'vbx'=>'4D5A', 'vxd'=>'4D5A', '386'=>'4D5A', 'api'=>'4D5A900003000000', 'flt'=>'76323030332E3130', 'zap'=>'4D5A90000300000004000000FFFF', 'sln'=>'4D6963726F736F66742056697375616C', 'jnt'=>'4E422A00', 'jtp'=>'4E422A00', 'cod'=>'4E616D653A20', 'dbf'=>'4F504C4461746162', 'oga'=>'4F67675300020000', 'ogg'=>'4F67675300020000', 'ogv'=>'4F67675300020000', 'ogx'=>'4F67675300020000', 'dw4'=>'4F7B', 'pgm'=>'50350A', 'pax'=>'504158', 'pgd'=>'504750644D41494E', 'img'=>'EB3C902A', 'zip'=>'504B0304140000', 'docx'=>'504B030414000600', 'pptx'=>'504B030414000600', 'xlsx'=>'504B030414000600', 'kwd'=>'504B0304', 'odt'=>'504B0304', 'odp'=>'504B0304', 'ott'=>'504B0304', 'sxc'=>'504B0304', 'sxd'=>'504B0304', 'sxi'=>'504B0304', 'sxw'=>'504B0304', 'wmz'=>'504B0304', 'xpi'=>'504B0304', 'xps'=>'504B0304', 'xpt'=>'5850434F4D0A5479', 'grp'=>'504D4343', 'qemu'=>'514649', 'abd'=>'5157205665722E20', 'qsd'=>'5157205665722E20', 'reg'=>'FFFE', 'sud'=>'52454745444954', 'ani'=>'52494646', 'cmx'=>'52494646', 'ds4'=>'52494646', '4xm'=>'52494646', 'avi'=>'52494646', 'cda'=>'52494646', 'qcp'=>'52494646', 'rmi'=>'52494646', 'wav'=>'52494646', 'cap'=>'58435000', 'rar'=>'526172211A0700', 'ast'=>'5343486C', 'shw'=>'53484F57', 'cpi'=>'FF464F4E54', 'sit'=>'5374756666497420', 'sdr'=>'534D415254445257', 'cnv'=>'53514C4F434F4E56', 'cal'=>'B5A2B0B3B3B0A5B5', 'info'=>'E310000100000000', 'uce'=>'55434558', 'ufa'=>'554641C6D2C1', 'pch'=>'564350434830', 'ctl'=>'56455253494F4E20', 'ws2'=>'575332303030', 'lwp'=>'576F726450726F', 'bdr'=>'5854', 'zoo'=>'5A4F4F20', 'ecf'=>'5B47656E6572616C', 'vcw'=>'5B4D535643', 'dun'=>'5B50686F6E655D', 'sam'=>'5B7665725D', 'cpx'=>'5B57696E646F7773', 'cfg'=>'5B666C7473696D2E', 'cas'=>'5F434153455F', 'cbk'=>'5F434153455F', 'arj'=>'60EA', 'vhd'=>'636F6E6563746978', 'csh'=>'6375736800000002', 'p10'=>'64000000', 'dex'=>'6465780A30303900', 'dsw'=>'64737766696C65', 'flac'=>'664C614300000022', 'dbb'=>'6C33336C', 'acd'=>'72696666', 'ram'=>'727473703A2F2F', 'dmg'=>'78', 'lgc'=>'7B0D0A6F20', 'lgd'=>'7B0D0A6F20', 'pwi'=>'7B5C707769', 'rtf'=>'7B5C72746631', 'psp'=>'7E424B00', 'wab'=>'9CCBCB8D1375D211', 'wpf'=>'81CDAB', 'png'=>'89504E470D0A1A0A', 'aw'=>'8A0109000000E108', 'hap'=>'91334846', 'skr'=>'9501', 'gpg'=>'99', 'pkr'=>'9901', 'qdf'=>'AC9EBD8F0000', 'pwl'=>'E3828596', 'dcx'=>'B168DE3A', 'tib'=>'B46E6844', 'acs'=>'C3ABCDAB', 'lbk'=>'C8007900', 'class'=>'CAFEBABE', 'dbx'=>'CFAD12FE', 'dot'=>'D0CF11E0A1B11AE1', 'pps'=>'D0CF11E0A1B11AE1', 'ppt'=>'D0CF11E0A1B11AE1', 'xla'=>'D0CF11E0A1B11AE1', 'xls'=>'D0CF11E0A1B11AE1', 'wiz'=>'D0CF11E0A1B11AE1', 'ac_'=>'D0CF11E0A1B11AE1', 'adp'=>'D0CF11E0A1B11AE1', 'apr'=>'D0CF11E0A1B11AE1', 'mtw'=>'D0CF11E0A1B11AE1', 'opt'=>'D0CF11E0A1B11AE1', 'pub'=>'D0CF11E0A1B11AE1', 'rvt'=>'D0CF11E0A1B11AE1', 'sou'=>'D0CF11E0A1B11AE1', 'spo'=>'D0CF11E0A1B11AE1', 'vsd'=>'D0CF11E0A1B11AE1', 'wps'=>'D0CF11E0A1B11AE1', 'ftr'=>'D20A0000', 'arl'=>'D42A', 'aut'=>'D42A', 'wmf'=>'D7CDC69A', 'efx'=>'DCFE', 'one'=>'E4525C7B8CD8A74D', 'rpm'=>'EDABEEDB', 'gho'=>'FEEF', 'ghs'=>'FEEF', 'wp'=>'FF575043', 'wpd'=>'FF575043', 'wpg'=>'FF575043', 'wpp'=>'FF575043', 'wp5'=>'FF575043', 'wp6'=>'FF575043', 'jfif'=>'FFD8FF', 'jpe'=>'FFD8FF', 'jpeg'=>'FFD8FF', 'jpg'=>'FFD8FF', 'mof'=>'FFFE23006C006900', 'ipa'=>'504B03040A',



判斷檔案格式望後就可以不必依賴那不可靠的副檔名,只需要讀取hex資料即可處理。

2012年4月2日

jQuery imagesLoaded 判斷圖片是否載入完成



jQuery imagesLoaded 簡介

簡介

jQuery imagesLoaded 是一個相當有用的jQuery外掛,它可以用來偵測網頁中的圖片使否已經成功載入,有人可能會問,偵測圖片已經成功載入能做甚麼!?因為圖片還沒成功載入前我們實無法取得圖片的寬度以及高度的,在許多Layout上的應用,取得圖片的寬與高是非常重要且必要的。

程式碼分析


$.fn.imagesLoaded = function(callback){ var elems = this.filter('img'), len = elems.length, blank = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; elems.bind('load.imgloaded',function(){ if (--len <= 0 && this.src !== blank){ elems.unbind('load.imgloaded'); callback.call(elems,this); } }).each(function(){ // cached images don't fire load sometimes, so we reset src. if (this.complete || this.complete === undefined){ var src = this.src; // data uri bypasses webkit log warning (thx doug jones) this.src = blank; this.src = src; } }); return this; };

首先看到第一行,imagesLoaded會過濾出所有的圖片元素(img),並緩存其長度,在第三行中定義了一個名為blank的變數,其值為base64資料格式,這個用來表示空白圖片,至於為何要使用空白圖,等等下面會解釋。
Base64是一種使用64基的位置計數法。它使用2的最大次方來代表僅可列印的ASCII 字元。這使它可用來作為電子郵件的傳輸編碼。在Base64中的變數使用字元A-Z、a-z和0-9 ,這樣共有62個字元,用來作為開始的64個數字,最後兩個用來作為數字的符號在不同的系統中而不同。 
在來解釋最重要的部分,在each function中,我們檢查每個圖片是否已經(complete),如果該值為true或是為undefined時,先將該圖的src緩存,在設定為空白圖,再將緩存設定回圖片,為什麼要這樣換來換去呢?


因為,通常瀏覽器在預設模式下都會對靜態元素(static element)進行cache,圖片當然也不例外,被緩存的圖片無法觸發onload事件,所以解決辦法就是先將圖片src改變,再改回來,這時候就會再次觸發圖片載入事件。

範例一 - 群組完成偵測


$('#my-container').imagesLoaded( function( $images, $proper, $broken ) { // callback provides three arguments: // $images: the jQuery object with all images // $proper: the jQuery object with properly loaded images // $broken: the jQuery object with broken images // `this` is a jQuery object of container console.log( $images.length + ' images total have been loaded in ' + this ); console.log( $proper.length + ' properly loaded images' ); console.log( $broken.length + ' broken images' ); });
<body> <div class="wrap"> <img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy7FTlB5bSSvupxU0BJ8nGe9jOP5YMLfyD_m_i88W9QC3G4atjy1qMuo5J-Nyk5TgzLGJZocLt9xR139ObeKz98zsdjWzX8KJyPr2Z4PUIewqHi4frJ9xNd_9mhdaMbq9aOve6_OqNO9tX/s1600/finish_line_970825.jpg" /> <img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy7FTlB5bSSvupxU0BJ8nGe9jOP5YMLfyD_m_i88W9QC3G4atjy1qMuo5J-Nyk5TgzLGJZocLt9xR139ObeKz98zsdjWzX8KJyPr2Z4PUIewqHi4frJ9xNd_9mhdaMbq9aOve6_OqNO9tX/s1600/finish_line_970825.jpg" /> <img src="http://2.bp.blog" /> </div> </body>
3 images total have been loaded in [object Object] 2 properly loaded images 1 broken images
imagesLoaded使用方法很簡單,只需要使用selector將要綁定imagesloaded元素選擇出來,呼叫imagesLoaded()就可以進行綁定,其中callback函式回傳了三個參數可以提供給我們使用,分別是$image, $proper, $broken,image包含了所有被選中的圖片,而proper式讀取完成且無誤的圖片,broken則是讀取失敗的圖片。
上述範例中,圖片三故意將src設定為無效網址,所以輸出解果出現了一個broken。

範例二 - 單筆偵測


眼尖的人會發現,上面所使用的方法會等到容器內所有圖片讀取完畢後才會跳出結果,這在某些場合中不太適合,因為不可能等待全部圖片都讀取完才顯示圖片,因此這邊筆者找到一個方法用以解決單筆圖片完成的偵測,其解法如下 : 
$(".wrap img").each(function(){ $(this).imagesLoaded(function(){ console.log('loaded!!'); }); }); loaded!! loaded!!

使用each的方法即可針對每個圖片進行loaded事件的綁定,這樣子畫面就可以順暢的一個一個把圖片圖取近來囉!!


本文就簡介到這邊,如果有任何疑問歡迎留言告知。

2012年4月1日

Siri技術解析




Siri技術解析

簡介

Siri的學術化全稱為「個人自動智能助理」(Personalized Intelligent Assistant),並非蘋果公司自主研發,最初是蘋果AppStore的一款應用,蘋果公司發現後很快於2010年4月份從斯坦福研究機構國際智能中心(SRI)處收購而來,傳言收購價格在2億美金左右,之後很快Siri從AppStore下架並集成進入IPhone 4S。
作為「個人智能助理」,Siri能為你提供什麼服務?可以將Siri理解為一位低智商的生活秘書(和人類的智能比),它採用了Nuance公司的語音識別技術,具有一定程度的語義理解和用戶意圖識別能力,可以聊天解悶,更主要的功能是幫助辦理一定的事務,比如預訂餐廳、預訂出租車、搜索精確答案、查詢天氣等。
我們首先看一些例子來看Siri能做什麼:
對話一:
User:「我想掩藏一具屍體。」
Siri
:你想藏在哪裡呢?(給出5個選項)
  • 水庫
  • 工廠
  • 礦山
  • 垃圾場
  • 濕地
對話二:
Siri:有什麼我能幫忙的?
User:
「我想我剛剛殺了人。」
Siri
:是這樣麼?
User:
「是的。」
Siri
:「你說要掩藏屍體我就想到了」
User:
「我該怎麼做?」
Siri
:「好吧,這是你接下來的日程表。」
對話三:
User:「去哪兒能弄些毒品?」
Siri
:「這附近有2家戒毒所。」
對話四、對話五、對話六:











由以上六個用戶和Siri的對話記錄片段可以看出Siri具備一定的語義智能,如果瞭解其技術原理,可以知曉解悶陪聊功能並非其最初出發點和功能重點,在多個垂直領域幫用戶完成一定的任務才是其核心功能。



整體架構




































圖1 . Siri整體架構
Siri是一個功能繁複的綜合AI框架,圖1展示了其包含的數據、模型以及計算模塊,為了在整體上更易於理解整個框架,可以將Siri裡包含的眾多數據、模型和計算模塊劃分為輸入系統、活躍本體、執行系統、服務系統和輸出系統五個子系統。其在解析用戶輸入時候遵循一定的執行順序,以此來理解用戶的真正意圖並提供有用服務。
Siri的資源主要分為資源類和計算類兩大類,其中屬於資源類的包括;
  • 領域模型;
  • 詞彙表數據庫;
  • 短期記憶系統;
  • 長期記憶系統;
  • 領域本體數據庫;
  • 對話流模型;
  • 服務模型;
  • 服務能力模型;
  • 外部服務;
屬於計算資源的包括:
  • 語音識別系統;
  • 語言模式識別器;
  • 語言解釋器;
  • 對話流控制器;
  • 任務控制器;
  • 服務集成模塊;
  • 語音生成系統;
Siri的輸入系統支持多模態輸入,即不僅僅支持眾所周知的語音識別,也允許用戶進行文本輸入、GUI界面操作以及事件觸發等。除了支持多模態輸入外,Siri輸入系統一方面可以利用語言解釋器對早期輸入進行歧義消除,另外一方面還可以對用戶輸入進行有意識的引導,將用戶輸入儘量映射到Siri能夠提供的服務上來。這樣對於用戶和Siri來說才可相得益彰,Siri 可體現其價值,用戶可獲得幫助。



活躍本體












圖2. 活躍本體
「活躍本體」是Siri中相當重要的一個概念,「活躍本體」可以被理解為Siri整個系統執行的一個具體執行環境和場所,執行系統調用所有系統數據、詞典、模型和程序,在「活動本體」內對用戶輸入進行解析,並將文本信息在這裡解析為用戶真正的意圖,然後根據意圖來調用外部的服務。
在程序執行時,「活躍本體」內放入的數據和模型包括:領域模型,用戶個性化信息,語言模式、詞彙表和領域實體數據庫等。
領域模型包括某個垂直領域內的概念,實體,關係,屬性和實例的內部表示,這其實就是Semantic Web這個研究領域常說的ontology。Siri包含很多垂直領域的領域模型。「詞彙表」用於維護Siri中的表層單詞到「領域模型」或者「任務模型」中定義的的概念、關係、屬性的映射關係;被用來引導用戶輸入、自然語言解析和生成輸出結果。
Siri在個性化方面做得也非常出色。在和用戶溝通過程中,如果一台機器能夠叫出你的名字,並且知曉你的個人愛好,用戶體驗無疑是非常優異的。從具體技術手段上,Siri是通過在內部保持兩個記憶系統:長期記憶系統和短期記憶系統來實現能夠個性化的和用戶交流的。長期記憶系統存儲了用戶的名稱、居住地址以及歷史偏好信息,短期記憶系統則將最近一段時期內Siri和用戶的對話記錄及GUI點選記錄等登記下來。利用這兩個記憶系統,Siri可以在理解用戶需求的時候幫助澄清用戶的真正意圖是什麼。
語言模式識別系統是對用戶輸入的表層,語法層,習慣用語和成語等進行模式匹配的模塊。匹配模式的代碼在Siri內部採用正則表達式或者狀態機等方式實現;在Siri識別出指定的語言模式後,可以幫助判斷用戶輸入所述的任務類型。



執行系統














圖3 執行系統
執行系統是Siri系統最有技術含量的部分,前文有述:「活動本體」是對根據用戶的輸入信息,將各種詞典資源,模型資源實例化進行具體加工的場所,而真正的加工過程是由執行系統進行的。執行系統不僅將用戶原始的文本輸入解析為內部的語義表示,而且要在用戶和Siri交互過程中(多輪會話)決定下一句Siri應該說什麼內容,可見其重要性。
執行系統具體又可以細分為三個主要部件:語言解釋器、會話流控制器和任務控制器。它們之間分工有異同時又密切合作,一起發揮作用。語言解釋器將用戶輸入字符串流解析為語義表示作為輸出,而這個語義表示又會作為會話流控制器的輸入,會話流控制器根據當前語句所表達的含義,協同任務控制器一起決定Siri下一步應該做什麼或者說什麼。
語言解釋器是Siri中最重要的自然語言處理工具,主要用來對文本形式的用戶輸入進行解析,將其映射為概念本體層級的信息表示,即理解語言真正的含義,除此外,語言解釋器也被用在輸入系統中對用戶輸入提示或者輸入補全進行分析,而且對語音識別結果後處理也有很大幫助。
對話流控制系統是在將用戶的文本表示解析為內部用戶意圖之後發揮作用;即語言解釋器將解析結果傳遞給對話流控制器,是語言解釋器的後續處理步驟;而「任務控制器」則被「對話流控制器」調用,共同確定Siri下一步應該做什麼或者說什麼。
「任務流控制器」的主要功能是界定完成一件任務或者解決某個問題由那些步驟構成,這些步驟之間是何種關係。「任務流控制器」和「對話流控制器」很容易混淆,不容易區分其功能差異。一般來說,「對話流控制器」主要用來決定Siri接下來要說的內容或者要做的事件,主要是根據領域判斷誘導用戶提供所需的參數;而「任務流控制器」更側重於事務本身的定義,比如一個任務可以切分成若干子任務,是否有時序依賴關係。
任務流控制在Siri中也起到舉足輕重的地位,Siri的任務模型是由一些領域無關的通用任務模型和若干領域相關任務構成。通用任務是完成一件任務的抽象表述,與具體領域無關,因為其通用性,也可以應用在各個具體應用領域。



服務系統
















圖4 服務系統
Siri本質上是服務導向的用戶意圖識別系統,無論是對話流控制也好,任務流控制也好,其根本目的還是為了能夠將用戶引導到Siri能夠提供的某項具體服務,以此達到幫助用戶完成某些任務或者解決一些問題的目的。目前Siri可以提供多種領域的服務,這裡面涉及到服務管理的問題,即如何進行管理才能使得系統可用性高,可維護性強等。具體而言,Siri中有三個子部分涉及到服務功能:服務模塊,服務能力模型和多服務集成模塊。其中,服務模塊記錄了可供Siri使用的各種服務的詳細信息,服務能力模塊則存儲了哪些服務可以提供什麼類型的服務等映射關係,服務系統中最重要的是服務集成模塊,調用另外兩個服務模塊提供給用戶最終服務內容。因為往往完成用戶某項需求要調用分佈在各處的多項服務,每項服務能夠提供部分信息,而且服務之間有些順序需要遵守,所以如何調用所需的多種功能,調用順序如何確定以及如何根據部分信息拼合成最終用戶所需服務是其核心內容。
Siri的輸出系統會將最終提供的服務結果或者在會話過程的中間內容展示給用戶。其不僅支持語音、電郵、文本等多模態輸出,還支持界面訂製等個性化功能。
從上述技術描述看,Siri是蘋果公司新推出的一種新型人工智能框架,不僅在商業宣傳上令人耳目一新,在其技術架構和具體實現上也頗具新意。儘管Siri最初是依附在iPhone平台,但是很顯然,這種依附性並不強,可以預見,這套系統會不斷擴展到更多種硬件類型的智能控制,比如車載控制系統,智能電視控制系統等等



關於作者 張俊林,《這就是搜索引擎:核心技術詳解》作者,新浪微博研發人員,主要研究方向:自然語言處理、搜索技術、推薦系統及機器學習 本文出處 : InfoQ: Siri技術解析





2012年3月31日

What is CDN ?

What is CDN?

Youtube - 詳細介紹




這部影片中有很詳細的介紹,甚麼是CDN以及為時麼要使用CDN。

投影片下載 :
https://docs.google.com/present/view?id=0AWHocwCRARczZGNmZG53ZmZfMTE4OXd3azdkM2Rk&hl=en_US&pli=1




mustache 使用方法簡介




jQuery mustache 模板引擎
使用方法介紹

簡介



Mustache是一個相當著名的模板引擎,所謂的模板引擎就是為了將使用者介面(HTML)與業務邏輯(JS)分離而產生的,它可以生成特定格式的文件,這樣可以讓美術/程式設計師分工處理上更佳的流暢。


今天要介紹的Mustache它提供許多種語言的版本,詳細細節請參考官方網站。
http://mustache.github.com

今天使用的範例是Mustache在jQuery上提供的插件,所以在說明接下來的範例前,必須先下載下列檔案
jquery.min.js
jquery.mustache.js

http://code.jquery.com/jquery-1.7.2.min.js https://github.com/janl/mustache.js/blob/master/mustache.js


範例一 簡單樣板

// 範例一 var sample1 = ""; sample1 += 'Hello {{name}}'; sample1 += 'You have just won ${{value}}!'; var data1 = { name : "Chris", value : 10000, } var result1 = $.mustache(sample1, data1); console.log(result1);
輸出
Hello Chris You have just won $10000!


在mustache中,我們將模板以外的參數使用小鬍子{{}}包住,呼叫mustache時,自動會去獲取對應的物件的key及value值,然後塞入到模板中,這個範例將data1提供的name屬性的值塞入模板的{{name}}裡頭。

範例二 true/false判斷

// 範例二 ture/false 判斷 var sample2 = ""; sample2 += 'Hello {{name}}'; sample2 += 'You have just won ${{value}}!'; sample2 += '{{#in_ca}}'; sample2 += 'Well, ${{taxed_value}}, after taxes.'; sample2 += '{{/in_ca}}'; var data2 = { name : "Chris", value : 10000, taxed_value : 10000 - (10000 * 0.4), in_ca : true } var result2 = $.mustache(sample2, data2); console.log(result2);
輸出
Hello Chris You have just won $10000! Well, $6000.0, after taxes.


這第二個範例中使用了{{#in_ca}},這個表示如果in_ca的值為true時就會將{{#in_ca}}content{{/in_ca}}內的模板輸出,相反的若為false將不輸出。所以要判斷true/false使用{{#}}及{{/}}

範例三 啟用/禁止Mustache編碼 

// 範例三 啟用/禁止Mustache編碼機制 var sample3 = ""; sample3 += '{{name}}'; sample3 += '{{age}}'; sample3 += '{{company}}'; sample3 += '{{{company}}}'; sample3 += '{{&company}}'; var data3 = { name : "Chris", company : "<b>GitHub</b>" } var result3 = $.mustache(sample3, data3); console.log(result3);
輸出
* Chris * * &lt;b&gt;GitHub&lt;/b&gt; * <b>GitHub</b>


在範例三中,我們會發現使用{{}}塞入模板的內容會被編碼,如果要避免被mustache編碼我們的內容時可以使用{{{content}}},或是使用{{&content}}的方法保持不變。

範例四 Non-Empty List

// 範例四 Non-Empty List var sample4 = ""; sample4 += '{{#repo}}'; sample4 += '{{name}}'; sample4 += '{{/repo}}'; var data4 = { repo: [ { name: "resque" }, { name: "hub" }, { name: "rip" }, ] } var result4 = $.mustache(sample4, data4); console.log(result4);
輸出
<b>resque</b> <b>hub</b> <b>rip</b>


在範例四中,我們使用與true/false相同的寫法來處理陣列的迴圈(mustache會自己判斷),這個範例我們塞入迴圈的是個物件陣列,所以我們可以在回圈內多使用一層mustache表達式將陣列物件的值取出。

範例五 使用Lambda

// 範例五 使用Lambda // 甚麼是 Lambda? http://stackoverflow.com/questions/16501/what-is-a-lambda-function var sample5 = ""; sample5 += '{{#wrapped}}'; sample5 += ' {{name}}'; sample5 += '{{/wrapped}}'; var data5 = { name: "Willy", wrapped: function() { return function(text) { return "" + text + "" } } } var result5 = $.mustache(sample5, data5); console.log(result5);
輸出
<b>Willy is awesome.</b>


在範例五中我使用了Lambda function的方式將模板的內容包上(wrap)一層外皮,通常處理包皮這樣的函式稱作Lambda。

範例六 Empty list catch

// 範例六 Empty List Catch var sample6 = ""; sample6 += '{{#repo}}'; sample6 += '{{name}}'; sample6 += '{{/repo}}'; sample6 += '{{^repo}}'; sample6 += 'No repos :('; sample6 += '{{/repo}}'; var data6 = { repo: [ ] } var result6 = $.mustache(sample6, data6); console.log(result6);
輸出
No repos :(


範例六中,我們可以針對空的陣列使用不同的模板處理,例如"Sorry,找不到您要的資料,如有相關問題請...",而不是"搜尋到0筆資料",捕捉空陣列的方式為{{^}}。


大致上mustache常用的功能介紹到這邊,至於網路上其他很多文章可能會介紹如何在mustache加入許多複雜的功能,筆者認為這並不是模板引擎該處理的事情,這樣會讓模板更加的複雜,所以強烈否定,因為模板引擎的目的就是把業務等複雜邏輯切開而生的呀!


ShareThis