Haskell Concat操作返回空列表 模块示例,其中 lastPeg=15 leftCol=[1,2,4,7,11] rightCol=[1,3,6,10,15] 行数据=[] makeRowData::Integer->Integer->[Integer] makeRowData行位置= if(pos=leftCol!!(从整数行-1))&& (pos
您的核心问题在于代码的这一部分:Haskell Concat操作返回空列表 模块示例,其中 lastPeg=15 leftCol=[1,2,4,7,11] rightCol=[1,3,6,10,15] 行数据=[] makeRowData::Integer->Integer->[Integer] makeRowData行位置= if(pos=leftCol!!(从整数行-1))&& (pos,haskell,concat,Haskell,Concat,您的核心问题在于代码的这一部分: module Sample where lastPeg = 15 leftCol = [1, 2, 4, 7 , 11] rightCol = [1, 3, 6, 10, 15] rowData = [] makeRowData :: Integer-> Integer-> [Integer] makeRowData row pos = if (pos <= lastPeg) then if (pos &g
module Sample where
lastPeg = 15
leftCol = [1, 2, 4, 7 , 11]
rightCol = [1, 3, 6, 10, 15]
rowData = []
makeRowData :: Integer-> Integer-> [Integer]
makeRowData row pos =
if (pos <= lastPeg) then
if (pos >= leftCol !! (fromIntegral row-1)) &&
(pos <= rightCol !! (fromIntegral row-1)) then
do
rowData ++ [row]
makeRowData row (pos + 1)
else
makeRowData (row+1) (pos)
else
rowData
我想知道是否有人向您解释了操作符(++)::[a]->[a]->[a]
“将一个列表附加到另一个列表”,给您的印象是像xs++ys
这样的表达式修改xs
。第一个问题是,情况并非如此;实际上,此运算符返回一个新列表,该列表由两个串联在一起的输入组成,例如在GHCi中:
do
rowData ++ [row]
makeRowData row (pos + 1)
第二个问题是这里的do
块误导了您:它是在列表上运行的,因此它不像您期望的那样执行IO
排序。do
符号可以用于列表,因为列表类型有一个Monad
typeclass的实例,而不是像IO,列表Monad
实例进行迭代,就像列表理解一样
关于单子和do
符号的完整教程超出了本答案的范围,但例如,所有这些都是等效的:
> xs = [1, 2, 3]
> ys = [4, 5]
> xs ++ ys -- Append the lists.
[1, 2, 3, 4, 5]
> xs -- The original lists aren’t modified.
[1, 2, 3]
> ys
[4, 5]
我还没有测试你的逻辑是否正确,但至少这会帮助你摆脱困境
除此之外,您还可以在这里进行一些性能和风格改进:
- 用
++
附加链表的速度很慢;上面的go
的每次迭代,++
必须遍历整个左侧以构造其结果,并且该参数随着每次递归调用而增长,因此该函数最终需要二次时间O(n2)输入的长度。对于这样的小列表来说,这并不重要,但很快就会变得效率太低,无法与较大的输入一起使用
解决此问题的一种常见方法是,使用“cons”运算符(元素:list
)将元素以相反的顺序预先添加到累加器参数,而不是将它们追加(列表++[element]
),然后在必要时将结果反向,因为这只是线性O(n)
- 在定义的顶层,通常认为使用防护装置更为惯用,而不是
if…then…else…
,例如:
-- List comprehension:
[x * y | x <- [2, 3], y <- [3, 5]]
-- Equivalent ‘do’ notation:
do { x <- [2, 3]; y <- [3, 5]; pure (x * y) }
-- Desugared ‘do’ notation:
[2, 3] >>= (\ x -> [3, 5] >>= (\ y -> pure (x * y)))
-- Instances of ‘Monad’ & ‘Applicative’ for lists:
concatMap (\ x -> concatMap (\ y -> [x * y]) [3, 5]) [2, 3]
-- Result of all of the above:
[6, 10, 9, 15]
makeRowData :: Integer -> Integer -> [Integer]
makeRowData initialRow initialPos
-- Start the “loop” with an initial ‘rowData’ of ‘[]’.
= makeRowDataHelper [] initialRow initialPos
makeRowDataHelper :: [Integer] -> Integer -> Integer -> [Integer]
makeRowDataHelper rowData row pos =
if (pos <= lastPeg) then
if (pos >= leftCol !! (fromIntegral row-1)) &&
(pos <= rightCol !! (fromIntegral row-1)) then
-- To “modify” the state for the next iteration,
-- recursively call ‘go’ with different values.
makeRowDataHelper (rowData ++ [row]) row (pos + 1)
else
makeRowDataHelper rowData (row+1) (pos)
else
-- To exit the iteration, just return a value.
rowData
go-rowData行位置
|pos>lastPeg
=行数据
|pos>=leftCol!!(来自整数行-1)
,pos看起来您可能假设Haskell中的变量是不可变的。事实并非如此:诸如rowData++[row]
之类的表达式不会更改rowData
,而是返回一个新列表,它是rowData
和[row]的串联
。类似地,do
块不一定要一行一行地运行其中的每一行。我怀疑您可能需要找到一种不同的方法来在Haskell中成功实现这一点。无论如何,这里有一个简单的解决方案:rowData=(!!)$[1..]>=加入复制
(请注意,您需要为此导入Control.Monad
)。例如,rowData 6
根据需要生成4。@bradrn的意思是,Haskell变量是不可变的。它们确实是不可变的,即它们不能修改。您的解决方案可以工作,而且更加优雅。@leftaroundabout Oops,这确实是我的意思!谢谢您的更正!
go rowData row pos
| pos > lastPeg
= rowData
| pos >= leftCol !! (fromIntegral row-1)
, pos <= rightCol !! (fromIntegral row-1)
= …
| otherwise
= …