F# list compare - list

I'm asking for help to solve a programming exercice in F#. I have to create a list where books and movies are listed in. All the books that have the same name as the movies sbould be listed in another list. I link what I've done until now and what the inputs are and what results i should get. Thank you in advance.
type Movie =
{ movieName: string
duration: Nat
fileSize: Nat }
type Book =
{ bookName: string
pages: Nat }
type Activity =
| Watch of Movie
| Read of Book
let booksWithMovie(activities: Activity list): Book list =
match activities with
| [] -> []
| [Read book] -> match activities with
| x :: xs -> match x with
| Watch same -> if (same.bookName = same.movieName) then [same] else
booksWithMovie(xs)
Here are the inputs:
Set.ofList (booksWithMovie [
Read { bookName = "The Hobbit"; pages = 304N }
Watch { movieName = "The Fellowship of the Ring"; duration = 228N; fileSize = 50N }
Read { bookName = "The Name of the Wind"; pages = 662N }
Watch { movieName = "The Emoji Movie"; duration = 86N; fileSize = 1024N }
Watch { movieName = "The Hobbit"; duration = 164N; fileSize = 9001N }
Read { bookName = "The Fellowship of the Ring"; pages = 700N }
And that's the result I should get:
Set.ofList [
{ bookName = "The Hobbit"; pages = 304N }
{ bookName = "The Fellowship of the Ring"; pages = 700N }

Since this looks like a learning exercise, rather than an actual problem (correct me if I'm wrong), I will try to give you a hint so that you can find the solution yourself, rather than just giving the solution.
As you mentioned in the comments, you wanted to iterate over all movies for every book (to check if a movie with the same title exists). This is a good plan. The best way to implement it is to use two recursive functions - one to walk over books and another to walk over movies (looking for a movie with a specific title).
The structure of the code should look something like this:
let rec movieWithTitleExists title (activities:Activity list) =
match activities with
| [] -> false
| Watch movie :: xs when movie.movieName = title -> (...)
| x :: xs -> (...)
let rec booksWithMovie (activities: Activity list): Book list =
match activities with
| [] -> []
| Book book :: xs when movieWithTitleExists book.bookName -> (...)
| x :: xs -> (...)
I left a couple of things out, so that you can still learn something from completing the exercise. However, I hope the example of the syntax helps! In movieWithTitleExists, we are looking for movie such that it has the specified title. In booksWithMovie, we are looking for book such that the title is also a movie name.
Filling the (...) in movieWithTitleExists should be easier - you want to return a boolean value, so you either need to return a constant or make a recursive call.
In booksWithMovie, you want to return a list of books, so you will need to call the function recursively and then either just return that, or append the current book to the front using the :: operator.

Related

F# List split,divide

i'm trying to solve an exercice in F#. I have to write a code that can differentiate between a book and a movie, and put it as book or a movie in a list. It can differentiate both by the filesize because books have no filesize. For exemple, if I put a Book in, the code has to add it in the list as a Book and same for the movie. I link a sample result and the input. Thank you in advance.
type Movie =
{ movieName: string
duration: Nat
fileSize: Nat }
type Book =
{ bookName: string
pages: Nat }
type Activity =
| Watch of Movie
| Read of Book
let rec createActivities(hl: (string * Nat * Nat) list): Activity list =
match hl with
| [] -> []
| x::xs -> ....
Here are the inputs:
createActivities([
"The Hobbit" , 304N, 0N
"The Fellowship of the Ring", 228N, 50N
"The Name of the Wind" , 662N, 0N
"The Emoji Movie" , 86N , 1024N
"The Hobbit" , 164N, 9001N
"The Fellowship of the Ring", 700N, 0N
Result:
[
Read { bookName = "The Hobbit"; pages = 304N }
Watch { movieName = "The Fellowship of the Ring"; duration = 228N; fileSize = 50N }
Read { bookName = "The Name of the Wind"; pages = 662N }
Watch { movieName = "The Emoji Movie"; duration = 86N; fileSize = 1024N }
Watch { movieName = "The Hobbit"; duration = 164N; fileSize = 9001N }
Read { bookName = "The Fellowship of the Ring"; pages = 700N }
]
Match expressions in F# can be quite advanced, with sub-matches inside various parts of the match expression. For example, the x::xs case in your match expression can be turned into (name, duration, filesize) :: xs. And if you specify a value for one of those, then it would only match when that part of the tuple had that value. With that in mind, I'd write your match expression as something like this:
let rec createActivities(hl: (string * Nat * Nat) list): Activity list =
match hl with
| [] -> []
| (name, pages, 0N) :: xs -> Read { bookName = name; pages = pages } :: createActivities xs
| (name, duration, fileSize) :: xs -> Watch { movieName = name; duration = duration; fileSize = fileSize } :: createActivities xs
How this works is that the match cases will be processed in order from top to bottom, and the first one that matches will be used. So if the tuple as a 0N as its third element, the second match case will be used, otherwise the third match case will be used. So the match expression can be kept quite simple and clean-looking.
I could resolve the Problem like this. Thanks for help.
let rec createActivities(hl: (string * Nat * Nat) list): Activity list =
match hl with
| [] -> []
| (name, pagesorduration, Size) :: xs ->
if Size = 0N then Read { bookName = name; pages = pagesorduration } :: createActivities(xs)
else Watch { movieName = name; duration = pagesorduration; fileSize = Size } :: createActivities xs

F# Nested lists immutability

I am trying to pick F# again and I have a case where I find it very difficult to deal with immutability.
In most cases I find immutability very nice. However, I find it very hard to deal with nested collections.
I would like to write a single user GUI program.
For simplicity let’s say we have the model:
type Employee =
{
Name : string
}
type Company =
{
Name : string
Employees : Employee list
}
If I modify an existing Employee with
let myNewEmployee = { myEmployee with Name = "John Smith" }
I get a new Employee and that is fine. However, here comes the chain of problems:
I need to remove the old myEmployee and add myNewEmployee to the
Company’s Employees list.
That provokes a mutation in the list with yields a new list.
That forces me to create a new Company record in order to inject the new collection, having to rebuild entire Company list I have.
In other words, changing an employee’s name, makes me rebuild the entire data structure.
Every time I am stumped, I have found that F# has a different and creative way of doing things.
I assume that it is my ignorance on not knowing how to deal with this situation in a functional way, please enlighten me ;)
Should I be using other F# libraries, like F# Data ?
TIA,
David
I think the answer depends partly on the larger context - such as how is the user interface of your application implemented. You are right that modifying one name in a company requires you to produce a new list and a new company record. That is not too bad if you do it in a single call to List.map though.
To make the sample easier, I added ID of type int to each emplyee:
let updateName id name company =
let newEmployees = company.Employees |> List.map (fun emp ->
if emp.ID = id then { emp with Name = name } else emp)
{ company with Employees = newEmployees }
If you are using something like the Elm architecture, then this is probably a reasonable way to go.
In some cases, you can do a more clever thing (but it depends on your scenario). For example, you could create a new type to represent a company with a list of updates that have been applied to it:
type Update =
| Rename of id:int * newName:string
type UpdatedCompany =
{ Company : Company
Updates : Update list }
Now changing a name is just a matter of appending a new Rename update to the list of Updates. Of course, once you need to display the final Company, you will need to iterate over all the employees (as above) and apply the updates. However, if you do a lot of updates before you need to get a new final Company value, this might be a nice trick.
The functional-first F# style means that you keep your functions pure by default i the core of your program and moving the necessary side-effects towards the edge of your program.
I need to remove the old myEmployee and add myNewEmployee to the Company’s Employees list.
Do you really need to do this? I encourage you to think about this chain going in the opposite direction. Why do you need this new list? What effect does that ultimately have on the outside world? Maybe the complete list can be built up in one pass by restructuring your code to a functional style. Maybe you are responding to user input and it's fine to build a new list every time. Maybe it would be better to use a different data structure like a Map (immutable dictionary) with the employee ID as the key.
Long entry with no clear answers but some ideas on how to deal with nested immutable data
As the OP noted, when discussing immutability an area that is ignored how to update nested properties. While this is trivial in mutable structure (just navigate there and update the property) with immutable structures one need to navigate there, create a copy with the updated property and then recreate all parents.
Luckily people have been thinking about this already and the functional pattern to address this is called Lenses or Prisms.
Lenses have a reputation of being somewhat difficult.
But that IMO is because in Haskell they talk about polymorphic lenses which can be defined like this:
type Lens s t a b = forall f. Functor f => (a -> f b) -> (s -> f t) // Que?
Braver souls than me have implemented polymorphic lenses in F# (which has a more simplistic type system than Haskell): http://www.fssnip.net/7Pk
(The polymorphic lens implementation in F# is provided AFAIK by the guy behind Hopac. He's pretty decent I say)
Polymorphic lenses are cool but if you remove the polymorphic bit lenses become much simpler to understand and still very usable.
Basically a lens is a pair of a getter and a setter function. It could look like this in F#:
type Lens<'T, 'U> = ('T -> 'U)*('T -> 'U -> 'T)
Given a value the getter get a property of that value. Given a value and property value the setter creates a new copy of the value with the property updated.
It can be thought of as a functional composable properties remotely comparable to .NET properties.
Given that your example (and many real-world things) deals with maps and lists Prisms are typically more usable. Here is one proposal:
type [<Struct>] Prism<'T, 'U> = Prism of ('T -> 'U option)*('T -> 'U -> 'T)
The only difference here is that the getter might return None if the property don't exists, for example if the employee don't exist in the list.
For a prism we define the operator >-> which combines a two prisms into a new one that let's you get the focused property as well as updating it like so:
let p = PropertyA >-> PropertyB >-> Property C
// Updates the nested property c in b in a and returns a new instance
let newA = a |> set p c
Let's see how this could look for the example in OPs post.
type Company =
{
Name : string
Employees : Map<EmployeeNo, Employee>
}
// Define Prisms for properties of Company
static member _Name : Prism<Company, _> = prism' (fun t v -> { t with Name = v }) (fun t -> t.Name |> Some)
static member _Employees : Prism<Company, _> = prism' (fun t v -> { t with Employees = v }) (fun t -> t.Employees |> Some)
Unfortunately there is a bit of boiler-plate code surrounding prisms, but that can be mitigated with code-gen tools and possibly even type-providers.
We define Employee in a similar manner and we can start defining functions that allows us manipulating the nested immutable structure.
// Uses Prisms to update the email
let updateEmail company employeeNo newEmail =
company
// The path to the Employee email
|> set (Company._Employees >-> lookup employeeNo >-> Employee._Email) newEmail
Prisms are chainable useful when updating more than one property.
// Uses Prisms to update the position and salary
let updatePosition company employeeNo newPosition newSalary =
company
// The path to the Employee position
|> set (Company._Employees >-> lookup employeeNo >-> Employee._Position) newPosition
// The path to the Employee salary
|> set (Company._Employees >-> lookup employeeNo >-> Employee._Salary ) newSalary
While the example above works it's inefficient to construct two Company objects and throw away the first. Better would be to navigate to the correct Employee and update it before updating the Company object.
// Uses Prisms to update the position and salary
// Does so in a more efficient manner
let updatePosition' company employeeNo newPosition newSalary =
// The problem with updatePosition above is that it constructs a new company
// object with position updated and then another one with the salary updated
// A faster approach is to navigate to the employee and once found
// update both the position and the salary
// Updates an employee position & salary
let updater = function
| None -> None
| Some e -> { e with Position = newPosition; Salary = newSalary} |> Some
company
// The path to the employee
|> update (Company._Employees >-> lookup employeeNo) updater
In conclusion; the implementation given here is intended to be a starting point for perhaps a different way of thinking on how to work with nested immutable structures. There are some issues with the implementation (like knowing if and why an update failed). This is solvable but I didn't want to pollute the idea with lots of worries.
Hopefully you found it interesting.
Full sample code:
// A Prism consists of two parts
// a getter that gets a property of a value (might return None)
// a setter that sets a property of a value (returns a new instance)
type [<Struct>] Prism<'T, 'U> = Prism of ('T -> 'U option)*('T -> 'U -> 'T)
module Prism =
let inline prism g s = Prism (g, s)
let inline prism' s g = Prism (g, s)
// join joins two Prisms into a new Prism, this is how we navigate nested structures
// Note: Creates in addition to a nested getter also a nested setter so a Prism
// allows both getting and setting of nested properties
let inline join (Prism (tg, ts)) (Prism (ug, us)) =
let getter t =
match tg t with
| None -> None
| Some tv -> ug tv
let setter t v =
match tg t with
| None -> t
| Some tv -> ts t (us tv v)
prism getter setter
// Prism that allows us to navigate Maps
let inline lookup key =
let getter m = Map.tryFind key m
let setter m v = Map.add key v m
prism getter setter
// Given a Prism and a value returns the nested property pointed out by the prism
let get (Prism (tg, _)) t = tg t
// Given a Prism and a value sets the nested property pointed out by the prism
let set (Prism (_, ts)) v t = ts t v
// Given a Prism and a value allows an update function to see the nested property
// and return update it
let update (Prism (tg, ts)) u t =
match u (tg t) with
| None -> t
| Some tv -> ts t tv
type Prism<'T, 'U> with
static member inline ( >-> ) (t, u) = Prism.join t u
module Demo =
open System
open Prism
// Our Domain Model
type [<Struct>] EmployeeNo = EmployeeNo of int
type Position = Contractor | IndividualContributor | Manager
// So prisms enforces some measure of boiler plating.
// Can be mitigated by code generations and possibly type providers
type Employee =
{
No : EmployeeNo
Name : string
Email : string
Hired : DateTime
Salary : decimal
Position : Position
}
// Define Prisms for properties of Employee
static member _No = prism' (fun t v -> { t with No = v }) (fun t -> t.No |> Some)
static member _Name = prism' (fun t v -> { t with Name = v }) (fun t -> t.Name |> Some)
static member _Email = prism' (fun t v -> { t with Email = v }) (fun t -> t.Email |> Some)
static member _Hired = prism' (fun t v -> { t with Hired = v }) (fun t -> t.Hired |> Some)
static member _Salary = prism' (fun t v -> { t with Salary = v }) (fun t -> t.Salary |> Some)
static member _Position = prism' (fun t v -> { t with Position = v }) (fun t -> t.Position |> Some)
type Company =
{
Name : string
Employees : Map<EmployeeNo, Employee>
}
// Define Prisms for properties of Company
static member _Name : Prism<Company, _> = prism' (fun t v -> { t with Name = v }) (fun t -> t.Name |> Some)
static member _Employees : Prism<Company, _> = prism' (fun t v -> { t with Employees = v }) (fun t -> t.Employees |> Some)
open Prism
// Uses Prisms to update the email
let updateEmail company employeeNo newEmail =
company
// The path to the Employee email
|> set (Company._Employees >-> lookup employeeNo >-> Employee._Email) newEmail
// Uses Prisms to update the position and salary
let updatePosition company employeeNo newPosition newSalary =
company
// The path to the Employee position
|> set (Company._Employees >-> lookup employeeNo >-> Employee._Position) newPosition
// The path to the Employee salary
|> set (Company._Employees >-> lookup employeeNo >-> Employee._Salary ) newSalary
// Uses Prisms to update the position and salary
// Does so in a more efficient manner
let updatePosition' company employeeNo newPosition newSalary =
// The problem with updatePosition above is that it constructs a new company
// object with position updated and then another one with the salary updated
// A faster approach is to navigate to the employee and once found
// update both the position and the salary
// Updates an employee position & salary
let updater = function
| None -> None
| Some e -> { e with Position = newPosition; Salary = newSalary} |> Some
company
// The path to the employee
|> update (Company._Employees >-> lookup employeeNo) updater
let test () =
// The initial state of the company
let company : Company =
let e no name email year month day salary position =
let eno = EmployeeNo no
let e : Employee =
{
No = eno
Name = name
Email = email
Hired = DateTime (year, month, day)
Salary = salary
Position = position
}
eno, e
let es =
[|
e 1 "Bill Gates" "billg#microsoft.com" 1979 1 1 100000M Manager
e 2 "Melinda Gates" "melindag#microsoft.com" 1985 6 6 20000M IndividualContributor
|] |> Map.ofArray
{ Name = "Microsoft"; Employees = es}
// Does some organizational changes of the company
printfn "Initial: %A" company
let company = updateEmail company (EmployeeNo 1) "billg#hotmail.com"
printfn "Changing Bill Gates email: %A" company
let company = updatePosition company (EmployeeNo 2) Manager 200000M
printfn "Promoting Melinda Gates: %A" company
let company = updatePosition' company (EmployeeNo 1) IndividualContributor 10000M
printfn "Demoting Bill Gates: %A" company

Updating a single value in a record, within a collection of records Haskell

So I have a custom data type Person
data Person = Person{ fname :: String
, lname :: String
, age :: Int
, siblings :: [String]
}
I have a list of this type, foo = [Person].
I'm trying to update a particular Person. My process is to match their fname (Assuming each name is unique) and then to update their siblings values.
addSiblingToPerson :: String -> String -> [Person] -> [Person]
addSiblingToPerson siblingParam fnameParam fooParam =
I'm really struggling to think 'functionally', if I were to do this in an imperative language I could go through each item in [Person] checking to see if name == fname then this.siblings push newSibling (Or something along those lines)
I know how to update a record in haskell but I want to return the list of Person after updating a single person in the collection of Person.
I just can't wrap my head around how to 'think Haskell'
Thank you :(
You shouldn't think about "updating" something, even though we use that terminology, unless you have a mutable reference and are working in the IO monad. In this situation the thought process should be "how do I compute a new list that is exactly like the previous one except...".
You could either update a single entry or map a modification function across the entire list. Lets look at the manual, single entry, solution first:
addSiblingToPerson :: String -> String -> [Person] -> [Person]
addSiblingToPerson siblingParam fnameParam allPeople =
case allPeople of
[] -> []
(p:ps) | fname p == fnameParam ->
p { siblings = siblingParam : siblings p } : ps
| otherwise ->
p : addSiblingToPerson siblingParam fnameParam ps
That is, we traverse the list, keeping any non-matching people and updating the first person with a matching fname, being sure to include the rest of the list.
The map solution is functionally different - it will update all people who share the given fname and it will traverse the whole list.
addSiblingToPerson :: String -> String -> [Person] -> [Person]
addSiblingToPerson siblingParam fnameParam allPeople =
let update p | fname p == fnameParam = p { siblings = siblingParam : siblings p }
| otherwise = p
in map update allPeople

How to edit an Item in a mutable list in f# and allow the other items in the list retain their values?

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
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
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
a better approach(?)
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
hyprid using 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.
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.

How to find value of type from list

So im in the early stages of learning how to use functional programming and I ran into this problem when I tried to compare a string with a string in a list, so that I could get the matching patterns.
Here is my code:
F# Code
type name = string;;
type number = string;;
type sex = string;;
type year = int;;
type interest = string list;;
type criteria = (sex * year * interest) list;;
type Register = (name * number * criteria) list;;
let reg = [("Lars","28551086",("male",1992,["soccer";"golf"])); ("Hanne","28598653",("female",1989,["cooking";"jewelry"]));
("Viktor","26587297",("male",1973,["clothes";"soccer"])); ("Henrik","22157864",("male",1985,["internet";"facebook"]));
("Lotte","23589462",("female",1997,["bombing";"internet"])); ("Susanne","25896742",("female",1923,["soccer";"cooking"]));
("Marie","22658943",("female",1975,["clothes";"jewelry"])) ];;
let rec findYear n = function
| [] -> failwith("No person with that year is registrered")
| (name,_,(_,n',_)) when n = n' -> name
| (name,_,(_,n',_))::tail when n <> n' -> findYear(tail);;
What im trying to do, is to retrieve all the people in the reg that has the same name as the one im searching for.
So a F# Interactive call could be:
findYear 1992;;
And then it should give me the details of the persons with that year. Im not sure how to search through my reg
I think you just forgot the n (and the tail of a list) here:
let rec findYear n = function
| [] -> failwith("No person with that year is registrered")
| (name,_,(_,n',_)) when n = n' -> name // forgot tail
| (name,_,(_,n',_))::tail when n <> n' -> findYear(tail) // forgot n here
(should have gotten an error
try this:
let rec findYear n = function
| [] -> failwith("No person with that year is registrered")
| ((name,_,(_,n',_))::_) when n = n' -> name
| ((_,_,(_,n',_))::tail) when n <> n' -> findYear n tail
making this a bit better
you don't need to check again
you don't need to recheck the year if the second pattern did not match:
let rec findYear n = function
| [] -> failwith("No person with that year is registrered")
| ((name,_,(_,n',_))::_) when n = n' -> name
| (_::tail) -> findYear n tail
option is better than an exception
The way you handle the case where you don't find a person with this year tells us that your function is "partial" (does not return for every input) - so just make it total again by using option:
let rec findYear n = function
| [] -> None
| ((name,_,(_,n',_))::_) when n = n' -> Some name
| (_::tail) -> findYear n tail
This will not throw and tell the user: "hey I might fail so better handle this!"
use records / ADTs
While your tuples are fine they are not really readable (hard to check if your pattern is ok for example) - why not use records and algebraic-data-types:
type Name = string
type Number = string
type Gender = Male | Female // add more if you need
type Year = int
type Interests = string list
type Criteria = { gender : Gender; year : Year; interests : Interests }
type Register = { name : Name; number : Number; criteria : Criteria }
let reg =
[ { name = "Lars"
; number = "28551086"
; criteria = { gender = Male; year = 1992; interests = ["soccer";"golf"] }
}
// ...
]
and use this:
let rec findYear n =
function
| [] -> None
| (reg::_) when reg.criteria.year = n'
-> Some reg
| (_::regs)
-> findYear n regs
use the List module
What you do here is a very common pattern and it's already implemented (List.tryFind) - so why not use it?
let findYear n =
let hasYear (reg : Register) = reg.criteria.year = n
List.tryFind hasYear
of course you can add the missing parameter if you don't really understand partial application yet:
let findYear n regs =
let hasYear (reg : Register) = reg.criteria.year = n
List.tryFind hasYear regs
finally let's give this a better name
this is of course just me not liking findYear if you really find a registration
// rest is the same
type Registration = { name : Name; number : Number; criteria : Criteria }
let firstRegistrationWithYear year =
let hasYear (reg : Register) = reg.criteria.year = year
List.tryFind hasYear
finding all registrations for one year
let filterRegistrationWithYear year =
let hasYear (reg : Register) = reg.criteria.year = year
List.filter hasYear
or if you want a (tail-recursive) implementation using continuation-passing style (the other answer has the accumulator aproach):
let filterYear n regs =
let rec filter regs cont =
match regs with
| [] -> cont []
| (reg::regs) when reg.criteria.year = n'
-> filter regs (fun res -> reg::res |> cont)
| (_::regs)
-> filter regs cont
filter regs id
remark:
I would not advise implementing this kind of stuff yourself - it's better to use the provided stuff from List (it's for example more performant as this, because I tried to show you how to do it CPS-style)
If you want to use recursion, you can add additional parameter (accumulator), to collect results:
let rec findYear n acc = function
| [] -> acc
| ((name,_,(_,n',_)) as h)::tail when n = n' -> findYear n (h::acc) tail
| h::tail -> findYear n acc tail
And call it this way:
findYear 1973 [] reg
Or you could use the 'filter' function from the List library functions:
let findYear' n lst =
lst |> List.filter (fun (name,_,(_,n',_)) -> n = n')
And call it this way:
findYear' 1973 reg