【发布时间】:2014-10-28 05:40:55
【问题描述】:
这可能不是一个很实际的问题,我只是好奇我是否可以只用 lambda 表达式实现一个堆栈。
一个栈支持 3 种操作:top、pop 和 push,所以我首先将栈定义为 3 元组:
data Stack a = Stack a (a -> Stack a) (Stack a)
| Empty
这里Empty 代表空栈,所以我们至少有一个居民开始。
在这个定义下,除了push 操作之外,一切看起来都不错:
import Control.Monad.State
import Control.Monad.Writer
import Data.Maybe
data Stack a = Stack a (a -> Stack a) (Stack a)
| Empty
safePop :: Stack a -> Maybe (Stack a)
safePop Empty = Nothing
safePop (Stack _ _ s) = Just s
safeTop :: Stack a -> Maybe a
safeTop Empty = Nothing
safeTop (Stack x _ _) = Just x
push :: a -> Stack a -> Stack a
push x s = _
stackManip :: StateT (Stack Int) (Writer [Int]) ()
stackManip = do
let doPush x = modify (push x)
doPop = do
x <- gets safeTop
lift . tell . maybeToList $ x
modify (fromJust . safePop)
return x
doPush 1
void doPop
doPush 2
doPush 3
void doPop
void doPop
main :: IO ()
main = print (execWriter (execStateT stackManip Empty))
所以当我完成代码时,我应该能够运行它并得到类似[1,3,2]
但是,我发现自己无限扩展了push 的定义:
push 应该构造一个新堆栈,第一个元素是刚刚压入堆栈的项目,第三个元素是当前堆栈:
push :: a -> Stack a -> Stack a
push x s = Stack x _ s
为了填补这个洞,我们需要创建堆栈,所以我需要一个 let 表达式:
push :: a -> Stack a -> Stack a
push x s = let s1 = Stack x (\x1 -> Stack x1 _ s1) s
in s1
为了填补新的漏洞,我需要另一个 let 表达式:
push :: a -> Stack a -> Stack a
push x s = let s1 = Stack x (\x1 ->
let s2 = Stack x1 _ s1
in s2) s
in s1
所以你可以看到我的push 定义中总是有一个漏洞,但是我将其展开。
我有点理解Data.Function.fix 背后的魔力,并且猜想在这里可以应用一些类似的魔力,但无法弄清楚。
我想知道
- 这可能吗?
- 如果答案是肯定的,那么它背后的魔力是什么?
【问题讨论】:
-
请注意,您的
doPop函数实际上并没有改变状态,所以它更像是一个doPeek。 -
您也可以只使用
[a]和pop = tail、push = (:)、top = head。 -
@Cactus
doPop已修复,我专注于push并且没有机会检查我的stackManip是否有效。而我只是想通过不使用列表来实现堆栈来获得乐趣。