Jav*_*ran 3 haskell lambda-calculus
这可能不是一个非常实际的问题,我只是好奇我是否可以实现只有lambda表达式的堆栈.
堆栈支持3个操作:top,pop和push,因此,我首先定义堆栈是一个3元组:
data Stack a = Stack a (a -> Stack a) (Stack a)
| Empty
Run Code Online (Sandbox Code Playgroud)
这里Empty代表空堆,所以我们至少有一个居民开始.
根据这个定义,除了push操作之外,eveything看起来很好:
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))
Run Code Online (Sandbox Code Playgroud)
因此,当我完成代码时,我应该能够运行它并获得类似的东西 [1,3,2]
但是,我发现自己扩展了push无限的定义:
push 应该构造一个新的堆栈,第一个元素是刚推入堆栈的项目,第三个元素是当前堆栈:
push :: a -> Stack a -> Stack a
push x s = Stack x _ s
Run Code Online (Sandbox Code Playgroud)
为了填补空洞,我们需要创建堆栈,所以我需要一个let-expression:
push :: a -> Stack a -> Stack a
push x s = let s1 = Stack x (\x1 -> Stack x1 _ s1) s
in s1
Run Code Online (Sandbox Code Playgroud)
要填补新洞,我需要另一个let-expression:
push :: a -> Stack a -> Stack a
push x s = let s1 = Stack x (\x1 ->
let s2 = Stack x1 _ s1
in s2) s
in s1
Run Code Online (Sandbox Code Playgroud)
所以你可以看到我的push定义中总有一个漏洞,但是我扩展了它.
我有点理解背后的魔法Data.Function.fix并猜测一些类似的魔法可以在这里应用,但无法弄明白.
我在想
您可以使用具有Church编码的函数类型完全实现它:
{-# LANGUAGE Rank2Types #-}
newtype Stack a = Stack (forall r. (a -> Stack a -> r) -> r -> r)
cons :: a -> Stack a -> Stack a
cons x (Stack f) = Stack (\g nil -> _)
peek :: Stack a -> Maybe a
peek (Stack f) = f (\x _ -> Just x) Nothing
Run Code Online (Sandbox Code Playgroud)
这表示a Stack是一个函数,它接受一个函数,该函数将顶部元素和堆栈的其余部分作为其参数.该Stack函数的第二个参数是如果堆栈为空时使用的缺省值.我实现了这个peek功能,但是我离开cons了,剩下的就是练习(让我知道你是否需要更多帮助.另外,你留下我放入的下划线cons,GHC将告诉你它预期的类型并列出一些可能相关的绑定).
rank-2类型表示,给定a Stack a,我们可以给它一个返回任何类型值的函数,不受a类型变量的约束.这很方便,因为我们可能不想使用相同的类型.考虑一堆列表,我们想要使用函数Stack来获取顶部元素的长度.更重要的是,它表示像一个函数cons无法以任何方式操纵结果.它必须返回r它从函数中获取的类型值(如果堆栈为空,则返回默认值),不变.
另一个好的练习是实现toList :: Stack a -> [a]并fromList :: [a] -> Stack a表明这两个函数形成同构(意味着它们彼此相反).
事实上,据我所知,所有Haskell数据类型都有一个表示为Church编码.您可以在此Stack类型中看到三种组合类型(总和类型,产品类型和"类型递归")的基本方法.