Pure Functional方法生成唯一ID

时间:2016-11-04 15:31:21

标签: javascript haskell functional-programming purely-functional

这更像是一个理论问题,但我觉得必须有办法做到这一点。

我有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或其他类似的东西来完成,但我不知道该怎么做。

谢谢!

3 个答案:

答案 0 :(得分: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 :(得分: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

对于您的示例,您需要稍微改变以便可以传递状态:

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

答案 2 :(得分:-1)

您可以使用闭包来保存计数器的状态,如下所示:

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

要生成随机数,请使用go:

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

虽然这看起来像是在调用纯函数,但我仍然认为它仍然是一个黑客。我基本上是seed()函数,你可以在IIFE中看到,然后基本上将返回的函数存储在变量generateRandom中。因此,它可以说不是纯粹的功能。

但是,我希望它能让你开始朝着正确的方向前进。