当前位置: 首页 > 工具软件 > Lens > 使用案例 >

Haskell语言学习笔记(38)Lens(1)

干照
2023-12-01

Lens

Lens是一个接近语言级别的库,使用它可以方便的读取,设置,修改一个大的数据结构中某一部分的值。

view, over, set

Prelude> :m +Control.Lens
Prelude Control.Lens> view _1 ("abc", "def")
"abc"
Prelude Control.Lens> over _1 (++ "!!!") ("abc", "def")
("abc!!!","def")
Prelude Control.Lens> set _1 "!!!" ("abc", "def")
("!!!","def")
Prelude Control.Lens> view _2 ("abc", "def")
"def"

这里 _1, _2 则相当于元组的属性名,在 Lens 库中被称为 lens。
view 是一个 Getting,相当于Java语言中用来读取属性的 .getXXX()。
over 以及 set 是一个 Setting,相当于Java语言中用来设置属性的 .setXXX()。
view l s 读取数据结构 s 中字段 l’ 的值。
set l s 直接设置数据结构 s 中字段 l’ 的值。
而 over l f s 则通过调用函数 f 修改数据结构 s 中字段 l’ 的值。

view, over, set的操作符版本

Prelude> :m +Control.Lens
Prelude Control.Lens> ("abc", "def") ^. _1 
"abc"
Prelude Control.Lens> ("abc", "def") & _1 %~ (++ "!!!") 
("abc!!!","def")
Prelude Control.Lens> ("abc", "def") & _1 .~ "!!!" 
("!!!","def")
Prelude Control.Lens> ("abc", "def") ^. _2
"def"
Prelude Control.Lens> _2 %~ (++ "***") $ ("abc", "def") 
("abc","def***")
Prelude Control.Lens> _2 .~ "***" $ ("abc", "def") 
("abc","***")

view l s ≡ s ^. l
set l v s ≡ l .~ v s ≡ s & l .~ v  
over l f s ≡ l %~ f
s ≡ s & l %~ f

over, set 的 State 版本

Prelude Control.Lens Control.Monad.State> execState (do _1 .= "!!!"; _2 .= "***") ("abc", "def")
("!!!","***")
Prelude Control.Lens Control.Monad.State> execState (do _1 %= (++ "!!!"); _2 %= (++ "***")) ("abc", "def")
("abc!!!","def***")

自定义 lens

{-# LANGUAGE TemplateHaskell, RankNTypes #-}

import Control.Lens

data Point = Point
    { _positionX :: Double
    , _positionY :: Double
    } deriving (Show)
makeLenses ''Point

data Segment = Segment
    { _segmentStart :: Point
    , _segmentEnd :: Point
    } deriving (Show)
makeLenses ''Segment

makePoint :: (Double, Double) -> Point
makePoint (x, y) = Point x y

makeSegment :: (Double, Double) -> (Double, Double) -> Segment
makeSegment start end = Segment (makePoint start) (makePoint end)

testSeg = makeSegment (0, 1) (2, 4)

自定义 lens 的几个步骤

  1. {-# LANGUAGE TemplateHaskell, RankNTypes #-}
    使用语言扩展
  2. import Control.Lens
    使用Lens库
  3. data Point = Point { _positionX :: Double, _positionY :: Double }
    使用 data 关键字定义数据结构以及字段名,注意字段名必须用下划线开头。
  4. makeLenses ”Segment
    使用 makeLenses ”TypeName 来定义 lens。
*Main> view segmentEnd testSeg
Point {_positionX = 2.0, _positionY = 4.0}
*Main> set segmentEnd (makePoint (2, 3)) testSeg
Segment {_segmentStart = Point {_positionX = 0.0, _positionY = 1.0}, _segmentEnd = Point {_positionX = 2.0, _positionY = 3.0}}
*Main> view (segmentEnd . positionY) testSeg
4.0
*Main> over (segmentEnd . positionY) (2 *) testSeg
Segment {_segmentStart = Point {_positionX = 0.0, _positionY = 1.0}, _segmentEnd = Point {_positionX = 2.0, _positionY = 8.0}}
*Main> testSeg^.segmentEnd
Point {_positionX = 2.0, _positionY = 4.0}
*Main> testSeg&segmentEnd .~ makePoint (2, 3)
Segment {_segmentStart = Point {_positionX = 0.0, _positionY = 1.0}, _segmentEnd = Point {_positionX = 2.0, _positionY = 3.0}}
*Main> testSeg^.segmentEnd.positionY
4.0
*Main> testSeg&segmentEnd.positionY %~ (2*)
Segment {_segmentStart = Point {_positionX = 0.0, _positionY = 1.0}, _segmentEnd = Point {_positionX = 2.0, _positionY = 8.0}}
 类似资料: