[英]Problems with Immutable Data in Functional Programming
我是函數式編程的新手。 我所理解的是函數式編程是使用純函數編寫代碼而不改變數據的價值。
我們不需要改變變量的值,而是在需要更新變量時在函數式編程中創建新的變量。
假設我們有一個變量x
,它表示程序發出的HTTP請求總數。 如果我們有兩個線程,那么我希望線程在任何線程發出HTTP請求時遞增x
。 如果兩個線程都生成變量x
的不同副本,那么它們如何同步x
的值。 例如:如果線程1發出10個HTTP請求而線程2發出11個HTTP請求,那么它們將分別打印10和11但是我將如何打印21。
我將解決Haskell部分。 MVar
是線程的通信機制之一。 這是Simon Marlow的書(該程序不言自明)中的一個例子:
main = do
m <- newEmptyMVar
forkIO $ do putMVar m 'x'; putMVar m 'y'
r <- takeMVar m
print r
r <- takeMVar m
print r
上述程序的輸出將是:
'x'
'y'
您可以在上面的示例中看到如何在線程之間共享變量m
的MVar
值。 您可以在本書中了解有關這些技術的更多信息。
我還將討論Haskell部分。
首先,我想澄清一些事情:
我們不需要改變變量的值,而是在需要更新變量時在函數式編程中創建新的變量。
那不太准確。 我們在需要時在FP中創建新的“變量”,而不是在需要改變現有變量時。 當我們做你所描述的事情時,我們甚至不會考慮突變; 我們可能只是認為我們正在創造一個類似於我們所擁有的新價值。
你用線程描述的內容有點不同。 你實際上是在尋找副作用(增加一個計數器)。 Haskell是純粹的,不會讓你在不明確的情況下拋出副作用。 因此,在這種情況下,您將需要求助於引用類型/可變單元格。 最簡單的一個叫做IORef
,在這個意義上它就像一個變量; 您可以分配值,讀取當前值,等等。
所以,正如你所看到的,當你在尋找這些東西時,你真的只有一個櫃台副本。
以上是我的答案的本質,但你已經具體詢問了線程,所以我也會回答這個問題。
IORef
實際上並不是線程安全的。 所以有建議的MVar
。 它們不像常規變量,但它們很接近,它們可以優雅地完成工作。 一般而言,松散地說:它們抽象變量和鎖定。 我想你可能會覺得TVar
更容易。 它們的行為類似於IORef
/變量,只有兩者不同,它們都是線程安全的; 您可以將它們的操作組合成一個操作,並且對它們執行的任何操作都是以原子方式完成的(STM)。
順便說一句,你可能會找到完全避免狀態的方法,這是非常鼓勵的。 例如,您可以讓兩個線程執行一個異步遞歸函數,該函數通過參數記住已經發出了多少請求,然后將其作為返回值。 請求總數是所有線程返回的請求總和。 這可以避免計數器上的副作用,但它只能在線程完成時產生結果。 這是非常有限的,所以有時你可能想要那種副作用。
好吧,我會在保持狀態時嘗試提供更一般的解釋,因為我認為這是你真正想知道的。
通常,您可以通過遞歸完成相同的操作,例如,如果您有以下功能:
somefun ()->
somefun(0).
somefun (X) ->
perform_http_request(),
if(something!=quit)
somefun(X+1)
end function.
generate_thread(0, Accumulator) ->
Accumulator;
generate_thread(X, Accumulator) ->
Y = somefun(),
NewAccumulator = add_to_accumulator(Y),
generate_thread(X-1, NewAccumulator).
我只是匆匆輸入這個,這是一個非常通用的解釋(你將不能直接使用這個代碼)但是你認為你可以發現你真的沒有這里的可變性......這個功能將在所有時候完成線程完成他們的處理,現在你可以做的實際線程同步取決於你選擇的語言和不同的語言有不同的處理並發和“線程”的方式..我建議你看看如果你是Erlang並發,因為它有一個非常好的並發模型imo。
無論如何,最后你可以將累加器中返回的所有值相加並顯示出來,順便看看foldl和foldr函數。
我自己不是大師,但我想你可能會錯過了解。
如果不保持狀態,就無法創建一個非常有用的程序 。 需要說明這種或那種方式。 FP的目標不是避免狀態, 而是要控制狀態的使用 。
以這種方式看待,您的狀態應該與數據庫條目一樣孤立和安全。 如果你像對待數據庫那樣對待狀態,我想你會沒事的
這意味着,
(inc count)
。 你寧願有一個函數increment-count!
這將安全地更新計數。 注意!
,這意味着副作用。 我希望這是有道理的。
我將地址Erlang部分。 即使在Erlang中也沒有神奇的同步,某個地方的某個人必須處理同步。 只是Erlang具有不變性(也就是沒有變量)的好處,有助於防止並發編程中的常見同步錯誤。 像gen_server這樣的Erlang / OTP已經擁有管理狀態的基礎設施。 實際上,gen_server是單線程的,它接收的任何消息都在郵箱中排隊。 這是一個關於Erlang的消息並發的鏈接。 Erlang如何同時處理訪問郵箱
在原始帖子的情況下,要掛起一個http請求計數器,您可以使用單個gen_server OTP(Erlang)。 你會驚訝於它可以處理多少吞吐量。 如果單個gen_server吞吐量確實不足,則可以使用分層gen_server來聚合計數。 Erlang / OTP附帶了一組運行時API,可以實時測量性能。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.