You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用_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)

关键部分解释

  1. 生成透镜:
    LensTH.makeLenses ''StackExample会自动生成名为internal的透镜,用来便捷访问和修改StackExample里的_internal列表字段,这是后续操作的基础。

  2. _Cons棱镜的作用:
    _Cons是一个Prism' [a] (a, [a]),它有两个核心能力:

    • 拆分非空列表:把x:xs拆成(x, xs)
    • 构建非空列表:把(x, xs)组合成x:xs
  3. Push操作:

    • %~是透镜的修改运算符,意思是"对透镜指向的值应用一个函数"
    • <|是_Cons提供的构建运算符,等价于普通的(:),用来把元素加到列表头部(对应栈的栈顶)
    • 整个stack & internal %~ (elem <|)的意思就是:取出stack里的internal列表,把elem加到列表头部,再放回stack中
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:48:38