摘要:過程是一個計算單元,計算是通過通訊來完成的。標題的表達式里還有一個符號,表示一個無行為的過程。一個過程的是它和外部產生行為交互的唯一方式。所以如果兩個過程需要通過一個交互,這個必須在兩個過程中都是,其中一方用于發(fā)送,另一方用于接收。
這篇文章的標題是一個π表達式,結尾是一段JavaScript代碼,和這個表達式的含義完全一致,或者說,完成了這個表達式的估值。
π演算(π calculus)是一種表達并發(fā)過程(process)的數學語言,和λ在形式上有很多類似之處;λ已經是公認的計算模型,它和圖靈機或遞歸理論(Recursion Theory)描述的計算模型是等價的,但是這個計算模型無法描述并發(fā)計算。
π沒有λ那樣的地位;它只是描述并發(fā)計算的模型之一,在計算數學領域科學家們還沒有對并發(fā)計算模型達成一致,每一種并發(fā)計算模型都強調了并發(fā)計算的某些方面的特性但還沒有一個模型成為λ那樣的經典模型可以解釋其他所有模型,或者證明其等價性;數學家們選擇使用不同的模型研究他們感興趣的并發(fā)計算特性。
π是并發(fā)計算模型中最精簡的一個,具有最小的概念元素集;它只包含兩個概念:過程(process)和通訊(channel)。過程是一個計算單元,計算是通過通訊來完成的。
“計算是通過通訊完成的”對于不熟悉數學理論的人來說不容易理解;這里也沒有更好的解釋;推薦看一點關于λ的介紹,感受一下計算是如何通過變量名binding和替換(substitution)完成的。在數學上,λ可以被encode在π里,但是不該對encode一詞產生基于其自然語言含義的聯(lián)想,如果想了解它的確切含義,請系統(tǒng)學習λ和π的相關理論。
題目是一個π表達式,也可以看作是用π演算寫下的一段程序,即把π當作一種編程語言,雖然這個表達式沒有什么實際用處,也沒有人會打算用π作為編程語言寫下實際的程序;但是在對照了題目的π表達式和文中的JavaScript代碼之后,我相信你會獲得一些新感受,關于對并發(fā)的理解,什么是描述并發(fā)的最小概念集。
這篇文章是一個起點,如果我有時間,還會逐步解釋在JavaScript里的callback,emitter,promise,async/await,等等,都不是在并發(fā)計算模型意義上的最小概念,它們更多的考慮了實際使用中最常用需求、最簡潔書寫、最容易學習等工程特性或需求,但同時也會遇到一些棘手的問題;而這篇文章,就是在探索對并發(fā)編程而言,最原始(primitive)的東西是什么。
解釋表達式和λ一樣簡單的是,在π里只有name,name表示一個channel。
標題里的π表達式(π-term)沒有包含所有的π符號,只包含了其中的一部分;解釋如下:
x(z)的意思是從channel x接收到z
x(z)和z都稱為π前綴(prefix),分別是input prefix和output prefix,是π里最重要的兩個前綴;
"."(dot)可以被理解為繼續(xù)(continuation),或者反過來理解,阻塞(blocking);它的意思是前綴必須先完成通訊,即接收或發(fā)送完成,"."之后的表達式才可以開始執(zhí)行,或者稱為估值;這是π里唯一表達順序(order)的地方;
你可以給一個表達式取一個名字,習慣上使用大寫字母P, Q, R...
例如:
如果 P = z.0,最左側的表達式就可以寫成x(z).P;
如果 P = x(z).z.0,則最左側的表達式就可以寫成P;
如果
P = x(z).z.0
Q = x
.y .0 R = y(v).v(u).0
則整個標題的表達式可以寫成 P | Q | R
有時候我們采用π.P的寫法表示一個通用的π表達式,而不關心這個表達式里的π具體是那種前綴;
當然也可以定義: U = P | Q | R,它仍然是π表達式。
每個π表達式都表達了一個過程。
"|"(vertical pipe)在π里的含義是并發(fā)組合,可以看作過程的運算符;U = P | Q | R就可以理解為過程U是由三個過程并發(fā)組成的。
π里的另一個組合過程的運算符是"+",summation,我們暫不介紹。
標題的表達式里還有一個符號0,0表示一個無行為的過程(inaction)。
Free name這一段可以在看了后面的代碼之后再回來對照理解。
Free name的含義和λ或編程語言里的定義一致;它是bound name的反義詞;bind的意思和λ也是一致的(λx);
在π里有兩個符號會bind name,標題里的表達式只出現了一個,即輸入前綴,例如:x(z)。這很容易理解,在JavaScript代碼里我們常用listener函數接收消息:
emitter.on("data", data => { // do something with data })
這里的data變量的scope就是在這個匿名函數內的,即bound name。一個過程P的Free name是它和外部產生行為交互的唯一方式。
這里是π process教材里的描述:
ReductionThe free names of a process circumscribe its capabilities for action: for a name x, in order for P to send x, to send via x, or to receive via x, it must be that x ∈ fn(P). Thus in order for two processes to interact via a name, that name must occur free in both of them, in one case expressing a capability to send, and in the other a capability to receive.
from π calculus by Davide Sangiorgi and David Walker
譯:
一個過程的free name決定了它的行為能力,對于過程P中的name x,如果P能夠:
發(fā)送x
通過x發(fā)送其他name
通過x接收其他name
x必須是P的free name。所以如果兩個過程需要通過一個name交互,這個name必須在兩個過程中都是free name,其中一方用于發(fā)送,另一方用于接收。
這個詞在編程界被用爛了。但是它的含義沒有什么高大上的地方。一個數學公式的形式變換就是reduction,當然我們正常情況下是希望它越變越簡潔的(所以叫reduce),除了你的陰險的數學老師會在出題時有個相反的邪惡目的。
π只有一個reduction:
x
含義是y從channel x發(fā)送出去之后,P才可以繼續(xù)執(zhí)行;同時x(z)前綴收到了y,Q得以繼續(xù)執(zhí)行,此時Q里的所有z都要替換成y。
在編程中:
x(z).Q意味著如果x尚未收到數據,Q不能開始執(zhí)行;這個input prefix在程序語言里很容易實現,就是常見的listener或者callback。
x
我們先假定存在一個構造函數或工廠方法,可以構造一個channel對象;我們先不回答channel如何構造,以及它內部是什么。
我們要求channel對象有一對接口方法,按照π的邏輯應該叫做send和receive;
注意在π里我們沒有類型和值的概念,一切變量皆channel,寫成代碼就是一切變量皆channel對象,通過channel傳遞的變量也是channel,這是π系統(tǒng)的重要特性之一:pass channel via channel(因為它會讓name突破一個scope使用)。
我們首先發(fā)現這個表達式里的free name都得先聲明(why?);x,y,w,a都聲明成channel(a在這個例子中沒有實際用于通訊,可以是任何東西)。
第一段代碼就是這個樣子。
class Channel { // placeholder } const channel = () => new Channel() const x = channel() const y = channel() const w = channel() const a = channel()
"."(dot)所表達的繼續(xù),我們可以用調用一個函數來實現;.0,可以用調用空函數(() => {})表示;
第一個表達式:x(z).z.0,可以這樣寫:
x.receive(z => z.send(a, () => {}))
receive方法形式上是提供一個函數f作為參數,channel x在接收到值z的時候調用這個函數f(z);
第二個表達式:x
x.send(w, () => y.send(w, () => {}))
注意這里要send成功之后才能繼續(xù)而不是調用send后就繼續(xù),所以不能寫成:
x.send(w) y.send(w)
最后一個表達式:y(v).v(u).0
y.receive(v => v.receive(u => (() => {})()))
到這里我們寫完了使用者代碼;在使用的時候我們也給Channel類的接口下了定義;如果你問哪個表示并發(fā)的vertical pipe(|)哪里去了?你想一下,我在文章的最后給出問題的答案。
在實現Channel類之前我們還要考慮一個順序問題。
π里的reduction是兩個并發(fā)過程之間發(fā)生的;在reduction的時候我們要調用兩個函數實現"."表示的繼續(xù),分別是發(fā)送者繼續(xù)和接收者繼續(xù),我們是否應該約定一個固定的順序?
答案是不應該;對于這里寫下的玩具代碼我們甚至故意加入了隨機性,這才是并發(fā)的含義,并發(fā)過程之間木有固定執(zhí)行順序。
我們先定義一個reduce函數;它的前提是send和receive兩個方法都被調用過了;這里存在兩種順序可能性:如果receive先被調用了,f被保存下來直到send被調用,這和常見的listener沒有區(qū)別;但π也允許反過來的順序,send先被調用了,則c和f都被保存下來,等到receive調用的時候再使用,這就是π里的兩個前綴會block后面的表達式估值的實現。
無論send和receive的實際調用順序如何,我們都希望reduce可以隨機執(zhí)行sender和receiver提供的回調函數。
class Channel { reduce () { if (!this.sendF || !this.receiveF) return let rnd = Match.random() if (rnd >= 0.5) { this.sendF() this.receiveF(this.c) } else { this.receiveF(this.c) this.sendF() } } send (c, f) { this.c = c this.sendF = f this.reduce() } receive (f) { this.receiveF = f this.reduce() } }
寫出reduce之后send和receive就是無腦代碼了;在標題的表達式里每個channel都只用了一次,所以我們不用在這里糾結如果重復發(fā)送和接受的情況如何解決;各種參數檢查和錯誤處理也先不管了,先跑起來試試。
最后所有的代碼都在這里,加了一點打印信息,你可以運行起來感受一下,也思考一下:
π系統(tǒng)capture了并發(fā)編程里哪些最重要的特性?沒有capture下來哪些?這段代碼里有什么東西是可能在實際編程問題上可以派上用場?
callback,emitter,promise,async/await能用Channel表達或者實現嗎?如果能,大概會是什么樣?
rx和這個Channel有什么異同?
class Channel { constructor (name) { this.name = name } reduce () { if (!this.sendF || !this.receiveF) return console.log(`passing name ${this.c.name} via channel ${this.name}`) let rnd = Math.random() if (rnd >= 0.5) { this.sendF() this.receiveF(this.c) } else { this.receiveF(this.c) this.sendF() } } send (c, f) { console.log(`${this.name} sending ${c.name}`) this.c = c this.sendF = f this.reduce() } receive (f) { console.log(`${this.name} receiving`) this.receiveF = f this.reduce() } } const channel = name => new Channel(name) const x = channel("x") const y = channel("y") const w = channel("w") const a = channel("a") x.receive(z => z.send(a, () => console.log("term 1 over"))) x.send(w, () => y.send(w, () => console.log("term 2 over"))) y.receive(v => v.receive(u => (() => console.log(`term 3 over, received ${u.name} finally`))()))答案
為什么vertical pipe表示的并發(fā)組合沒了呢?因為連續(xù)執(zhí)行上面代碼段里最后三句的時候,就是并發(fā)了;一定要說語言上什么符號對應了"|"的話,對于JavaScript就是;號了;它本來在語言上是statement的順序組合,在我們這個代碼里,就是并發(fā)組合了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/106973.html
本文關鍵闡述了Python完成1個全連接層的神經元網絡,文章內容緊扣主題進行詳盡的基本介紹,具有很強的實用價值,必須的朋友可以學習一下 序言 在本文中,提前準備用Python重新開始完成1個全連接層的神經元網絡。你可能會說,為何需要自己去完成,有許多庫和架構能夠給我們做這些事,例如Tensorflow、Pytorch等。這兒只想說僅有自己親自完成了,就是自己的。 想起今日他從接觸到了從事...
在vue項目中canvas實現截圖功能是常用的,下面是具體代碼: 實現效果: 在vue項目中做的一個截圖功能(只能夠截取圖片),只用鼠標就可以在畫面中進行框選截取。 實現:做一個彈窗,打開彈窗的時候傳入要截的圖,接下來在這個窗口里面,點擊截圖按鈕,開始截圖;點擊取消按鈕,取消截圖。 窗口里面的html主要是三個部分,一個是可截圖區(qū)域,一個是截取圖片的回顯,一個是操作按鈕(截圖按鈕和取消...
單眼三維成像是依據單獨監(jiān)控攝像頭健身運動仿真模擬雙目視覺獲得物件和空間里的三維視覺信息內容,下面本文關鍵為大家介紹了對于如何依據python完成單眼三維成像的資料,原文中依據案例編碼推薦的十分詳盡,必須的小伙伴可以借鑒一下 一、單眼三維成像簡述 客觀現實的物件是三維立體的,而我用監(jiān)控攝像頭獲得的圖象是二維動畫的,但我們可以依據二維圖像認知總體目標三維信息內容。三維重建技術要以相對應的形式解...
OpenCV-Python是一個Python庫,旨在解決計算機視覺問題。那么,很多的小伙伴都有興趣了解一下吧,正好,小編給大家總結了關于這方面的一些代碼問題,本文將利用Python+OpenCV實現圖像識別替換功能,小伙伴們在讀完之后,可以自己動手實踐一下哦。 OpenCV-Python是一種Python庫,目的在于處理機器視覺難題。 OpenCV可以說是開源系統(tǒng)的機器視覺庫,1999...
contour和contourf全是畫三維立體等高線圖的,接下來本文主要是為大家介紹了關于python做圖基本操作之plt.contour的相關信息,原文中依據案例編碼推薦的十分詳盡,需用的小伙伴可以參考一下 序言 plt.contour是python中用以畫等值線的函數公式,這兒簡單的介紹plt.contour的應用。 應用示例 importnumpyasnp importmat...
閱讀 2485·2021-11-16 11:45
閱讀 2458·2021-10-11 10:59
閱讀 2263·2021-10-08 10:05
閱讀 3856·2021-09-23 11:30
閱讀 2382·2021-09-07 09:58
閱讀 820·2019-08-30 15:55
閱讀 785·2019-08-30 15:53
閱讀 1931·2019-08-29 17:00