如何使用_Cons棱镜组合?基于Haskell Lens实现列表增删的代码疑问
使用_Cons棱镜操作自定义栈类型
看起来你想用_Cons棱镜来操作StackExample里的列表,实现类似栈的添加(push)和删除(pop)操作对吧?我来帮你补全代码并解释每一步的用法:
完整实现代码
{-# LANGUAGE TemplateHaskell, FlexibleContexts, ScopedTypeVariables #-} module LensExample where import qualified Control.Lens.TH as LensTH import Control.Lens.Cons (_Cons, (<|), (^?)) import Control.Lens.Setter (over, (%~)) import Control.Lens.Getter (view) import Data.Maybe (fromMaybe) data StackExample = StackExample { _internal :: [String] } deriving (Show) -- 自动生成访问_internal字段的透镜 LensTH.makeLenses ''StackExample -- 往栈顶添加元素(Push操作) push :: String -> StackExample -> StackExample push elem stack = stack & internal %~ (elem <|) -- 移除栈顶元素(Pop操作),返回Maybe (弹出的元素, 新栈) pop :: StackExample -> Maybe (String, StackExample) pop stack = do (topElem, restList) <- stack ^? internal . _Cons return (topElem, stack & internal .~ restList) -- 安全Pop:栈为空时返回默认值和原栈 safePop :: String -> StackExample -> (String, StackExample) safePop defaultVal stack = fromMaybe (defaultVal, stack) (pop stack)
关键部分解释
生成透镜:
LensTH.makeLenses ''StackExample会自动生成名为internal的透镜,用来便捷访问和修改StackExample里的_internal列表字段,这是后续操作的基础。_Cons棱镜的作用:
_Cons是一个Prism' [a] (a, [a]),它有两个核心能力:- 拆分非空列表:把
x:xs拆成(x, xs) - 构建非空列表:把
(x, xs)组合成x:xs
- 拆分非空列表:把
Push操作:
%~是透镜的修改运算符,意思是"对透镜指向的值应用一个函数"<|是_Cons提供的构建运算符,等价于普通的(:),用来把元素加到列表头部(对应栈的栈顶)- 整个
stack & internal %~ (elem <|)的意思就是:取出stack里的internal列表,把elem加到列表头部,再放回stack中
Pop操作:
^?是预览运算符,用来尝试匹配_Cons的拆分规则:如果列表非空,返回Just (topElem, restList);如果为空,返回Nothing- 我们用
do语法处理Maybe类型,成功匹配后就返回弹出的元素和更新后的栈(把internal字段设为剩下的列表restList)
示例用法
你可以用下面的代码测试上述功能:
main :: IO () main = do let emptyStack = StackExample [] stackWithHello = push "Hello" emptyStack stackWithTwoElems = push "World" stackWithHello print stackWithTwoElems -- 输出: StackExample {_internal = ["World","Hello"]} case pop stackWithTwoElems of Just (popped, newStack) -> do print popped -- 输出: "World" print newStack -- 输出: StackExample {_internal = ["Hello"]} Nothing -> putStrLn "Stack is empty" let (defaultVal, unchangedStack) = safePop "Default" emptyStack print defaultVal -- 输出: "Default" print unchangedStack -- 输出: StackExample {_internal = []}
内容的提问来源于stack exchange,提问作者Julian Birch
相关产品推荐
相关产品推荐

