access data from a record in a discriminated union - list

Having this setup, how do I loop through list and print data about each employee?
type Person =
{ first_name: string
last_name: string
age: int
salary_hour: int }
type Employee =
| Administrator of Person
| OfficeWorker of Person
| WarehouseWorker of Person
let emps =
[ Administrator
{ first_name = "name"
last_name = "name"
age = 19
salary_hour = 200 }]

Typically, I'd recommend breaking this into pieces.
Start with a function for each portion, such as a function to print each person (or convert to string, which is often easier to reuse and compose), then a second function that uses that to convert an employee to a string.
Finally, you can use List.iter to iterate your list to print:
let personToString p =
sprintf "%s %s [%d] - Salary %d" p.first_name p.last_name p.age p.salary_hour
let employeeToString e =
match e with
| Administrator a -> sprintf "Administrator: %s" (personToString a)
| OfficeWorker o -> sprintf "Office: %s" (personToString o)
| WarehouseWorker w -> sprintf "Warehouse: %s" (personToString w)
emps |> List.iter (fun e -> employeeToString e |> printfn "%s")

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# Traversing list of different types

I have five different types:
type Name = string
type PhoneNumber = int
type Sex = string
type YearOfBirth = int
type Interests = string list
type Client = Name * PhoneNumber * Sex * YearOfBirth * Interests
Which represent clients. Then let's say I have three of these clients:
let client1 = "Jon", 37514986, "Male", 1980, ["Cars"; "Sexdolls"; "Airplanes"]
let client2 = "Jonna", 31852654, "Female", 1990, ["Makeup"; "Sewing"; "Netflix"]
let client3 = "Jenna", 33658912, "Female", 1970, ["Robe Swinging"; "Llamas"; "Music"]
let clients = [client1; client2; client3]
How would I go about searching through clients for a certain element? Say, I have a method where I want to get the names of the clients with the same sex as me? I've written the below function for at least determining whether the input sex is the same but that doesn't cut it apparently.
let rec sexCheck sex cs =
match cs with
| [] -> []
| c::cs -> if sex = c then sex else sexCheck sex cs
sexCheck "Male" clients
Any hints?
You can accumulate the results in another parameter, like this:
let sexCheck sex cs =
let rec loop acc (sex:string) cs =
match cs with
| [] -> acc
| ((_, _, s, _, _) as c)::cs -> loop (if sex = s then c::acc else acc) sex cs
loop [] sex cs
As usual, I would like to remind you what's the easiest way, by using the provided functions in F#:
clients |> List.filter (fun (_, _, c, _, _) -> c = "Male")

OCaml pattern match

in ast.ml, the structure is below:
type beantype =
| Bool
| Int
| TLr of fieldsrec
| TId of ident
and fieldsrec = { fields : field list }
and field =
| FIe of (ident * beantype)
in printer.ml, i use it like below:
let rec print_bean fmt = function
| Bool -> put fmt "%s" "bool"
| Int -> put fmt "%s" "int"
| TLr f -> put fmt "%s" "{"; print_fieldsrec fmt f ; put fmt "%s" "}"
| TId id -> put fmt "%s" id
and print_fieldsrec fmt = function
| f :: fe -> print_field fmt f; put fmt "%s" "," ; print_fieldsrec fmt fe
and print_field fmt = function
| FIe (id, beantype) -> put fmt "%s" id; put fmt "%s" ":"; print_bean fmt beantype
However it said the different pattern match in print_fieldsrec
Error: This pattern matches values of type 'a list
but a pattern was expected which matches values of type
Bean_ast.fieldsrec
how can i change the printer.ml?
You seem to be confused by the type fieldsrec = { fields : field list }. You should have followed Jeffrey's advice of using | Fields of field list instead.
fieldsrec is not a list, it is a record containing a list, so
print_fieldsrec fmt = function f :: fe -> ...
doesn't have the type its name suggests.
Also you forgot the base case for the recursive print_fieldsrec.

F# records and list sorting

I am fairly new to F# and am messing around with records and ways to store/sort them. What i have now is a this, which works fairly nicely... i think. Now seeing how this is a Functional first language i wanted to try and do a selection sort of this list of records i have made. Is there way to access the elements in the records so that i can sort by age? or am i thinking about this in the wrong way
module RecordTypes =
type Student =
{
Name : string
mutable age : int
mutable major : string
}
let studentOne = { Name = "bob" ; age = 20 ; major = "spanish" }
let studentTwo= { Name = "sally" ; age = 18 ; major = "english" }
let studentThree = { Name = "frank" ; age = 22 ; major = "history" }
let studentFour = { Name = "lisa" ; age = 19 ; major = "math" }
let studentFive = { Name = "john" ; age = 17 ; major = "philosophy" }
// studentFive.age <- studentFive.age + 2
let studentList = [studentOne; studentTwo; studentThree ;studentFour; studentFive]
studentList |> List.iter (fun s-> printf "Name: %s, Age: %d, Major: %s\n" s.Name s.age s.major)
let rec findStudent s =
match s with
| [] -> None
| x :: xs -> if studentList.Name then return true else findStudent xs
You can use member access:
studentList
|> List.sortBy (fun s -> s.age)
|> List.iter (fun s -> printf "Name: %s, Age: %d, Major: %s\n" s.Name s.age s.major)
or pattern matching (preferred IMO):
studentList
|> List.sortBy (fun { age=a } -> a)
|> List.iter (fun { Name=n; age=a; major=m } -> printf "Name: %s, Age: %d, Major: %s\n" n a m)
You can pipe your list through List.sortBy before doing List.iter:
studentList
|> List.sortBy (fun s -> s.age)
|> List.iter (fun s -> printf "Name: %s, Age: %d, Major: %s\n" s.Name s.age s.major)
As you can see, it takes a function to be used to get a value used for sorting. Passing (fun s -> s.age) will make the list be sorted by Age.

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