Reputation: 61
I created a list in f# named tickets that contains 10 records called Ticket.The records are all initialized with their specific seat number and empty customer name.
type Ticket = {seat:int; customer:string}
let mutable tickets = [for n in 1..10 -> {Ticket.seat = n; Ticket.customer = ""}]
I want to write a function to book a specific seat in the list(add a customer name to the seat). How can i edit an item in the list and have other items still retain their value
Upvotes: 5
Views: 3515
Reputation: 10781
I think the most idiomatic option here would be to simply use a string array
. Given you know in advance the size and you want to be able to update it, this is the structure that fills both those needs most idiomatically. So,
// create 10-element string array initialized with null
let (tickets : string array) = Array.zeroCreate 10
...
tickets.[3] <- "New Customer"
keep it simple.
Granted this is not "purely-functional" (but any purely functional solutions here just kick the non-pure parts down the road anyway), but if the goal is just to get the job done, this will do it.
Upvotes: 0
Reputation: 52290
Here is a way to use a list of mutable (ref-cells) 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"
obvious you can make tickets
itself mutable too, if you want to add
seats instead of initializing them with all the obvious seats
Still I think that this is the wrong way to do it - I think you want a Map:
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 }
note that these are all immutable values, so bookSeat
will return a new Ticket
-reservation-map
Dictionary
or you can use a common .net Dictionary
and mutate this:
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 }
Here you don't have to pass the Tickets
around - they will be mutated in place (but still the Ticket
-objects themselves are still immutable)
Note that this right now is not thread-safe so be careful.
Upvotes: 0
Reputation: 243096
The functional F# list type is immutable, which means that you cannot change it. The typical way of working with it would be to return a new, modified, copy.
To do that, you can write a function bookSeat
that takes list<Ticket>
together with number & new name and produces a new list<Ticket>
with that one field updated:
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
Upvotes: 6