在Haskell中遍历列表并对堆栈执行操作

在Haskell中遍历列表并对堆栈执行操作,haskell,stack,Haskell,Stack,我是自学成才的哈斯克尔。我有以下代码使用列表实现堆栈: push :: Int -> [Int] -> [Int] push x [] = [x] push x xs = xs ++ [x] pop :: [Int] -> [Int] pop [] = error "Cannot pop from an empty list!" pop xs = init xs peek :: [Int] -> Int peek [] = error "

我是自学成才的哈斯克尔。我有以下代码使用列表实现堆栈:

push :: Int -> [Int] -> [Int]
push x [] = [x]
push x xs = xs ++ [x]

pop :: [Int] -> [Int]
pop [] = error "Cannot pop from an empty list!"
pop xs = init xs

peek :: [Int] -> Int
peek [] = error "Cannot peek from an empty list!"
peek xs = last xs

isEmpty :: [Int] -> Bool
isEmpty [] = True
isEmpty xs = False
现在,我想创建一个函数,该函数迭代整数列表,并在堆栈上执行以下操作:

  • 如果是偶数,则推到堆栈
  • 如果是奇数,则从堆栈中弹出
例如,假设我们有一个整数的输入列表
[0,2,6,7,3,4]
。该功能的流程应如下所示:

Current Int         Operation           Result
0 (First item)      push 0 []           [0]
2                   push 2 [0]          [0, 2]
6                   push 6 [0, 2]       [0, 2, 6]
7                   pop [0, 2, 6]       [0, 2]
3                   pop [0, 2]          [0]
4 (Last item)       push 4 [0]          [0, 4]
到目前为止,这就是我得到的,它显然没有遍历列表,也没有真正起作用:

operation :: [Int] -> [Int]
operation [] = []
operation (x:xs) | even x = push x stack
                 | odd x = pop stack
    where stack = []

非常感谢您的帮助。提前谢谢

使用您的代码,使用

但是,应该注意的是,堆栈的实现使得这些
pop
push
函数的速度慢得多。由于Haskell列表是单链接列表,因此必须遍历整个列表才能达到末尾的值。如果只操作列表开头的值,然后在整个操作完成后反转列表,效率会更高。这将使您的O(n2)操作变为O(n)

还应注意,此功能仍然不安全,因为如果有太多的
奇数
数字,操作可能会产生错误。最好使用
来停止任何错误

import Control.Monad (foldM)

pop :: [a] -> Maybe [a]
pop [] = Nothing
pop (_:xs) = Just xs

push :: a -> [a] -> [a]
push = (:)

operation :: [Int] -> Maybe [Int]
operation = fmap reverse . foldM step []
    where step xs x | odd x = pop xs
                    | otherwise = Just (push x xs)

这与您的问题无关,但
[]
是一个链接列表。从末尾添加或删除需要O(n)个时间。您应该在前端工作,以便您的操作都是O(1)。使用手指树实现链表。@Carl您能在O(1)时间内通过外科手术从Haskell列表的中间删除一个节点吗?如果不是,则它不是链表。报告中没有这样的内容,报告中也没有这样的内容。对于您的编辑(尽管在得到答案后对问题进行显著编辑并不好),有一个问题是您没有删除
,只是
@WillNess:您使用了一个奇怪的“链接列表”的狭义定义。当然我可以:
let(pre,post)=pre++中的拆分nxs drop 1 post
在O(1)时间内计算。不过,你可能会对要求结果的时间感到失望。谢谢!如果我想添加一个单独的条件来检查一个数字是否为0,然后中断循环并打印一条消息,我该怎么做?我完全知道“操作”函数的类型签名不支持IO。(我还编辑了“操作”功能)您可以使用
字符串[Int]
而不是
Maybe[Int]
退出
foldM
,并发送消息谢谢!我已经更新了我的代码,包括“左”和“右”,但是,我得到了“foldM step[]”部分的一个匹配错误…@jk_01最好撤消该编辑,并用更新的代码发布一个新问题:),如果需要,可能还包括一个链接到这篇文章作为背景参考。@WillNess,我明白了,谢谢!
pop = tail
push = (:)

operation :: [Int] -> [Int]
operation = reverse . foldl step []
    where step xs x | odd x = pop xs
                    | otherwise = push x xs
import Control.Monad (foldM)

pop :: [a] -> Maybe [a]
pop [] = Nothing
pop (_:xs) = Just xs

push :: a -> [a] -> [a]
push = (:)

operation :: [Int] -> Maybe [Int]
operation = fmap reverse . foldM step []
    where step xs x | odd x = pop xs
                    | otherwise = Just (push x xs)