Javascript 生成唯一id的纯函数方法

Javascript 生成唯一id的纯函数方法,javascript,haskell,functional-programming,purely-functional,Javascript,Haskell,Functional Programming,Purely Functional,这更像是一个理论问题,但我觉得一定有办法做到这一点 我有一些JS组件,当它们被创建时,它们需要为一个html元素分配一个唯一的id,一个在任何其他组件中都没有使用过的id。这通常是非常琐碎的: let currentId = 0; function getNextId() { currentId += 1; return currentId; } function MyComponent() { this.id = getNextId(); // Code which use

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

我有一些JS组件,当它们被创建时,它们需要为一个html元素分配一个唯一的id,一个在任何其他组件中都没有使用过的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
我想知道是否有任何方法可以仅仅使用纯函数来做这类事情,因为我正试图围绕一些更高级的纯函数思想来思考。据我所知,这需要单子或类似的东西才能完成,但我不知道怎么做


谢谢大家!

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

 var generateRandom = (function seed(){
          var start = 0;
            return function(){
             return start++;
            }
         })();
要生成随机数,请使用go:

 generateRandom(); //logs 0
 generateRandom(); //logs 1
 generateRandom(); //logs 2 and so on..
虽然这看起来像是在调用一个纯函数,但我认为这仍然是一个黑客行为。正如您在IIFE中看到的,我基本上是seed()函数,然后基本上将返回的函数存储在变量
generateradom
中。因此,可以说,它不是纯粹的功能性的


但是,我希望它能让你从正确的方向开始。

在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
因为每次对
getNextId
的“调用”都会“返回”行中的下一个数字。
遍历
函数类似于
映射
,但它确保在将
makeComponent
应用于每个值的过程中,每个monad的效果都会发生

可以提供一些帮助,使其适应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]

纯函数意味着不改变状态。因此,这将起作用:

函数idGenerator(FNEXT,aInit){
函数Gen(value){this.value=value}
Gen.prototype.next=函数(){
返回新的Gen(fnext(this.value));
};
返回新一代(aInit);
}
const evenGen=idGenerator(函数(n){返回n+2;},2);
evenGen.value/=>2
const evenGen2=evenGen.next();
evenGen2.value/=>4
常量循环=功能(序号,acc){
返回序列值>16?
行政协调会:
循环(seq.next(),seq.value+acc);
}
const sumeen=循环(evenGen,0);
console.log(sumEven);//=>72
例如,您需要稍微更改它,以便可以传递状态:

const seq=idGenerator(函数(n){返回n+1;},1);
功能MyComponent(seq){
this.id=seq.value;
this.seq=seq;
//使用id的代码
}
设c1=新的MyComponent(seq);
//c1.id==1
设c2=新的MyComponent(c1.seq.next());
//c2.id==2

模拟全局状态的方法是:1)将状态作为附加参数传递给每个函数;2)将(可能已更新的)状态作为附加返回值返回;3)确保状态的所有使用者都正确排序,以便将一个函数的更新状态传递给下一个函数。在Haskell中,这是由
状态
单子封装的。@chepner这几乎已经是一个答案了。嘿@chepner!谢谢你的信息!你能提供一个StackOverflow答案吗?也许可以用我上面给出的例子在实践中展示这个想法?如果你这么做了,而且它起作用了,我很乐意接受这个答案!相关:“concurrent supply”包提供了可拆分标识符生成器,在处理多线程的更高级方案中非常有用:可能相关:。但这不是一个纯粹的函数,因为GeneratorDomain()使用相同的参数返回不同的结果吗?是的,您是对的
generateradom()
每次都返回不同的值,但这就是您希望随机数生成器正确执行的操作吗?我推测您希望有一个随机数生成器的功能实现。这里的函数在内部保存状态,使其能够首先生成随机数。此实现所做的一切就是更改可变变量的范围;因此,它根本没有回答这个问题(尽管至少你是坦率的)。@VivekPradhan我想我应该澄清一下,我不想要一个单一的纯函数来实现这一点,这是不可能的,而是一个只使用纯函数就可以获得类似功能的系统。为了更安全,可以在
State Int
周围编写一个
newtype
包装器的模块,
派生Monad
,并且只导出
getNextId、get
和其他很少的内容。这样,进一步防止用户重置计数器,而不能访问
put
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]