Haskell数组增量更新:使用//运算符反转数组遇问题
Haskell数组反转问题解析
第一个代码结果全为1的原因
你写的列表推导式[(i, x!j) | i<-[1..4], j<-[4,3..1]]生成的是笛卡尔积:
- 当
i=1时,j遍历4,3,2,1,生成(1, x!4), (1, x!3), (1, x!2), (1, x!1) - 当
i=2时,j再次遍历4,3,2,1,生成(2, x!4), (2, x!3), (2, x!2), (2, x!1) - 以此类推,最终生成16个元组
而//运算符的规则是后出现的更新会覆盖之前同下标的更新,所以每个下标i最后被设置的是j=1时的x!1(也就是1),最终数组所有元素都是1。
两段代码结果不同的原因
[4,3..1]并不是enumFromTo 4 1的语法糖:
[4,3..1]对应的是enumFromThenTo 4 3 1,指定了起始值4、第二个元素3(步长为-1),所以会生成[4,3,2,1]enumFromTo 4 1对于整数类型来说,是从4递增到1,这个序列是空的(因为4>1,无法递增到达)
所以第二个代码里的列表推导式因为j的序列为空,没有生成任何更新元组,数组保持原样。
正确的数组反转实现
要实现反转,需要让下标i和对应的反转下标j一一配对,而不是笛卡尔积。可以用zip来关联两个序列:
test = listArray (1,4) [1..4] -- 方法1:用zip直接配对下标和反转下标 reverser x = x // [(i, x!j) | (i,j) <- zip [1..4] [4,3..1]] -- 方法2:更简洁的写法 reverser x = x // zip [1..4] (map (x!) [4,3..1])
运行reverser test会得到预期结果:array (1,4) [(1,4),(2,3),(3,2),(4,1)]
如果后续需要反转指定区间(比如从a到b),只需要调整zip里的序列即可,例如反转下标2到3:
reverseRange x a b = x // [(i, x!j) | (i,j) <- zip [a..b] [b,b-1..a]]
内容的提问来源于stack exchange,提问作者Alfy B
相关产品推荐
相关产品推荐

