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 

ただ純粋関数を使用して、この種のものを行うにはどのような方法があります場合、私はいくつかのより高度な純粋な機能のアイデアのまわりで私の頭をラップしようとしているように私は、思ったんだけど。限り、私はそれを達成するMonadまたはそのようなものの何かを必要とすることがわかりますが、私はそうする方法を知らない。

ありがとうございました!ちょうど行く使う乱数を生成するには

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

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

あなたは純粋な関数を呼び出しているように、これは見えますが

+6

グローバル状態が1でシミュレートされた)への追加の引数として状態を渡します2)毎回の状態を追加の戻り値として返す、3)状態のすべてのコンシューマが正しくシーケンスされるようにして、ある関数からの更新された状態が次の関数に渡されるようにする。 Haskellでは、これは 'State'モナドによってカプセル化されています。 – chepner

+0

@chepnerこれはすでにかなりの答えです。 – duplode

+0

ちょっと@chepner!情報をありがとう!上記の例を使用して実際にこのアイデアを表示するStackOverflow答えを提供することができますか?あなたがしてそれが動作する場合、私は答えを受け入れることをうれしく思います! –

答えて

4

、あなたは上記のスクリプトが出力

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

それぞれが次の行に番号を「返す」でしょうgetNextIdに「コール」と同じように

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 

のような何かを書くかもしれません。 traverse関数はmapのようなものですが、各モナドの効果が各値にmakeComponentを適用する過程で確実に発生します。

This linkは、これをJavascriptに適合させるのに役立つかもしれません。


State型コンストラクタ自体は、ここではタイプInt -> (Int, Int)の、機能単なるラッパーです。

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'、' Monad'を派生させる 'newtype'ラッパー、' getNextId、get'とそれ以外のもののみをエクスポートするモジュールを書くことができます。このようにして、ユーザは、「put」にアクセスすることなく、カウンタをリセットすることがさらに防止される。 – chi

-1

あなたはそうのようなカウンタの状態を保存するためにクロージャを使用することができますそれはまだ私が言うハックです。私は基本的にはIIFEで見ることができるようにseed()関数を1回実行してから、返された関数を基本的に変数generateRandomに格納します。それで、話すのは純粋に機能的ではありません。

しかし、私はあなたが正しい方向にスタートすることを願っています。 Haskellで

+1

これはまったく純粋ではありませんgenerateRandom()は同じパラメータで別の結果を返しますか? –

+0

はい、あなたは正しいです。 'generateRandom()'は毎回違う値を返しますが、それは乱数ジェネレータが正しいことをしたいのですか?私は乱数ジェネレータの機能的な実装をしたいと思っています。ここでの関数は、内部で状態を保存し、最初に乱数を生成できるようにします。 –

+2

この実装はすべて、可変変数のスコープを変更しています。したがって、それは全く質問に答えることはできません(少なくともあなたはそれについて前もって話しています)。 – duplode

1

ピュア機能を使用して、状態を変化させません暗示する:このタイプのMonadインスタンスは、あなたがこのようなコードを書くことを避けることができます。したがって、これは動作します:あなたたとえば

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 
関連する問題