Created
April 1, 2014 18:21
-
-
Save jatesy/9919949 to your computer and use it in GitHub Desktop.
A hotel reservation system implemented in functional language(Standard ML)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(*The signature includes the total number of 2-double-bed romms,queen-bed rooms and king-bed rooms accordingly | |
in a hotel. Also the requirements of minimum number of nights and number limit of occupancy must be met by | |
any reservation*) | |
signature ROOMDETAIL = | |
sig | |
val doubleAvailable:int; | |
val queenAvailable:int; | |
val kingAvailable:int; | |
val minnights:int option; | |
val occupancyLimit:int; | |
end; | |
(*The functor use a structure with a signature of ROOMDETAIL to build a structure for a specified | |
hotel,in which the reservation system will be built up*) | |
functor MakeHotel(Q:ROOMDETAIL): | |
(*The signature exposes the full details of types resrecord and roomconfig so that the users | |
can use these definitions. It also exposes the existences of type ressys and other functions *) | |
sig | |
datatype roomconfig = DOUBLE_RM | QUEEN_RM | KING_RM; | |
exception Deny; | |
type resrecord = | |
{ | |
id:int, | |
first_name:string, | |
last_name:string, | |
check_in_date:int, | |
num_of_nights:int, | |
num_of_occu:int, | |
rm_config:roomconfig, | |
num_of_gst:int | |
};type ressys; | |
val empty: ressys; | |
val pred_date: int -> resrecord -> bool; | |
val length: 'a list -> int; | |
val filter: ('a -> 'b -> bool) -> 'a -> 'b list -> 'b list; | |
val exist: int -> resrecord list -> bool; | |
val reserve: ressys -> resrecord -> ressys; | |
val cancel: ressys -> int -> ressys; | |
val getInventory: ressys -> roomconfig -> int -> int; | |
val getInventorySpan: ressys -> roomconfig -> int -> int ->bool; | |
val completedStays: ressys -> int -> int; | |
val removeCompletedStays: ressys -> int -> ressys; | |
val restrictions: resrecord -> bool; | |
val guestQuantity: ressys -> int -> int; | |
val update_reserve: ressys -> resrecord -> ressys; | |
end = struct | |
(*The structure defines all the datatypes, exceptions, types and functions with implementation details*) | |
open Q; | |
(*Room configuration includes three types of rooms, namely, 2-double-bed romm,queen-bed room and | |
king-bed room*) | |
datatype roomconfig = DOUBLE_RM | QUEEN_RM | KING_RM; | |
(*Whenever an operation is denied by the reservation system, it will raise the exception of Deny*) | |
exception Deny; | |
(*type resrecord includes all information needed as a record of reservation*) | |
type resrecord = | |
{ | |
id:int, | |
first_name:string, | |
last_name:string, | |
check_in_date:int, | |
num_of_nights:int, | |
num_of_occu:int, | |
rm_config:roomconfig, | |
num_of_gst:int | |
}; | |
(*type ressys includes all information needed as a reservation system of a hotel*) | |
type ressys = | |
{ | |
double_rm_quantity:int, | |
queen_rm_quantity:int, | |
king_rm_quantity:int, | |
resv_record:resrecord list | |
}; | |
(*A helper function. Inputs are a date and a reservation record, output is whether the date range of the stay on the | |
given record includes the given date, as a boolean*) | |
fun pred_date a (rrc:resrecord) = (#check_in_date rrc) <= a andalso ( (#check_in_date rrc) + (#num_of_nights rrc) ) > a; | |
(*A helper function, which gets the length of a given list*) | |
fun length [] = 0 | |
| length(x::xs) = 1 + length xs; | |
(*A helper funtion. It gets elements for which function pred evaluates to ture from a list. Inputs: a list and a funtion | |
that returns boolean. Output: a list of elements on given list that can make pred envaluate to ture*) | |
fun filter pred i [] = [] | |
| filter pred i (r::rl) = if pred i r | |
then r::(filter pred i rl) | |
else filter pred i rl; | |
(*A helper function, which checks if an ID exists on a given list of reservation records. Inputs: a list of reservation | |
records and an ID. Output: Boolean*) | |
fun exist x [] = false | |
| exist x ((y::ys):resrecord list)= if x = (#id y) | |
then true | |
else exist x ys; | |
(*Function empty creats an empty reservation system using the quantity of available hotel rooms for each configiuration in the | |
signature ROOMDETAIL*) | |
val empty = { double_rm_quantity = Q.doubleAvailable,queen_rm_quantity = Q.queenAvailable, king_rm_quantity = Q.kingAvailable, | |
resv_record = [] }; | |
(*Function getInventory gives the number of rooms available to be reserved for the specified configiration for the given date | |
Input: a reserve system, a room configiration and date. Output:number of available rooms*) | |
fun getInventory (rsy:ressys) (rmcf:roomconfig) date = | |
let | |
fun pred_rmcf a (rrc:resrecord) = a = (#rm_config rrc); | |
in | |
if rmcf = DOUBLE_RM | |
then (#double_rm_quantity rsy) - (length (filter pred_rmcf DOUBLE_RM (filter pred_date date (#resv_record rsy)))) | |
else if rmcf = QUEEN_RM | |
then (#queen_rm_quantity rsy) - (length (filter pred_rmcf QUEEN_RM (filter pred_date date (#resv_record rsy)))) | |
else (#king_rm_quantity rsy) - (length (filter pred_rmcf KING_RM (filter pred_date date (#resv_record rsy)))) | |
end | |
(*Function getInventorySpan checks whether the room inventory is available for all nights, given a reservation system, | |
roomconfiguration, a date and a number of nights | |
Input:a reservation system, a room configuration, a date and a number of nights. Output:boolean*) | |
fun getInventorySpan (a_rsy:ressys) rmcf date 0 = true | |
| getInventorySpan (a_rsy:ressys) rmcf date nights = if getInventory a_rsy rmcf date > 0 | |
then getInventorySpan a_rsy rmcf (date + 1) (nights - 1) | |
else false; | |
(*Function restrictions checks whether a reserve record met the requirements of minimum number of nights and the number limit of | |
occupancy.Input: a reservation record. Output:boolean*) | |
fun restrictions (rrc:resrecord) = | |
let | |
fun getInt NONE = true | |
| getInt (SOME x) = if (#num_of_nights rrc) < x | |
then false | |
else true | |
in | |
if (getInt Q.minnights) andalso (Q.occupancyLimit > (#num_of_occu rrc)) | |
then true | |
else false | |
end; | |
(*Function reserve adds a reserve record to a reserve system. Input:a reservation system and a reservation record. | |
Output: a new reservation system with this record*) | |
fun reserve (rsy:ressys) (rrc:resrecord) = if (restrictions rrc) | |
then (if (exist (#id rrc) (#resv_record rsy)) = false andalso (getInventorySpan rsy | |
(#rm_config rrc) (#check_in_date rrc) (#num_of_nights rrc)) | |
then{ double_rm_quantity = (#double_rm_quantity rsy), queen_rm_quantity = | |
(#queen_rm_quantity rsy), king_rm_quantity = (#king_rm_quantity rsy),resv_record = | |
rrc::(#resv_record rsy)} | |
else raise Deny) | |
else raise Deny; | |
(*Function cancel cancels a reserve record from a reserve system. Input:a reservation system and a reservation record. | |
Output: a new reservation system without this record*) | |
fun cancel (rsy:ressys) id = | |
let | |
fun pred1 id (rrc:resrecord) = id <> (#id rrc); | |
in | |
if exist id (#resv_record rsy) | |
then { double_rm_quantity = (#double_rm_quantity rsy),queen_rm_quantity = (#queen_rm_quantity rsy), | |
king_rm_quantity = (#king_rm_quantity rsy),resv_record = (filter pred1 id (#resv_record rsy)) } | |
else raise Deny | |
end; | |
(*Function completedStays calculates the number of completed stays due to a given date. Input: a reservation system and a date. | |
Output: the number of completed stays due to a given date*) | |
fun completedStays (rsy:ressys) date = | |
let | |
fun pred_date2 a (rrc:resrecord) = a > ((#check_in_date rrc) + (#num_of_nights rrc)); | |
in | |
length (filter pred_date2 date (#resv_record rsy)) | |
end; | |
(*Function removeCompletedStays removes all completed stays due to a given date.Input: a reservation system and a date. | |
Output:a new reservation system without all completed stays due to the given date*) | |
fun removeCompletedStays (rsy:ressys) date = | |
let | |
fun pred_date3 a (rrc:resrecord) = a <= ((#check_in_date rrc) + (#num_of_nights rrc)); | |
in | |
{ double_rm_quantity = (#double_rm_quantity rsy),queen_rm_quantity = (#queen_rm_quantity rsy),king_rm_quantity = | |
(#king_rm_quantity rsy),resv_record = (filter pred_date3 date (#resv_record rsy)) } | |
end; | |
(*Function guestQuantity calculates the total number of guests in the hotel on a given date. Input: a reservation system and a date | |
Output: total number of guests in the hotel on the given date*) | |
fun guestQuantity (rsy:ressys) date = | |
let | |
val rrc_list = filter pred_date date (#resv_record rsy) | |
fun getQ [] = 0 | |
| getQ ((y::ys):resrecord list) = (#num_of_gst y) + getQ ys | |
in | |
getQ rrc_list | |
end; | |
(*This is the improvement I have made to the reserve system. If a custtomer wants to update his reservation and change some | |
information like number of nights he will stay, room configuration or number of occupants. This function called | |
update_reserve can easily take care of this. It will first check whether the ID exists on the reserve system and whether | |
there are enough available rooms for the updating. If so, it will replace the old reserve record with the new one | |
Input: a reserve system and the new record. | |
Output: a new reserve system with the new record instead of the old one*) | |
fun update_reserve (rsy:ressys) (rrc:resrecord) = if (restrictions rrc) | |
then (if (exist (#id rrc) (#resv_record rsy)) andalso (getInventorySpan (cancel rsy (#id rrc)) | |
(#rm_config rrc) (#check_in_date rrc) (#num_of_nights rrc)) | |
then reserve (cancel rsy (#id rrc)) rrc | |
else raise Deny) | |
else raise Deny; | |
end; | |
(*Below is the test bed with some comment*) | |
structure TestHotelRoomDetail : ROOMDETAIL = | |
struct | |
val doubleAvailable = 20 | |
val queenAvailable = 1 | |
val kingAvailable = 3 | |
val minnights = SOME 2 | |
val occupancyLimit = 4 | |
end | |
structure TestHotel = MakeHotel(TestHotelRoomDetail); | |
exception TestFailed | |
open TestHotel; | |
(*Test of creating a reserve system*) | |
fun testCreateSys () = | |
let | |
val hotel = empty; | |
val double = getInventory hotel DOUBLE_RM 0; | |
val queen = getInventory hotel QUEEN_RM 0; | |
val king = getInventory hotel KING_RM 0; | |
in | |
if double = 20 andalso queen = 1 andalso king = 3 | |
then print "\nThe hotel reservation system has been created successfully!\n" | |
else raise TestFailed | |
end | |
(*Test of adding reservation records to reserve system*) | |
fun testReserve () = | |
let | |
val emptyHotel = empty; | |
val record1 = {id = 100, first_name = "A", last_name = "F", check_in_date= 0, num_of_nights = 2, num_of_occu = 2, rm_config = DOUBLE_RM, num_of_gst = 1}; | |
val record2 = {id = 101, first_name = "B", last_name = "G", check_in_date= 0, num_of_nights = 3, num_of_occu = 3, rm_config = KING_RM, num_of_gst = 3}; | |
val record3 = {id = 102, first_name = "C", last_name = "H", check_in_date= 0, num_of_nights = 4, num_of_occu = 3, rm_config = QUEEN_RM, num_of_gst = 1}; | |
val record4 = {id = 103, first_name = "D", last_name = "I", check_in_date= 0, num_of_nights = 6, num_of_occu = 2, rm_config = KING_RM, num_of_gst = 0}; | |
val record5 = {id = 104, first_name = "E", last_name = "J", check_in_date= 0, num_of_nights = 7, num_of_occu = 3, rm_config = KING_RM, num_of_gst = 2}; | |
val hotelwrc1 = reserve emptyHotel record1; | |
val hotelwrc2 = reserve hotelwrc1 record2; | |
val hotelwrc3 = reserve hotelwrc2 record3; | |
val hotelwrc4 = reserve hotelwrc3 record4; | |
val hotelwrc5 = reserve hotelwrc4 record5; | |
val double = getInventory hotelwrc5 DOUBLE_RM 0; | |
val queen = getInventory hotelwrc5 QUEEN_RM 0; | |
val king = getInventory hotelwrc5 KING_RM 0; | |
fun test x = true; | |
in | |
if ((test (reserve emptyHotel record1)) handle Deny => false) andalso ((test (reserve hotelwrc1 record2)) handle Deny => false) andalso | |
((test (reserve hotelwrc2 record3)) handle Deny => false) andalso ((test (reserve hotelwrc3 record4)) handle Deny => false) andalso | |
((test (reserve hotelwrc4 record5)) handle Deny => false) | |
then print "\n5 reservations have been added to the hotel reservation system!\n" | |
else raise TestFailed | |
end | |
(*Demonstration of unsuccessful reservation due to lack of inventory*) | |
fun Demo_lack_inventory () = | |
let | |
val emptyHotel = empty; | |
val record6 = {id = 100, first_name = "A", last_name = "F", check_in_date= 0, num_of_nights = 2, num_of_occu = 2, rm_config = QUEEN_RM, | |
num_of_gst = 1}; | |
val record7 = {id = 106, first_name = "K", last_name = "L", check_in_date= 0, num_of_nights = 2, num_of_occu = 2, rm_config = QUEEN_RM, | |
num_of_gst = 1}; | |
val hotelwrc6 = reserve emptyHotel record6; | |
fun test x = false; | |
in | |
if ((test (reserve hotelwrc6 record7)) handle Deny => true) | |
then print "\nDemonstration of unsuccessful reservation due to lack of inventory completed!\n" | |
else raise TestFailed | |
end | |
(*Demonstration of unsuccessful reservation due to room occupancy being exceeding or minimum night requirement not being met*) | |
fun Demo_restriction () = | |
let | |
val emptyHotel = empty; | |
val record8 = {id = 100, first_name = "A", last_name = "F", check_in_date= 0, num_of_nights = 1, num_of_occu = 5, rm_config = QUEEN_RM, num_of_gst = 1}; | |
fun test x = false; | |
in | |
if ((test (reserve emptyHotel record8)) handle Deny => true) | |
then print "\nDemonstration of unsuccessful reservation due to room occupancy being exceeding or minimum night requirement not being met completed!\n" | |
else raise TestFailed | |
end | |
(*Test of canceling reservation records from the reservation system*) | |
fun testCancel () = | |
let | |
val emptyHotel = empty; | |
val record1 = {id = 100, first_name = "A", last_name = "F", check_in_date= 0, num_of_nights = 2, num_of_occu = 2, rm_config = DOUBLE_RM, num_of_gst = 1}; | |
val record2 = {id = 101, first_name = "B", last_name = "G", check_in_date= 0, num_of_nights = 3, num_of_occu = 3, rm_config = QUEEN_RM, num_of_gst = 3}; | |
val record3 = {id = 102, first_name = "C", last_name = "H", check_in_date= 0, num_of_nights = 4, num_of_occu = 3, rm_config = KING_RM, num_of_gst = 1}; | |
val record4 = {id = 103, first_name = "D", last_name = "I", check_in_date= 0, num_of_nights = 6, num_of_occu = 2, rm_config = KING_RM, num_of_gst = 0}; | |
val record5 = {id = 104, first_name = "E", last_name = "J", check_in_date= 0, num_of_nights = 7, num_of_occu = 3, rm_config = KING_RM, num_of_gst = 2}; | |
val hotelwrc1 = reserve emptyHotel record1; | |
val hotelwrc2 = reserve hotelwrc1 record2; | |
val hotelwrc3 = reserve hotelwrc2 record3; | |
val hotelwrc4 = reserve hotelwrc3 record4; | |
val hotelwrc5 = reserve hotelwrc4 record5; | |
val hotelwrc6 = cancel hotelwrc5 104; | |
fun test x = true; | |
in | |
if ((test (reserve emptyHotel record1)) handle Deny => false) andalso ((test (reserve hotelwrc1 record2)) handle Deny => false) andalso | |
((test (reserve hotelwrc2 record3)) handle Deny => false) andalso ((test (reserve hotelwrc3 record4)) handle Deny => false) andalso | |
((test (reserve hotelwrc4 record5)) handle Deny => false) andalso ((test (cancel hotelwrc5 104)) handle Deny => false) andalso | |
((test (cancel hotelwrc6 103)) handle Deny => false) | |
then print "\n2 reservations have been canceled from the hotel reservation system!\n" | |
else raise TestFailed | |
end | |
(*Test of querying room inventory*) | |
fun queryInventory () = | |
let | |
val emptyHotel = empty; | |
val record1 = {id = 100, first_name = "A", last_name = "F", check_in_date= 0, num_of_nights = 2, num_of_occu = 2, rm_config = DOUBLE_RM, num_of_gst = 1}; | |
val record2 = {id = 101, first_name = "B", last_name = "G", check_in_date= 0, num_of_nights = 3, num_of_occu = 3, rm_config = DOUBLE_RM, num_of_gst = 3}; | |
val record3 = {id = 102, first_name = "C", last_name = "H", check_in_date= 0, num_of_nights = 4, num_of_occu = 3, rm_config = DOUBLE_RM, num_of_gst = 1}; | |
val record4 = {id = 103, first_name = "D", last_name = "I", check_in_date= 0, num_of_nights = 6, num_of_occu = 2, rm_config = DOUBLE_RM, num_of_gst = 0}; | |
val record5 = {id = 104, first_name = "E", last_name = "J", check_in_date= 0, num_of_nights = 7, num_of_occu = 3, rm_config = DOUBLE_RM, num_of_gst = 2}; | |
val hotelwrc1 = reserve emptyHotel record1; | |
val hotelwrc2 = reserve hotelwrc1 record2; | |
val hotelwrc3 = reserve hotelwrc2 record3; | |
val hotelwrc4 = reserve hotelwrc3 record4; | |
val hotelwrc5 = reserve hotelwrc4 record5; | |
val double = getInventory hotelwrc5 DOUBLE_RM 0; | |
in | |
if double = 15 | |
then print "\nRoom inventory for 2-double-bed room on date 0 has been queried successfully!\n" | |
else raise TestFailed | |
end | |
(*Demostration of removeCompletedStays and completedStays*) | |
fun Demo_cmplt () = | |
let | |
val emptyHotel = empty; | |
val record1 = {id = 100, first_name = "A", last_name = "F", check_in_date= 0, num_of_nights = 2, num_of_occu = 2, rm_config = DOUBLE_RM, num_of_gst = 1}; | |
val record2 = {id = 101, first_name = "B", last_name = "G", check_in_date= 0, num_of_nights = 3, num_of_occu = 3, rm_config = KING_RM, num_of_gst = 3}; | |
val record3 = {id = 102, first_name = "C", last_name = "H", check_in_date= 0, num_of_nights = 4, num_of_occu = 3, rm_config = QUEEN_RM, num_of_gst = 1}; | |
val record4 = {id = 103, first_name = "D", last_name = "I", check_in_date= 0, num_of_nights = 6, num_of_occu = 2, rm_config = KING_RM, num_of_gst = 0}; | |
val record5 = {id = 104, first_name = "E", last_name = "J", check_in_date= 0, num_of_nights = 7, num_of_occu = 3, rm_config = KING_RM, num_of_gst = 2}; | |
val hotelwrc1 = reserve emptyHotel record1; | |
val hotelwrc2 = reserve hotelwrc1 record2; | |
val hotelwrc3 = reserve hotelwrc2 record3; | |
val hotelwrc4 = reserve hotelwrc3 record4; | |
val hotelwrc5 = reserve hotelwrc4 record5 | |
val int1 = completedStays hotelwrc5 5; | |
val hotelwrc7 = removeCompletedStays hotelwrc5 5; | |
val int2 = completedStays hotelwrc7 5; | |
in | |
if (int1 = 3) andalso (int2 = 0) | |
then print "\nDemostration of removeCompletedStays and completedStays succeed!\n" | |
else raise TestFailed | |
end | |
(*Demostration of the getting the guest quantity*) | |
fun Demo_gstQtt () = | |
let | |
val emptyHotel = empty; | |
val record1 = {id = 100, first_name = "A", last_name = "F", check_in_date= 0, num_of_nights = 2, num_of_occu = 2, rm_config = DOUBLE_RM, num_of_gst = 1}; | |
val record2 = {id = 101, first_name = "B", last_name = "G", check_in_date= 0, num_of_nights = 3, num_of_occu = 3, rm_config = KING_RM, num_of_gst = 3}; | |
val record3 = {id = 102, first_name = "C", last_name = "H", check_in_date= 0, num_of_nights = 4, num_of_occu = 3, rm_config = QUEEN_RM, num_of_gst = 1}; | |
val record4 = {id = 103, first_name = "D", last_name = "I", check_in_date= 0, num_of_nights = 6, num_of_occu = 2, rm_config = KING_RM, num_of_gst = 2}; | |
val record5 = {id = 104, first_name = "E", last_name = "J", check_in_date= 0, num_of_nights = 7, num_of_occu = 3, rm_config = KING_RM, num_of_gst = 3}; | |
val hotelwrc1 = reserve emptyHotel record1; | |
val hotelwrc2 = reserve hotelwrc1 record2; | |
val hotelwrc3 = reserve hotelwrc2 record3; | |
val hotelwrc4 = reserve hotelwrc3 record4; | |
val hotelwrc5 = reserve hotelwrc4 record5 | |
val int1 = guestQuantity hotelwrc5 1; | |
in | |
if (int1 = 10) | |
then print "\nDemostration of the guestQuantity succeed!\n" | |
else raise TestFailed | |
end | |
(*Test of my own improvement: Updating a reservation record*) | |
fun testUpdateReserve () = | |
let | |
val emptyHotel = empty; | |
val record1 = {id = 100, first_name = "A", last_name = "F", check_in_date= 0, num_of_nights = 2, num_of_occu = 2, rm_config = DOUBLE_RM, num_of_gst = 1}; | |
val record2 = {id = 101, first_name = "B", last_name = "G", check_in_date= 0, num_of_nights = 3, num_of_occu = 3, rm_config = KING_RM, num_of_gst = 3}; | |
val record3 = {id = 102, first_name = "C", last_name = "H", check_in_date= 0, num_of_nights = 4, num_of_occu = 3, rm_config = QUEEN_RM, num_of_gst = 1}; | |
val record4 = {id = 103, first_name = "D", last_name = "I", check_in_date= 0, num_of_nights = 6, num_of_occu = 2, rm_config = KING_RM, num_of_gst = 2}; | |
val record5 = {id = 104, first_name = "E", last_name = "J", check_in_date= 0, num_of_nights = 7, num_of_occu = 3, rm_config = KING_RM, num_of_gst = 3}; | |
val hotelwrc1 = reserve emptyHotel record1; | |
val hotelwrc2 = reserve hotelwrc1 record2; | |
val hotelwrc3 = reserve hotelwrc2 record3; | |
val hotelwrc4 = reserve hotelwrc3 record4; | |
val hotelwrc5 = reserve hotelwrc4 record5; | |
val updatewrc5 = update_reserve hotelwrc5 {id = 101, first_name = "B", last_name = "G", check_in_date= 1, num_of_nights = 5, num_of_occu = 2, | |
rm_config = DOUBLE_RM, num_of_gst = 1}; | |
fun pred2 id (rrc:resrecord) = (id = (#id rrc)); | |
in | |
if (filter pred2 101 (#resv_record updatewrc5)) = [{id = 101, first_name = "B", last_name = "G", check_in_date= 1, num_of_nights = 5, num_of_occu = 2, | |
rm_config = DOUBLE_RM, num_of_gst = 1}] | |
then print"\nTest of New Feature UpdateReserve succeed!\n\n" | |
else raise TestFailed | |
end | |
val tests = [testCreateSys, testReserve, Demo_lack_inventory, Demo_restriction, testCancel, queryInventory, Demo_cmplt, Demo_gstQtt,testUpdateReserve]; | |
fun unitTest() = map (fn x => x()) tests |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment