f# - 如何使用列表更新嵌套记录结构
问题描述
有人可以告诉我如何更新嵌套记录中的子项吗?我想为值为“B”的项目设置isSelected
为true
type MyItem = {isSelected:bool; value:string}
type MyModel = {list:MyItem list}
let a = {isSelected = false; value = "A"}
let b = {isSelected = false; value = "B"}
let c = {isSelected = false; value = "C"}
let m = {list = [a;b;c]}
let m2 = { m with list = { m.list with ??? = { ??? }}}
我不会使用可变数据结构。
解决方案
不变性很棒,但是在处理嵌套的不可变结构时,它可能会有点麻烦。特别是如果它嵌套得很深。
解决这个问题的一种方法是所谓的镜头。
所以我稍微增加了示例的嵌套级别,以便让镜头的价值更加明显。
module Lenses =
// This lens is a pair of function, a getter that get's inner value of an object
// and a setter that sets the inner value of an object
// The cool thing is that a lens is composable meaning we can create a lens
// that allows us to get and set a deeply nested property succinctly
type Lens<'O, 'I> = L of ('O -> 'I)*('I -> 'O -> 'O)
let lens (g : 'O -> 'I) (s : 'I -> 'O -> 'O) = L (g, s)
// Gets an inner value
let get (L (g, _)) o = g o
// Sets an inner value
let set (L (_, s)) i o = s i o
// Updates an inner value given an updater function that sees the
// inner value and returns a new value
let over (L (g, s)) u o = s (u (g o)) o
// Compose two lenses into one, allows for navigation into deeply nested structures
let compose (L (og, os)) (L (ig, is)) =
let g o = ig (og o)
let s i o = os (is i (og o)) o
L (g, s)
type Lens<'O, 'I> with
static member (-->) (o, i) = compose o i
open Lenses
// I made the model a bit more complex to show benefit of lenses
type MySelection =
{
isSelected: bool
}
// Define a lens that updates the property, this code can potentially be generated
// Scala does this with macros, in F# there are other possibilities
static member isSelectedL : Lens<MySelection, bool> = lens (fun o -> o.isSelected) (fun i o -> { o with isSelected = i })
type MyValue =
{
value: string
}
static member valueL : Lens<MyValue, string> = lens (fun o -> o.value) (fun i o -> { o with value = i })
type MyItem =
{
selection : MySelection
value : MyValue
}
static member selectionL : Lens<MyItem, MySelection> = lens (fun o -> o.selection) (fun i o -> { o with selection = i })
static member valueL : Lens<MyItem, MyValue> = lens (fun o -> o.value ) (fun i o -> { o with value = i })
type MyModel =
{
list: MyItem list
}
static member listL : Lens<MyModel, MyItem list> = lens (fun o -> o.list) (fun i o -> { o with list = i })
[<EntryPoint>]
let main argv =
// Define example model
let a = {selection = {isSelected = false}; value = {value = "A"}}
let b = {selection = {isSelected = false}; value = {value = "B"}}
let c = {selection = {isSelected = false}; value = {value = "C"}}
let m = {list = [a;b;c]}
// Print it
printfn "%A" m
// Update the model
let m2 =
let mapper (v : MyItem) =
// Grabs the nest value using lens composition
let nestedValue = v |> get (MyItem.valueL --> MyValue.valueL)
let isSelected = nestedValue = "B"
// Set the nested isSelected using lens composition
v |> set (MyItem.selectionL --> MySelection.isSelectedL) isSelected
// Maps nested list property
m |> over MyModel.listL (List.map mapper)
printfn "%A" m2
0
推荐阅读
- svg - 响应式 SVG(调整大小后截断)
- javascript - 检查 React 中哪个元素是 ":active"
- java - 使用循环填充具有所需属性的数组
- vue.js - 为什么以这种方式导入组件时会出错?我在代码中遗漏了什么吗?
- computer-vision - Yolov4 调整大小
- android - 导航栏中奇怪的颜色块与 Flutter 中的某些颜色
- nginx - 在 NGINX 中使用 URI 路由 websocket
- encryption - 蓝牙用于加密数据的协议是什么?
- java - 如何将 Blazegraph 数据库转储/保存到只有断言三元组的文件中?
- r - 在 cbind 两个数据框后保留一列