如何在 f# 中编辑可变列表中的项目并允许列表中的其他项目保留其值?
How to edit an Item in a mutable list in f# and allow the other items in the list retain their values?
我在 f# 中创建了一个名为 tickets 的列表,其中包含 10 条名为 Ticket.The 的记录,所有记录都使用其特定的座位号和空的客户名称进行了初始化。
type Ticket = {seat:int; customer:string}
let mutable tickets = [for n in 1..10 -> {Ticket.seat = n; Ticket.customer = ""}]
我想写一个函数来预订列表中的特定座位(向座位添加客户名称)。
我怎样才能编辑列表中的项目并让其他项目仍然保留其价值
函数式 F# 列表类型是不可变的,这意味着您无法更改它。使用它的典型方法是 return 一个新的、修改过的副本。
为此,您可以编写一个函数 bookSeat
,将 list<Ticket>
与数字和新名称一起生成一个新的 list<Ticket>
并更新该字段:
let bookSeat seatNo name tickets =
tickets |> List.map (fun ticket ->
if ticket.seat = seatNo then { ticket with customer = name }
else ticket )
bookSeat 3 "Tomas" tickets
这是使用可变(引用单元)票证列表的方法:
let maxSeats = 10
let tickets : (int * Ticket option) ref list =
[1..maxSeats]
|> List.map (fun s -> ref (s, None) )
let bookSeat seat name =
match List.tryFind (fun r -> let (s,_) = !r in s = seat) tickets with
| Some r ->
r :=
match !r with
| (s, Some ticket) -> (s, Some { ticket with customer = name })
| (s, None) -> (s, Some { seat = s; customer = name })
| None ->
failwith "seat not found"
显然你也可以让 tickets
本身可变,如果你想 add
席位而不是用所有明显的席位初始化它们
更好的方法(?)
我仍然认为这是错误的做法 - 我认为您需要地图:
type Ticket = {seat:int; customer:string}
type Tickets = Map<int, Ticket>
let bookSeat seat name (tickets : Tickets) =
match Map.tryFind seat tickets with
| Some oldTicket ->
tickets
|> Map.remove seat
|> Map.add seat { oldTicket with customer = name }
| None ->
tickets
|> Map.add seat { seat = seat; customer = name }
请注意,这些都是不可变的值,因此 bookSeat
将 return 一个新的 Ticket
-reservation-map
hyprid 使用 Dictionary
或者您可以使用通用的 .net Dictionary
并对其进行修改:
type Ticket = {seat:int; customer:string}
let tickets = System.Collections.Generic.Dictionary<int, Ticket>()
let bookSeat seat name =
match tickets.TryGetValue seat with
| (true, oldTicket) ->
tickets.[seat] <- { oldTicket with customer = name }
| (false, _) ->
tickets.[seat] <- { seat = seat; customer = name }
在这里你不必传递 Tickets
- 它们会在原地发生变化(但 Ticket
-对象本身仍然是不可变的)
请注意,目前这不是线程安全的,所以要小心。
我认为这里最惯用的选项是简单地使用 string array
。如果您事先知道大小并且希望能够对其进行更新,那么这是最符合这两种需求的结构。所以,
// create 10-element string array initialized with null
let (tickets : string array) = Array.zeroCreate 10
...
tickets.[3] <- "New Customer"
保持简单。
当然这不是 "purely-functional"(但这里的任何纯功能性解决方案无论如何都会将非纯部分踢到一边),但如果目标只是完成工作,那么这就可以了.
我在 f# 中创建了一个名为 tickets 的列表,其中包含 10 条名为 Ticket.The 的记录,所有记录都使用其特定的座位号和空的客户名称进行了初始化。
type Ticket = {seat:int; customer:string}
let mutable tickets = [for n in 1..10 -> {Ticket.seat = n; Ticket.customer = ""}]
我想写一个函数来预订列表中的特定座位(向座位添加客户名称)。 我怎样才能编辑列表中的项目并让其他项目仍然保留其价值
函数式 F# 列表类型是不可变的,这意味着您无法更改它。使用它的典型方法是 return 一个新的、修改过的副本。
为此,您可以编写一个函数 bookSeat
,将 list<Ticket>
与数字和新名称一起生成一个新的 list<Ticket>
并更新该字段:
let bookSeat seatNo name tickets =
tickets |> List.map (fun ticket ->
if ticket.seat = seatNo then { ticket with customer = name }
else ticket )
bookSeat 3 "Tomas" tickets
这是使用可变(引用单元)票证列表的方法:
let maxSeats = 10
let tickets : (int * Ticket option) ref list =
[1..maxSeats]
|> List.map (fun s -> ref (s, None) )
let bookSeat seat name =
match List.tryFind (fun r -> let (s,_) = !r in s = seat) tickets with
| Some r ->
r :=
match !r with
| (s, Some ticket) -> (s, Some { ticket with customer = name })
| (s, None) -> (s, Some { seat = s; customer = name })
| None ->
failwith "seat not found"
显然你也可以让 tickets
本身可变,如果你想 add
席位而不是用所有明显的席位初始化它们
更好的方法(?)
我仍然认为这是错误的做法 - 我认为您需要地图:
type Ticket = {seat:int; customer:string}
type Tickets = Map<int, Ticket>
let bookSeat seat name (tickets : Tickets) =
match Map.tryFind seat tickets with
| Some oldTicket ->
tickets
|> Map.remove seat
|> Map.add seat { oldTicket with customer = name }
| None ->
tickets
|> Map.add seat { seat = seat; customer = name }
请注意,这些都是不可变的值,因此 bookSeat
将 return 一个新的 Ticket
-reservation-map
hyprid 使用 Dictionary
或者您可以使用通用的 .net Dictionary
并对其进行修改:
type Ticket = {seat:int; customer:string}
let tickets = System.Collections.Generic.Dictionary<int, Ticket>()
let bookSeat seat name =
match tickets.TryGetValue seat with
| (true, oldTicket) ->
tickets.[seat] <- { oldTicket with customer = name }
| (false, _) ->
tickets.[seat] <- { seat = seat; customer = name }
在这里你不必传递 Tickets
- 它们会在原地发生变化(但 Ticket
-对象本身仍然是不可变的)
请注意,目前这不是线程安全的,所以要小心。
我认为这里最惯用的选项是简单地使用 string array
。如果您事先知道大小并且希望能够对其进行更新,那么这是最符合这两种需求的结构。所以,
// create 10-element string array initialized with null
let (tickets : string array) = Array.zeroCreate 10
...
tickets.[3] <- "New Customer"
保持简单。
当然这不是 "purely-functional"(但这里的任何纯功能性解决方案无论如何都会将非纯部分踢到一边),但如果目标只是完成工作,那么这就可以了.