Javascript 使用converge生成列表的所有旋转时出现意外结果

Javascript 使用converge生成列表的所有旋转时出现意外结果,javascript,functional-programming,ramda.js,Javascript,Functional Programming,Ramda.js,我正在尝试获取列表的所有旋转v。因此,在旋转的定义中,我使用翻转版本的rotateLeft作为第一个分支函数(为了首先接受列表),然后是一个返回列表的函数[0,1,2,…,v.length-1],其中映射作为收敛函数 const {curry,mathMod,pipe,splitAt,reverse,unnest,converge,map,flip,length,times,identity} = require("ramda"); const v = [1,2,3,4]; const ro

我正在尝试获取列表的所有旋转
v
。因此,在
旋转
的定义中,我使用翻转版本的
rotateLeft
作为第一个分支函数(为了首先接受列表),然后是一个返回列表的函数
[0,1,2,…,v.length-1]
,其中
映射
作为收敛函数

const {curry,mathMod,pipe,splitAt,reverse,unnest,converge,map,flip,length,times,identity} = require("ramda");

const v = [1,2,3,4];

const rotateLeft = curry((n,vet) => {
    const i = mathMod(n,vet.length);
    return pipe(
        splitAt(i),
        reverse,
        unnest
    )(vet);
});

const rotations = converge(map,[
    flip(rotateLeft),
    pipe(length,times(identity))
]);

rotations(v);
然而,这并没有返回我所期望的结果。相反,如果我将其改写为以下内容,则效果良好:

map(flip(rotateLeft)(v),
    pipe(length,times(identity))(v));

// gives [[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3]]
据我所知,
converge
将两个分支函数应用于
v
,然后将结果作为参数提供给
map
。是这样吗? 那么为什么旋转(v)不返回相同的结果呢

使用
reduce

受vanilla JS版本的启发,我提出了以下
reduceRotations
函数,它没有明显地使用
map
的索引参数或递归。然后,当然,我把它翻译成了香草拉姆达,完全没有要点

还有一个

以下等效功能使用
scan
而不是
reduce

const {converge,scan,always,append,head,tail,identity} = require("ramda");

const scanRotations = (v) => {
    const rotate = (v) => append(head(v),tail(v));
    return scan(rotate,v,tail(v));
};

const scanPointFreeRotations =
    converge(scan,[
        always(converge(append,[head,tail])),
        identity,
        tail
    ]);

这是因为
converge
将提供给它的最长函数的arity作为其arity

因此,由于
flip(rotateLeft).length/=>2
pipe(length,times(identity))/=>1
,旋转将具有长度2。但你显然想要一元函数。最简单的修复方法是将
翻转(rotateLeft)
放入
一元数

const rotateLeft=curry((n,vet)=>{
常数i=数学模型(n,纵向长度);
回流管(
第(i)款,
相反,
不耐烦
)(兽医);
});
常数旋转=收敛(映射[
一元(翻转(旋转英尺)),
管道(长度、次数(标识))
])
常数v=[1,2,3,4];
控制台日志(
轮换(五)
)


const{curry,mathMod,pipe,splitAt,reverse,unnest,converge,map,一元数,flip,length,times,identity}=R
通过这种简单的递归实现的
旋转
证明,有时由大量小函数组成的无点代码不值得额外的头痛-

const rotations=([x,…xs],count=0)=>
计数>xs.length
? []
:[[x,…xs],…旋转([…xs,x],计数+1)]
控制台日志(旋转([1,2,3]))
// [ [ 1, 2, 3 ], [ 2, 3, 1 ], [ 3, 1, 2 ] ]
控制台日志(旋转([1,2,3,4]))

//[[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3]。
使用
地图切片的出色实现
!我想我不会想到这个:D@user633183:嗯,它利用了
Array.prototype.map
的额外索引参数(通常很不幸)。这感觉有点像滥用
map
。哦,谢谢!算术。。。好吧,现在说得通了!和往常一样回答得很好!。我正要写一个递归版本,这时我想到了我的
map
答案,然后就不费事了。干得好!尽管如此,我的目标是操作之前定义的
rotateLeft
,尽可能多地使用Ramda函数和无点合成,主要是作为练习。否则,我可能会写一些和你在这里做的非常相似的东西。。。还可以查看我的
旋转的替代版本
!也许甚至比无点版本更干净:
constrotate=lift(append)(head(v),tail(v))
是无点版本:
constrotate=lift(append)(head,tail)
。(可选的
converge(append,[head,tail])
感觉不那么优雅,而且肯定不那么标准。)
const {converge,scan,always,append,head,tail,identity} = require("ramda");

const scanRotations = (v) => {
    const rotate = (v) => append(head(v),tail(v));
    return scan(rotate,v,tail(v));
};

const scanPointFreeRotations =
    converge(scan,[
        always(converge(append,[head,tail])),
        identity,
        tail
    ]);