这更像是一个理论问题,但我觉得必须有办法做到这一点。
我有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或其他类似的东西来完成,但我不知道该怎么做。
谢谢!
答案 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
中。因此,它可以说不是纯粹的功能。
但是,我希望它能让你开始朝着正确的方向前进。