Javascript chainRec的基本理念是什么?
[编辑] 这是我们的后续问题Javascript chainRec的基本理念是什么?,javascript,recursion,functional-programming,monads,Javascript,Recursion,Functional Programming,Monads,[编辑] 这是我们的后续问题 给定的是chainRec的类型 chainRec :: ChainRec m => ((a -> c, b -> c, a) -> m c, a) -> m b 通常,chainRec与蹦床一起实现,以允许monad中的堆栈安全递归。但是,如果我们放下蹦床,我们可以实现正常功能的chainRec类型,如下所示: const chainRec = f => x => join(f(chainRec(f), of, x));
给定的是chainRec的类型
chainRec :: ChainRec m => ((a -> c, b -> c, a) -> m c, a) -> m b
通常,chainRec
与蹦床一起实现,以允许monad中的堆栈安全递归。但是,如果我们放下蹦床,我们可以实现正常功能的chainRec
类型,如下所示:
const chainRec = f => x => join(f(chainRec(f), of, x));
接下来,我想将其应用于递归操作:
constmap=f=>g=>x=>f(g(x));
常数join=f=>x=>f(x)(x);
常数=x=>y=>x;
常量chainRec=f=>x=>join(f(chainRec(f),of,x));
常量重复=n=>f=>x=>
chainRec((循环,完成,参数)=>
args[0]==0
?完成(参数[1])
:loop([args[0]-1,map(f)(args[1]))([n,of(x)]);
常数inc=x=>of(x+1);
重复(10)(inc)(0)(;/)错误
不知道你的repeat
函数做什么,我想你的调用repeat(10)(inc)(0)
应该扩展到
map(inc)(
map(inc)(
map(inc)(
map(inc)(
map(inc)(
map(inc)(
map(inc)(
map(inc)(
map(inc)(
map(inc)(
of(0)
)
)
)
)
)
)
)
)
)
)
由于您的inc
出于某种原因返回函数\u=>Int
而不是普通的Int
,这将调用函数x
上的x+1
,从而导致该函数的字符串化(y=>x
变成“y=>x1”
),尝试调用时将引发异常
固定后
const inc=x=>x+1
,您的repeat
功能仍然不起作用。它需要是简单的递归,带有
const id = x => x
// rec :: ((a -> c, b -> c, a) -> c) -> a -> b
// here with c == b, no trampoline
const rec = f => x => f(rec(f), id, x) // a bit like the y combinator
const repeat = n => f => x =>
rec((loop, done, [m, g]) =>
m === 0
? done(g)
: loop([m - 1, map(f)(g)])
)([n, of(x)]);
repeat(10)(inc)(0)() // 10 - works!
根本不涉及monad
如果我们想使用chainRec
,我们需要引入一些任意的monad(这里是函数monad),而f
对chainRec
的回调需要返回该monad类型的实例,而不仅仅是循环
/完成
:
chainRec :: ChainRec m => ((a -> c, b -> c, a) -> m c, a) -> m b
// ^
我们只需将返回值包装在的中即可:
const repeat = n => f => x =>
chainRec((loop, done, [m, g]) =>
of(m === 0
// ^^
? done(g)
: loop([m - 1, map(f)(g)])
)
)([n, of(x)]);
当然,现在得到一个mb
,也就是说,所有东西都包装在另一个函数中:
repeat(10)(inc)(0)()() // 10
// ^^
// repeat(1)(inc)(0) expands to `of(map(inc)(of(0)))
但我怀疑这是您想要的。您是在询问chainRec
的基本思想还是您的具体问题?“通常chainRec
与蹦床一起实现,以允许monad中的堆栈安全递归。”-是的,就是这样。“然而,如果我们扔下蹦床……”那么你根本不应该实现chainRec
,而应该简单地实现chain
。我想理解的是,如果chainRec
本质上是chain
通过map
和join
实现的。由于chain
现在由两个步骤组成,我们可以将它们分开。映射发生在f
(属于chainRec
)和join
自身的chainRec
中。然后我试图通过一个人为的例子来验证这个模糊的想法,但没有成功。没有蹦床,它只会增加无意中出现类型错误的机会,混合单子类型和c
类型。我认为这种形式只适用于理解递归的工作原理。它背后的实际想法最好通过chainRec=f=>x=>unlaprecandchain(inLazyRecWrapper(chainRec(f)),stopUnwrap,x))
来表达。是的,这正是我想要的!我的错误是将inc
定义为一个动作,它与map
一起提供嵌套的函数上下文。显然,chainRec
的f
必须提供这种上下文。repeat
这样定义显然是胡说八道。下次我应该花更多的时间来找到一个不那么做作的例子。为了避免泄漏嵌套的上下文,我实现了chainRec=f=>x=>join(f(chainRec(f),map(join)(of),x))
@ftor我想你可以把inc
作为一个动作,用chain
而不是map
来构建调用序列。尽管仍然不会使用chainRec
,因为您永远不会对递归函数进行尾部链接call@ftorchainRec=f=>x=>join(f(chainRec(f),map(join)(of),x))
的类型错误