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
      = …