继续
这是一个涉及delimited continuations 的解决方案——分隔的延续有时被称为可组合延续,因为它们有一个返回值,因此可以与任何其他普通函数组合——此外,它们可以称为多个可以产生非凡效果的时代
// identity :: a -> a
const identity = x =>
x
// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => ([x,...xs]) =>
x === undefined
? []
: f (x) .concat (concatMap (f) (xs))
// cont :: a -> cont a
const cont = x =>
k => k (x)
// reset :: cont a -> (a -> b) -> b
const reset = m =>
k => m (k)
// shift :: ((a -> b) -> cont a) -> cont b
const shift = f =>
k => f (x => k (x) (identity))
// amb :: [a] -> cont [a]
const amb = xs =>
shift (k => cont (concatMap (k) (xs)))
// demo
reset (amb (['J', 'Q', 'K', 'A']) (x =>
amb (['♡', '♢', '♤', '♧']) (y =>
cont ([[x, y]]))))
(console.log)
// [ ['J','♡'], ['J','♢'], ['J','♤'], ['J','♧'], ['Q','♡'], ['Q','♢'], ['Q','♤'], ['Q','♧'], ['K','♡'], ['K','♢'], ['K','♤'], ['K','♧'], ['A','♡'], ['A','♢'], ['A','♤'], ['A','♧'] ]
当然,这适用于任何种类的输入和任何嵌套限制(不会破坏堆栈^_^)
const choices =
[0,1]
reset (amb (choices) (x =>
amb (choices) (y =>
amb (choices) (z =>
cont ([[x, y, z]])))))
(console.log)
// [ [0,0,0], [0,0,1], [0,1,0], [0,1,1], [1,0,0], [1,0,1], [1,1,0], [1,1,1] ]
但是您一定想知道我们如何抽象 amb 本身的嵌套——例如,在上面的代码中,我们有 3 层嵌套来生成长度为 3 的排列——如果我们想排列我们的选择 4 、5 次还是 N 次?
const permute = (n, choices) =>
{
const loop = (acc, n) =>
n === 0
? cont ([acc])
: amb (choices) (x =>
loop (acc.concat ([x]), n - 1))
return loop ([], n)
}
permute (4, [true,false]) (console.log)
// [ [ true , true , true , true ],
// [ true , true , true , false ],
// [ true , true , false, true ],
// [ true , true , false, false ],
// [ true , false, true , true ],
// [ true , false, true , false ],
// [ true , false, false, true ],
// [ true , false, false, false ],
// [ false, true , true , true ],
// [ false, true , true , false ],
// [ false, true , false, true ],
// [ false, true , false, false ],
// [ false, false, true , true ],
// [ false, false, true , false ],
// [ false, false, false, true ],
// [ false, false, false, false ] ]
听起来像德语,什么的
如果我正确理解您的评论,您想要压缩输入并排列每一对的东西 - 我们可以称之为 zippermute 吗?
const zippermute = (xs, ys) =>
{
const loop = (acc, [x,...xs], [y,...ys]) =>
x === undefined || y === undefined
? cont ([acc])
: amb ([x,y]) (choice =>
loop (acc.concat ([choice]), xs, ys))
return loop ([], xs, ys)
}
zippermute (['a', 'b', 'c'], ['x', 'y', 'z']) (console.log)
// [ [ 'a', 'b', 'c' ],
// [ 'a', 'b', 'z' ],
// [ 'a', 'y', 'c' ],
// [ 'a', 'y', 'z' ],
// [ 'x', 'b', 'c' ],
// [ 'x', 'b', 'z' ],
// [ 'x', 'y', 'c' ],
// [ 'x', 'y', 'z' ] ]