日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
示例:JavaScript中的后續(xù)傳遞風(fēng)格

現(xiàn)在,CPS作為非阻塞式(通常是分布式的)系統(tǒng)的編程風(fēng)格而被再次發(fā)掘出來(lái)。

10多年專(zhuān)注成都網(wǎng)站制作,成都定制網(wǎng)頁(yè)設(shè)計(jì),個(gè)人網(wǎng)站制作服務(wù),為大家分享網(wǎng)站制作知識(shí)、方案,網(wǎng)站設(shè)計(jì)流程、步驟,成功服務(wù)上千家企業(yè)。為您提供網(wǎng)站建設(shè),網(wǎng)站制作,網(wǎng)頁(yè)設(shè)計(jì)及定制高端網(wǎng)站建設(shè)服務(wù),專(zhuān)注于成都定制網(wǎng)頁(yè)設(shè)計(jì),高端網(wǎng)頁(yè)制作,對(duì)加固等多個(gè)行業(yè),擁有多年建站經(jīng)驗(yàn)。

我對(duì)CPS很有好感,因?yàn)樗俏耀@取博士學(xué)位的一個(gè)秘密武器。它十有八九幫我消減掉了一兩年的時(shí)間和一些難以估量的痛苦。

本文介紹了CPS所扮演的兩種角色——作為JavaScript中的一種非阻塞編程風(fēng)格,以及作為一種功能性語(yǔ)言的中間形式(簡(jiǎn)要介紹)。

內(nèi)容包括:

◆JavaScript中的CPS

◆CPS用于Ajax編程

◆用在非阻塞式編程(node.js)中的CPS

◆CPS用于分布式編程

◆如何使用CPS來(lái)實(shí)現(xiàn)異常

◆極簡(jiǎn)Lisp的一個(gè)CPS轉(zhuǎn)換器

◆如何用Lisp實(shí)現(xiàn)call/cc

◆如何用JavaScript實(shí)現(xiàn)call/cc

請(qǐng)往下閱讀以了解更多內(nèi)容。

什么是持續(xù)傳送風(fēng)格?

如果一種語(yǔ)言支持后續(xù)(continuation)的話,編程者就可以添加諸如異常、回溯、線程以及構(gòu)造函數(shù)一類(lèi)的控制構(gòu)造。

可惜的是,許多關(guān)于后續(xù)的解釋(我的也包括在內(nèi))給人的感覺(jué)是含糊不清,令人難以滿意。

后續(xù)傳遞風(fēng)格是那么的基礎(chǔ)。

后續(xù)傳遞風(fēng)格賦予了后續(xù)在代碼方面的意義。

更妙的是,編程者可以自我發(fā)掘出后續(xù)傳遞風(fēng)格來(lái),如果其受限于下面這樣的一個(gè)約束的話:

沒(méi)有過(guò)程被允許返回到它的調(diào)用者中——永遠(yuǎn)如此。

存在的一個(gè)啟示使得以這種風(fēng)格編程成為可能:

過(guò)程可以在它們返回值時(shí)調(diào)用一個(gè)回調(diào)方法。

當(dāng)一個(gè)過(guò)程(procedure)準(zhǔn)備要“返回”到它的調(diào)用者中時(shí),它在返回值時(shí)調(diào)用“當(dāng)前后續(xù)(current continuation)”這一回調(diào)方法(由它的調(diào)用者提供)

一個(gè)后續(xù)是一個(gè)初始類(lèi)型(first-class)返回點(diǎn)。

例子:標(biāo)識(shí)函數(shù)

考慮這個(gè)正常寫(xiě)法的標(biāo)識(shí)函數(shù):

 
 
 
  1. function id(x) {    
  2.          return x ;  

然后是后續(xù)傳遞風(fēng)格的:

 
 
 
  1. function id(x,cc) {  
  2.    cc(x) ;  

有時(shí)候,把當(dāng)前后續(xù)參數(shù)命名為ret會(huì)使得其目的更為明顯一些:

 
 
 
  1. function id(x,ret) {  
  2.    ret(x) ;  

例子:樸素階乘

下面是標(biāo)準(zhǔn)的樸素階乘:

 
 
 
  1. function fact(n) {  
  2.    if (n == 0)  
  3.      return 1 ;  
  4.    else 
  5.     return n * fact(n-1) ;  

下面是CPS風(fēng)格實(shí)現(xiàn)的:

 
 
 
  1. function fact(n,ret) {  
  2.    if (n == 0)  
  3.      ret(1) ;  
  4.    else 
  5.     fact(n-1, function (t0) {  
  6.       ret(n * t0) }) ;  

接下來(lái),為了“使用”這一函數(shù),我們把一個(gè)回調(diào)方法傳給它:

 
 
 
  1. fact (5, function (n) {  
  2.     console.log(n) ; // 在Firebug中輸出120  
  3. })   

例子:尾遞歸階乘

下面是尾遞歸階乘:

 
 
 
  1. function fact(n) {  
  2.    return tail_fact(n,1) ;  
  3. }    
  4. function tail_fact(n,a) {  
  5.    if (n == 0)  
  6.      return a ;  
  7.    else 
  8.      return tail_fact(n-1,n*a) ;  

然后,是CPS實(shí)現(xiàn)方式的:

 
 
 
  1. function fact(n,ret) {  
  2.    tail_fact(n,1,ret) ;  
  3. }     
  4. function tail_fact(n,a,ret) {  
  5.    if (n == 0)  
  6.      ret(a) ;  
  7.    else 
  8.      tail_fact(n-1,n*a,ret) ;  
  9. }    

CPS和Ajax

Ajax是一種web編程技術(shù),其使用JavaScript中的一個(gè)XMLHttpRequest對(duì)象來(lái)從服務(wù)器端(異步地)提取數(shù)據(jù)。(提取的數(shù)據(jù)不必是XML格式的。)CPS提供了一種優(yōu)雅地實(shí)現(xiàn)Ajax編程的方式。使用XMLHttpRequest,我們可以寫(xiě)出一個(gè)阻塞式的過(guò)程fetch(url),該過(guò)程抓取某個(gè)url上的內(nèi)容,然后把內(nèi)容作為串返回。這一方法的問(wèn)題是,JavaScript是一種單線程語(yǔ)言,當(dāng)JavaScript阻塞時(shí),瀏覽器就被暫時(shí)凍結(jié),不能動(dòng)彈了。這會(huì)造成不愉快的用戶(hù)體驗(yàn)。一種更好的做法是這樣的一個(gè)過(guò)程fetch(url, callback),其允許執(zhí)行(或是瀏覽器呈現(xiàn)工作)的繼續(xù),并且一旦請(qǐng)求完成就調(diào)用所提供的回調(diào)方法。在這種做法中,部分CPS轉(zhuǎn)換變成了一種自然的編碼方式。

實(shí)現(xiàn)fetch

實(shí)現(xiàn)fetch過(guò)程并不難,至于其以非阻塞模式或是阻塞模式操作則取決于編程者是否提供回調(diào)方法:

 
 
 
  1. /*  
  2.  對(duì)于客戶(hù)端—>服務(wù)器端的請(qǐng)求來(lái)說(shuō),  
  3.  fetch是一個(gè)可選阻塞的過(guò)程。  
  4.    
  5.  只有在給出url的情況下,過(guò)程才會(huì)阻塞并返回該url上的內(nèi)容。  
  6.    
  7.  如果提供了onSuccess回調(diào)方法,  
  8.  則過(guò)程是非阻塞的,并使用文件的  
  9.  內(nèi)容來(lái)調(diào)用回調(diào)方法。  
  10.    
  11.  如果onFail回調(diào)方法也提供了的話,  
  12.  則過(guò)程在失敗事件出現(xiàn)時(shí)調(diào)用onFail。  
  13.    
  14. */ 
  15.    
  16. function fetch (url, onSuccess, onFail) {  
  17.      // 只有在定義回調(diào)方法的情況下才是異步的  
  18.     var async = onSuccess ? true : false ;   // (別抱怨此行代碼的效率低下,    
  19.                                                                                   
  20.    // 否則你就是不明白關(guān)鍵所在。)  
  21.      var req ; // XMLHttpRequest對(duì)象.  
  22.    
  23. // XMLHttpRequest的回調(diào)方法:  
  24.    function processReqChange() {  
  25.      if (req.readyState == 4) {  
  26.        if (req.status == 200) {  
  27.          if (onSuccess)  
  28.             onSuccess(req.responseText, url, req) ;  
  29.         } else {  
  30.          if (onFail)  
  31.             onFail(url, req) ;  
  32.        }  
  33.      }  
  34.    }  
  35.    
  36. // 創(chuàng)建XMLHttpRequest對(duì)象:  
  37.    if (window.XMLHttpRequest)  
  38.       req = new XMLHttpRequest();  
  39.    else if (window.ActiveXObject)  
  40.       req = new ActiveXObject("Microsoft.XMLHTTP");  
  41.    
  42. // 如果是異步的話,設(shè)定回調(diào)方法:  
  43.    if (async)  
  44.       req.onreadystatechange = processReqChange;  
  45.    
  46. // 發(fā)起請(qǐng)求:  
  47.    req.open("GET", url, async);  
  48.    req.send(null);  
  49.    
  50. // 如果是異步的話,  
  51. // 返回請(qǐng)求對(duì)象,否則  
  52. //  返回響應(yīng).  
  53.    if (async)  
  54.       return req ;  
  55.    else 
  56.     return req.responseText ;  

例子:提取數(shù)據(jù)

考慮一個(gè)程序,該程序需要從UID中抓取一個(gè)名字

下面的兩種做法都要用到fetch:

 
 
 
  1. // 阻塞直到請(qǐng)求完成:  
  2.  var someName = fetch("./1031/name") ;  
  3.    
  4.  document.write ("someName: " + someName + "  
  5. ") ; 

 
 
 
  1. //不做阻塞的:  
  2.  fetch("./1030/name", function (name) {  
  3.   document.getElementById("name").innerHTML = name ;  
  4.  }) ;  
  5.   

CPS和非阻塞式編程

node.js是一個(gè)高性能的JavaScript服務(wù)器端平臺(tái),在該平臺(tái)上阻塞式過(guò)程是不允許的。

巧妙的是,通常會(huì)阻塞的過(guò)程(比如網(wǎng)絡(luò)或是文件I/O)利用了通過(guò)結(jié)果來(lái)調(diào)用的回調(diào)方法。

對(duì)程序做部分CPS轉(zhuǎn)換促成了自然而然的node.js編程。

#p#

例子:簡(jiǎn)單的web服務(wù)器

node.js中的一個(gè)簡(jiǎn)單的web服務(wù)器把一個(gè)后續(xù)傳遞給文件讀取過(guò)程。相比于非阻塞式IO的基于select的方法,CPS使非阻塞I/O變得更加的簡(jiǎn)單明了。

 
 
 
  1. var sys = require('sys') ;  
  2. var http = require('http') ;  
  3. var url = require('url') ;  
  4. var fs = require('fs') ;  
  5.    
  6. // Web服務(wù)器的根目錄:  
  7.  var DocRoot = "./www/" ;  
  8.    
  9. // 使用一個(gè)處理程序回調(diào)來(lái)創(chuàng)建web服務(wù)器:  
  10.  var httpd = http.createServer(function (req, res) {  
  11.    sys.puts(" request: " + req.url) ;  
  12.           
  13.          // 解析url:  
  14.    var u = url.parse(req.url,true) ;  
  15.    var path = u.pathname.split("/") ;  
  16.    
  17.          // 去掉路徑中的..:  
  18.    var localPath = u.pathname ;  
  19.    //  "  
  20. /.." => ""  
  21.    var localPath =  
  22.         localPath.replace(/[^/]+\/+[.][.]/g,"") ;  
  23.    //  ".." => "."  
  24.    var localPath = DocRoot +  
  25.                      localPath.replace(/[.][.]/g,".") ;  
  26.    
  27.    // 讀入被請(qǐng)求的文件,并把它發(fā)送回去.  
  28.    // 注:readFile用到了當(dāng)前后續(xù)(current continuation):  
  29.    fs.readFile(localPath, function (err,data) {  
  30.      var headers = {} ;  
  31.    
  32.      if (err) {  
  33.        headers["Content-Type"] = "text/plain" ;  
  34.        res.writeHead(404, headers);  
  35.        res.write("404 File Not Found\n") ;  
  36.        res.end() ;  
  37.      } else {  
  38.        var mimetype = MIMEType(u.pathname) ;  
  39.    
  40.     // 如果沒(méi)有找出內(nèi)容類(lèi)型的話,  
  41.      // 就由客戶(hù)來(lái)猜測(cè).  
  42.        if (mimetype)  
  43.          headers["Content-Type"] = mimetype ;  
  44.          res.writeHead(200, headers) ;  
  45.         
  46.             res.write(data) ;  
  47.        res.end() ;  
  48.         }  
  49.     }) ;  
  50.  }) ;  
  51.    
  52. // 映射后綴名和MIME類(lèi)型:  
  53.  var MIMETypes = {  
  54.   "html" : "text/html" ,  
  55.   "js"   : "text/javascript" ,  
  56.   "css"  : "text/css" ,  
  57.   "txt"  : "text/plain" 
  58. } ;  
  59.    
  60. function MIMEType(filename) {  
  61.   var parsed = filename.match(/[.](.*)$/) ;  
  62.   if (!parsed)  
  63.     return false ;  
  64.   var ext = parsed[1] ;  
  65.   return MIMEType[ext] ; }  
  66.    
  67. // 啟動(dòng)服務(wù)器,監(jiān)聽(tīng)端口8000:  
  68.  httpd.listen(8000) ; 

CPS用于分布式計(jì)算

CPS簡(jiǎn)化了把計(jì)算分解成本地部分和分布部分的做法。

假設(shè)你編寫(xiě)了一個(gè)組合的choose函數(shù);開(kāi)始是一種正常的方式:

 
 
 
  1. function choose (n,k) {  
  2.  return       fact(n) /  
  3.           (fact(k) * fact(n-k)) ;  
  4.  } 

現(xiàn)在,假設(shè)你想要在服務(wù)器端而不是本地計(jì)算階乘。

你可以重新把fact寫(xiě)成阻塞的并等待服務(wù)器端的響應(yīng)。

那樣的做法很糟糕。

相反,假設(shè)你使用CPS來(lái)寫(xiě)choose的話:

 
 
 
  1. function choose(n,k,ret) {  
  2.    fact (n,   function (factn) {  
  3.    fact (n-k, function (factnk) {  
  4.    fact (k,   function (factk) {  
  5.    ret  (factn / (factnk * factk)) }) }) })  
  6.   } 

現(xiàn)在,重新把fact定義成在服務(wù)器端的異步計(jì)算階乘就是一件很簡(jiǎn)單的事情了。

(有趣的練習(xí):修改node.js服務(wù)器端以讓這一做法生效。)

使用CPS來(lái)實(shí)現(xiàn)異常

一旦程序以CPS風(fēng)格實(shí)現(xiàn),其就破壞了語(yǔ)言中的普通的異常機(jī)制。 幸運(yùn)的是,使用CPS來(lái)實(shí)現(xiàn)異常是一件很容易的事情。

異常是后續(xù)的一種特例。

通過(guò)把當(dāng)前異常后續(xù)(current exceptional continuation)與當(dāng)前后續(xù)一起做傳遞,你可以實(shí)現(xiàn)對(duì)try/catch代碼塊的脫糖處理。

考慮下面的例子,該例子使用異常來(lái)定義階乘的一個(gè)“完全”版本:

 
 
 
  1. function fact (n) {  
  2.    if (n < 0)  
  3.      throw "n < 0" ;  
  4.    else if (n == 0)  
  5.      return 1 ;  
  6.    else 
  7.      return n * fact(n-1) ; }  
  8.    
  9. function total_fact (n) {  
  10.    try {  
  11.      return fact(n) ;  
  12.    } catch (ex) {  
  13.      return false ;    
  14.    }  
  15. }  
  16.    
  17. document.write("total_fact(10): " + total_fact(10)) ;  
  18. document.write("total_fact(-1): " + total_fact(-1)) ; 

通過(guò)使用CPS來(lái)添加異常后續(xù),我們就可以對(duì)throw、try和catch做脫糖處理:

 
 
 
  1. function fact (n,ret,thro) {  
  2.   if (n < 0)  
  3.     thro("n < 0")  
  4.    else if (n == 0)  
  5.     ret(1)  
  6.   else 
  7.     fact(n-1,  
  8.          function (t0) {  
  9.            ret(n*t0) ;  
  10.          },  
  11.          thro)  
  12.  }  
  13.    
  14. function total_fact (n,ret) {  
  15.    fact (n,ret,  
  16.      function (ex) {  
  17.        ret(false) ;  
  18.      }) ;  
  19.  }  
  20.    
  21. total_fact(10, function (res) {  
  22.    document.write("total_fact(10): " + res)  
  23. }) ;  
  24.    
  25. total_fact(-1, function (res) {  
  26.    document.write("total_fact(-1): " + res)  
  27. }) ; 

CPS用于編譯

三十年以來(lái),CPS已經(jīng)成為了功能性編程語(yǔ)言的編譯器的一種強(qiáng)大的中間表達(dá)形式。

CPS脫糖處理了函數(shù)的返回、異常和初始類(lèi)型后續(xù);函數(shù)調(diào)用變成了單條的跳轉(zhuǎn)指令。

換句話說(shuō),CPS在編譯方面做了許多繁重的提升工作。

把lambda演算轉(zhuǎn)寫(xiě)成CPS

lambda演算是Lisp的一個(gè)縮影,只需足夠的表達(dá)式(應(yīng)用程序、匿名函數(shù)和變量引用)來(lái)使得其對(duì)于計(jì)算是通用的。

 
 
 
  1. exp ::= (exp exp)           ; 函數(shù)應(yīng)用  
  2.       |  (lambda (var) exp)  ; 匿名函數(shù)  
  3.       |  var              ; 變量引用 

下面的Racket代碼把這一語(yǔ)言轉(zhuǎn)換成CPS:

 
 
 
  1. (define (cps-convert term cont)  
  2.   (match term  
  3.     [`(,f ,e)  
  4.      ; =>  
  5.      (let (($f (gensym 'f))  
  6.            ($e (gensym 'e)))  
  7.        (cps-convert f `(lambda (,$f)  
  8.          ,(cps-convert e `(lambda (,$e)  
  9.              (,$f ,$e ,cont))))))]  
  10.      
  11.     [`(lambda (,v) ,e)  
  12.      ; =>  
  13.      (let (($k (gensym 'k)))  
  14.        `(,cont (lambda (,v ,$k)  
  15.                  ,(cps-convert e $k))))]  
  16.      
  17.     [(? symbol?)  
  18.      ; =>  
  19.      `(,cont ,term)]))  
  20.    
  21. (define (cps-convert-program term)  
  22.   (cps-convert term '(lambda (ans) ans))) 

對(duì)于感興趣的讀者來(lái)說(shuō),Olivier Danvy有許多關(guān)于編寫(xiě)有效的CPS轉(zhuǎn)換器的文章。

使用Lisp實(shí)現(xiàn)call/cc

原語(yǔ)call-with-current-continuation(通常稱(chēng)作call/cc)是現(xiàn)代編程中最強(qiáng)大的控制流結(jié)構(gòu)。

CPS使得call/cc的實(shí)現(xiàn)成為了小菜一碟;這是一種語(yǔ)法上的脫糖:

 
 
 
  1. call/cc => (lambda (f cc) (f (lambda (x k) (cc x)) cc)) 

這一脫糖處理(與CPS轉(zhuǎn)換相結(jié)合)是準(zhǔn)確理解call/cc所做工作的最好方式。

其所實(shí)現(xiàn)的正是其名稱(chēng)所說(shuō)明的:其使用一個(gè)已經(jīng)捕捉了當(dāng)前后續(xù)的過(guò)程來(lái)調(diào)用被作為參數(shù)指定的過(guò)程。

當(dāng)捕捉了后續(xù)的過(guò)程被調(diào)用時(shí),其把計(jì)算“返回”給計(jì)算創(chuàng)建點(diǎn)。

使用JavaScript實(shí)現(xiàn)call/cc

如果有人要把JavaScript中的代碼轉(zhuǎn)寫(xiě)成后續(xù)傳遞風(fēng)格的話,call/cc有一個(gè)很簡(jiǎn)單的定義:

 
 
 
  1. function callcc (f,cc) {  
  2.   f(function(x,k) { cc(x) },cc)  

原文鏈接:http://article.yeeyan.org/view/213582/179432

【編輯推薦】

  1. 如何編寫(xiě)高質(zhì)量的JavaScript代碼
  2. 深入理解JavaScript的閉包特性
  3. 淺析JavaScript繼承方式
  4. 淺析JavaScript的寫(xiě)類(lèi)方式
  5. JavaScript跨域總結(jié)與解決辦法

分享文章:示例:JavaScript中的后續(xù)傳遞風(fēng)格
分享鏈接:http://www.dlmjj.cn/article/cdeohsi.html