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

时间:2021-05-10 23:15:43

标签: haskell stack

我正在自学 Haskell。我有以下代码使用列表实现堆栈:

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 = []

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

1 个答案:

答案 0 :(得分:2)

使用您的代码,使用 foldl 实现这一点是最简单的。

operation :: [Int] -> [Int]
operation = foldl step []
    where step xs x | odd x = pop xs
                    | otherwise = push x xs

但是,应该注意的是,您对堆栈的实现会使这些 poppush 函数变慢。由于 Haskell 列表是单链表,您必须遍历整个列表才能到达最后的值。只操作列表头部的值,然后在整个操作完成时反转列表会更有效率。这会将您的 O(n2) 操作变成 O(n)。

pop = tail
push = (:)

operation :: [Int] -> [Int]
operation = reverse . foldl step []
    where step xs x | odd x = pop xs
                    | otherwise = push x xs

还需要注意的是,这个函数仍然不安全,因为如果odd数过多,操作可能会产生错误。最好使用 Maybe 来阻止任何错误。

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)
相关问题