F# List split,divide - list

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

Related

F# list compare

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.

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 extract an element from a tuple in haskell

I have the following tuples:
type Id = Int
type Name = String
type FSK = Int
type Movie = (Id, Name, FSK)
now I want to define a function that extracts a movie from a given list like that:
extract :: Id -> [Movie] -> (Maybe Movie, [Movie])
extract = .... ??
so that when I give an id and a list of Movies, it extracts:
1) Nothing +the given list, if the id is found
2) Just + the movie , and the new list without that movie, if the given id is found, and the movie is deleted from the list
example:
*Main> extract 0 [(1,"Matrix",16),(2,"Gladiator",0)]
(Nothing,[(1,"Matrix",16),(2,"Gladiator",0)])
*Main> extract 1 [(1,"Matrix",16),(2,"Gladiator",0)]
(Just (1,"Matrix",16),[(2,"Gladiator",0)])
How should I define the function?
extract :: Id -> [Movie] -> (Maybe Movie, [Movie])
extract id [] = (Nothing, [])
extract id ((fid, n, f):list) | fid == id = (Just (fid, n, f), list)
| otherwise = (found, (fid, n, f):tail)
where (found, tail) = extract id list

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