将专门用于列表的未来态表示为命令式循环
Express a futumorphism specialized to lists as an imperative loop
我一直在尝试翻译专门用于 List
s
的未来态的递归 Haskell 实现
futuL :: (a -> Maybe (b, ([b], Maybe a))) -> a -> [b]
futuL f x = case f x of
Nothing -> []
Just (y, (ys, mz)) -> y : (ys ++ fz)
where fz = case mz of
Nothing -> []
Just z -> futuL f z
进入命令式 Javascript 循环。这非常困难(至少对我而言):
const None = ({type: "Option", tag: "None"});
const Some = x => ({type: "Option", tag: "Some", runOption: x});
const arrFutu = coalg => x => { // futuL f x
const acc = []; // ~ fz
while (true) {
let optX = coalg(x); // f x
switch (optX.tag) { // case
case "None": return acc; // Nothing -> []
case "Some": {
let [y, [ys, optX_]] = optX.runOption; // Just (y, (ys, mz))
switch(optX_.tag) {
case "None": {
arrPushFlat(acc) ((ys.unshift(y), ys)); // y : (ys ++ [])
return acc;
}
case "Some": { // y : (ys ++ futuL f z)
arrPushFlat(acc) ((ys.unshift(y), ys));
x = optX_.runOption;
break;
}
default: throw new UnionError("invalid tag");
}
break;
}
default: throw new UnionError("invalid tag");
}
}
};
我很难在头脑中解析 Haskell 代码,尤其是包含递归调用的 where
部分。我想这个调用不在尾部位置,因此我的 JS 循环需要一个显式堆栈 (acc
)。
我的翻译正确吗?
因为 Haskell 是惰性的,所以可以在计算其余部分之前开始使用 "futu" 返回的列表的开头。在 Javascript 术语中,最好用 generators 建模。
例如(为简单起见,我使用 null
值来表示 None
s):
const arrFutu = coalg => function*(seed) {
while (true) {
const next = coalg(seed);
if (next) {
// Maybe (b, ([b], Maybe a)), using null for None & flattening nested tuple
const [item, items, nextseed] = next;
yield item;
yield* items;
// Maybe a, using null for None
if (nextseed) {
seed = nextseed;
continue; // Continue iterating if the seed isn't null.
}
}
return;
}
}
举个例子,例如:
const coalg1 = seed => {
if (seed < 5) {
return ['a', ['a','a'], seed + 1];
} else if (seed < 10) {
return ['b', ['b','b'], seed + 1];
} else if (seed == 10) {
return ['c', ['c'], null];
}
return null;
}
for (const x of arrFutu(coalg1)(0)) {
console.log(x);
}
for (const x of arrFutu(coalg1)(20)) {
console.log(x);
}
让futu递归而不是迭代会很好,但似乎。
顺便说一下,即使 Haskell 代码不是尾递归,递归也是 "guarded":递归调用发生在一个或多个数据构造函数(此处为列表构造函数)中,并且可以延迟调用,直到客户端在使用返回列表时 "peeled away" 构造函数。
我一直在尝试翻译专门用于 List
s
futuL :: (a -> Maybe (b, ([b], Maybe a))) -> a -> [b]
futuL f x = case f x of
Nothing -> []
Just (y, (ys, mz)) -> y : (ys ++ fz)
where fz = case mz of
Nothing -> []
Just z -> futuL f z
进入命令式 Javascript 循环。这非常困难(至少对我而言):
const None = ({type: "Option", tag: "None"});
const Some = x => ({type: "Option", tag: "Some", runOption: x});
const arrFutu = coalg => x => { // futuL f x
const acc = []; // ~ fz
while (true) {
let optX = coalg(x); // f x
switch (optX.tag) { // case
case "None": return acc; // Nothing -> []
case "Some": {
let [y, [ys, optX_]] = optX.runOption; // Just (y, (ys, mz))
switch(optX_.tag) {
case "None": {
arrPushFlat(acc) ((ys.unshift(y), ys)); // y : (ys ++ [])
return acc;
}
case "Some": { // y : (ys ++ futuL f z)
arrPushFlat(acc) ((ys.unshift(y), ys));
x = optX_.runOption;
break;
}
default: throw new UnionError("invalid tag");
}
break;
}
default: throw new UnionError("invalid tag");
}
}
};
我很难在头脑中解析 Haskell 代码,尤其是包含递归调用的 where
部分。我想这个调用不在尾部位置,因此我的 JS 循环需要一个显式堆栈 (acc
)。
我的翻译正确吗?
因为 Haskell 是惰性的,所以可以在计算其余部分之前开始使用 "futu" 返回的列表的开头。在 Javascript 术语中,最好用 generators 建模。
例如(为简单起见,我使用 null
值来表示 None
s):
const arrFutu = coalg => function*(seed) {
while (true) {
const next = coalg(seed);
if (next) {
// Maybe (b, ([b], Maybe a)), using null for None & flattening nested tuple
const [item, items, nextseed] = next;
yield item;
yield* items;
// Maybe a, using null for None
if (nextseed) {
seed = nextseed;
continue; // Continue iterating if the seed isn't null.
}
}
return;
}
}
举个例子,例如:
const coalg1 = seed => {
if (seed < 5) {
return ['a', ['a','a'], seed + 1];
} else if (seed < 10) {
return ['b', ['b','b'], seed + 1];
} else if (seed == 10) {
return ['c', ['c'], null];
}
return null;
}
for (const x of arrFutu(coalg1)(0)) {
console.log(x);
}
for (const x of arrFutu(coalg1)(20)) {
console.log(x);
}
让futu递归而不是迭代会很好,但似乎
顺便说一下,即使 Haskell 代码不是尾递归,递归也是 "guarded":递归调用发生在一个或多个数据构造函数(此处为列表构造函数)中,并且可以延迟调用,直到客户端在使用返回列表时 "peeled away" 构造函数。