2

這更像是一個理論問題,但我覺得必須有辦法做到這一點。生成唯一ID的純功能方法

我有一些JS組件,當它們被創建時,它們需要爲html元素分配一個唯一的id,這個元素還沒有被用在任何其他組件中。這是相當瑣碎正常:

let currentId = 0; 
function getNextId() { 
    currentId += 1; 
    return currentId; 
} 

function MyComponent() { 
    this.id = getNextId(); 

    // Code which uses id 
} 

let c1 = new MyComponent(); 
// c1.id === 1 
let c2 = new MyComponent(); 
// c2.id === 2 

我不知道是否有任何的方式來做到只使用純函數這種事情,因爲我試圖環繞一些更高級的純功能的想法我的頭。據我所知,這需要Monads或其他類似的東西來完成,但我不知道該怎麼做。

謝謝!

+6

全局狀態由1模擬)通過國家作爲一個額外的參數每2)返回(可能更新的)狀態作爲附加的返回值,以及3)確保狀態的所有使用者都被正確排序,以便來自一個函數的更新狀態被傳遞給下一個函數。在Haskell中,這由「State」monad封裝。 – chepner

+0

@chepner這已經是一個很好的答案。 – duplode

+0

嘿@chepner!謝謝(你的)信息!你能提供一個StackOverflow的答案,也許這將顯示這個想法在實踐中使用我給上面的例子嗎?如果你這樣做,它會工作,我很樂意接受答案! –

回答

4

在Haskell中,你可能會喜歡寫東西

import Control.Monad.State 

data Component = Component Int String deriving (Show) 

getNextId :: State Int Int 
getNextId = do x <- get 
       put (x + 1) 
       return x 

makeComponent :: String -> State Int Component 
makeComponent name = do x <- getNextId 
         return (Component x name) 

components = evalState (traverse makeComponent ["alice", "bob"]) 0 

main = print $ components 

以上腳本將輸出

[Component 0 "alice",Component 1 "bob"] 

爲每個「呼」地getNextId會「迴歸」未來的行號。 traverse功能類似於map,但它確保每個monad的效果在將makeComponent應用於每個值的過程中發生。

This link可能會提供一些幫助,使其適應Javascript。


State類型構造本身只是圍繞一個函數的包裝,這裏Int -> (Int, Int)類型。該Monad實例,該類型可以讓你避免編寫代碼,看起來像這樣:

getNextID :: Int -> (Int, Int) 
getNextID x = (x, x+1) 

makeComponent :: String -> Int -> (Int, Int) 
makeComponent name x = let (now, then) = getNextID x 
         in (then, Component now name) 

components = let state_0 = 0 
       (state_1, c1) = makeComponent "alice" state_0 
       (state_2, c2) = makeComponent "bob" state_1 
      in [c1, c2] 
+1

很好放。爲了更安全起見,可以在'State Int','derived Monad'以及'只輸出'getNextId,get''和其他幾個''''newtype'包裝器上編寫一個模塊。以這種方式,進一步防止用戶重置計數器,不能訪問「put」。 – chi

-1

你可以使用閉包來保存計數器的狀態,像這樣:

var generateRandom = (function seed(){ 
      var start = 0; 
      return function(){ 
      return start++; 
      } 
     })(); 

生成隨機數使用時只需去:

generateRandom(); //logs 0 
generateRandom(); //logs 1 
generateRandom(); //logs 2 and so on.. 

雖然這看起來像你調用一個純函數,這仍然是一個黑客,我會說。我基本上是seed()函數,你可以在IIFE中看到,然後基本上將返回的函數存儲在變量generateRandom中。所以說,這不是純粹的功能。

但是,我希望它能讓你開始朝正確的方向發展。

+1

但是,這不是一個完全不是純粹的函數自generateRandom()返回具有相同參數的不同結果? –

+0

是的你是對的。 'generateRandom()'每次都會返回一個不同的值,但這就是你想讓隨機數生成器做對嗎?我收集了你想要的隨機數發生器的功能實現。這裏的函數保存內部狀態,使其能夠首先生成隨機數 –

+2

所有這些實現都在改變可變變量的範圍;因此,它根本沒有回答這個問題(儘管至少你是這方面的先行者)。 – duplode

1

純函數就意味着你沒有變異狀態。因此,這將工作:

function idGenerator(fnNext, aInit) { 
    function Gen(value){this.value = value} 
    Gen.prototype.next = function() { 
     return new Gen(fnNext(this.value)); 
    }; 
    return new Gen(aInit); 
} 

const evenGen = idGenerator(function(n){return n+2;}, 2); 
evenGen.value      //==> 2 
const evenGen2 = evenGen.next(); 
evenGen2.value     //==> 4 

const loop = function (seq, acc) { 
    return seq.value > 16 ? 
     acc : 
     loop(seq.next(), seq.value+acc); 
} 
const sumEven = loop(evenGen, 0); 
console.log(sumEven);    //==> 72 

對於你比如你需要稍微的changeit使國家可以通過:

const seq = idGenerator(function(n){return n+1;}, 1); 

function MyComponent(seq) { 
    this.id = seq.value; 
    this.seq = seq; 

    // Code which uses id 
} 

let c1 = new MyComponent(seq); 
// c1.id === 1 
let c2 = new MyComponent(c1.seq.next()); 
// c2.id === 2