Instead of taking a lucrative job at the company across the lake, you decide to pursue a career as the wine steward at the Internet Cafe. In keeping with the ambiance of the job, you decide to write an internet shopping agent to track down the best buys for wines from on-line merchants. Needless to say, after your experience in CSE341, you decide to do the project in ML.
A major portion of the program is a collection of routines which parse web pages and identify the wines being sold. A difficulty is that each web site has its own format for information, so it is necessary to write some code specific to each web site being searched. Of course, the hope is to write as little page specific code as possible, and to use general search routines for most of the work.
For the assignment, you are to write two functions: findWine, which, given a query string and a list of files, returns a list of wines satisfying the query, and printWineList, which displays a list of wines.
findWine : ( string * ( string * fileCode ) list ) -> wine list
printWineList : wine list -> bool
datatype fileCode = VirtualVineyard | WineConnection | RedCarpet; val myIOList = [ ("vv1.html", VirtualVineyard), ("wc1.html", WineConnection), ("wc2.html", WineConnection), ("rc1.html", RedCarpet), ("rc2.html", RedCarpet), ("rc3.html", RedCarpet)];
The keywords may appear in any order. An example query string is "Vintage 1995 Variety Merlot MinPrice 8 MaxPrice 11"
An example that uses the {} delimiters is "Vintage 1995 Variety {Pinot Gris} Minprice 8 MaxPrice 11"
The result of the query would be a list of all 1995 Merlot's priced between $8 and $11.
The keywords are Vintage, Winery, Variety, Location, MinPrice, MaxPrice, Merchant.
If a keyword is not present, the corresponding attribute is unconstrained, so if Vintage is not present, then any year is okay.
You may assume that each keyword appears at most once in a query string. (Although it would be really cool to allow and/or/not and other higher-order queries. You are allowed to do more than the assignment asks - although it is a good strategy to get the basic stuff working before additional features).
Keywords:
One way to deal with multiword names is to use just a single word for input and searching, but the multiword name for output. For example, the user would enter Variety Noir instead of Variety {Pinot Noir} to get a listing of the Pinot Noirs, although the output routine could use the name Pinot Noir. Note that some web pages use abbreviations.
Here is a sample interaction with the system:
- val myIOlist = [ ("vv1.html", VintageVineyards) ]; val myIOlist = [("vv1.html",VintageVineyards)] : (string * fileCode) list - val myQuery = "Variety Soave Vintage 1673 MinPrice 800"; val myQuery = "Variety Soave Vintage 1673 MinPrice 800" : string - val wines = findWine (myQuery, myIOlist); val wines =: wine list - printWineList wines; ... ... val it = () : unit
You should not turn in web pages - only your code, a description of what your code does, and output from sample runs. Also, make sure that your code doesn't produce reams of debugging output.
IO is a bit of a problem because documentation is hard to find. Online sources include http://cm.bell-labs.com/cm/cs/what/smlnj/top-level-comparison.html and http://www.dina.kvl.dk/%7Esestoft/sml/sml-std-basis.html. (These take effort to decipher).
I hope the following is all that is necessary: for output, use
print
which takes a string as an argument. Here is a
tiny example:
fun printPair(str, x) = (print str; print " "; print (Int.toString x); print "\n"); printPair ("Red", 1991); Red 1991
By far the easiest way to handle input is to read the files and store them as lists of characters. If you want, use the following code:
fun charList L = if null(L) then nil else explode(hd L) @ charList (tl L); fun buildStringList(file) = if TextIO.endOfStream(file) then nil else TextIO.inputLine(file) :: buildStringList(file); fun buildCharList(str) = charList (buildStringList (TextIO.openIn str));The function
buildCharList
returns a character list when given
a file name.
You _are_ expected to write clean ML code in a functional style. You are expected to make appropriate use of pattern matching, datatypes and higher-order functions.
fullWineList : (string * fileCode) list -> wine list findWine : ( string * wine list ) -> wine list printWineList : wine list -> bool
When I wrote my solution, I broke the task into the following components:
Script started on Tue Oct 07 09:50:36 1997 orcas% sml Standard ML of New Jersey, Version 109.30, July 17, 1997 [CM; autoload enabled] - use "wino.ml"; [opening wino.ml] [opening utility.ml] [opening query.ml] [opening parse.ml] val iList = [(VirtualVineyard,"vv1.html"),(WineConnection,"wc1.html"), (WineConnection,"wc2.html"),(WineConnection,"wc3.html"), (WineConnection,"wc4.html"),(RedCarpet,"rc1.html"),(RedCarpet,"rc2.html"), (RedCarpet,"rc3.html"),(RedCarpet,"rc4.html"),(RedCarpet,"rc5.html")] : (merchant * string) list val findWine = fn : string -> wineType list -> wineType list val fl = [(VirtualVineyard,CabernetSauvignon,1991,18), (VirtualVineyard,Chardonnay,1995,20),(VirtualVineyard,PinotNoir,1995,20), (VirtualVineyard,CabernetSauvignon,1993,15), (VirtualVineyard,Chardonnay,1994,22), (VirtualVineyard,CabernetSauvignon,1994,17), (VirtualVineyard,Chardonnay,1993,19),(VirtualVineyard,Chardonnay,1995,15), (VirtualVineyard,Chardonnay,1994,35), (VirtualVineyard,CabernetSauvignon,1993,22), (VirtualVineyard,Chardonnay,1994,23),(VirtualVineyard,Chardonnay,1995,12), ...] : wineType list - val w1 = findWine "variety pinot vintage 1995" fl; val w1 = [(VirtualVineyard,PinotNoir,1995,20),(VirtualVineyard,PinotNoir,1995,16), (VirtualVineyard,PinotNoir,1995,14),(VirtualVineyard,PinotNoir,1995,14), (VirtualVineyard,PinotNoir,1995,18),(VirtualVineyard,PinotNoir,1995,14), (VirtualVineyard,PinotNoir,1995,17),(VirtualVineyard,PinotNoir,1995,11), (VirtualVineyard,PinotNoir,1995,12),(VirtualVineyard,PinotNoir,1995,12)] : wineType list - printWineList w1; Virtual Vinyard has a 1995 Pinot Noir for $20 Virtual Vinyard has a 1995 Pinot Noir for $16 Virtual Vinyard has a 1995 Pinot Noir for $14 Virtual Vinyard has a 1995 Pinot Noir for $14 Virtual Vinyard has a 1995 Pinot Noir for $18 Virtual Vinyard has a 1995 Pinot Noir for $14 Virtual Vinyard has a 1995 Pinot Noir for $17 Virtual Vinyard has a 1995 Pinot Noir for $11 Virtual Vinyard has a 1995 Pinot Noir for $12 Virtual Vinyard has a 1995 Pinot Noir for $12 val it = 0 : int - fun wineBot str = printWineList (findWine str fl); val wineBot = fn : string -> int - wineBot "variety Pinot maxPrice 25 minPrice 20"; Virtual Vinyard has a 1995 Pinot Noir for $20 Virtual Vinyard has a 1994 Pinot Noir for $24 Virtual Vinyard has a 1994 Pinot Noir for $25 Virtual Vinyard has a 1993 Pinot Noir for $21 Virtual Vinyard has a 1992 Pinot Noir for $23 Virtual Vinyard has a 1994 Pinot Noir for $25 Virtual Vinyard has a 1994 Pinot Noir for $24 Virtual Vinyard has a 1994 Pinot Noir for $20 Wine Connection has a 1994 Pinot Noir for $25 Wine Connection has a 1994 Pinot Noir for $24 Wine Connection has a 1993 Pinot Noir for $25 Wine Connection has a 1992 Pinot Noir for $25 Wine Connection has a 1992 Pinot Noir for $22 val it = 0 : int - wineBot "variety pino vint 1994 minprice 17 maxprice 21"; Virtual Vinyard has a 1994 Pinot Noir for $20 val it = 0 : int - wineBot "variety chardonnay vintage 1993"; Virtual Vinyard has a 1993 Chardonnay for $19 Virtual Vinyard has a 1993 Chardonnay for $15 Virtual Vinyard has a 1993 Chardonnay for $17 Virtual Vinyard has a 1993 Chardonnay for $24 Wine Connection has a 1993 Chardonnay for $21 Wine Connection has a 1993 Chardonnay for $18 Wine Connection has a 1993 Chardonnay for $13 Wine Connection has a 1993 Chardonnay for $12 Wine Connection has a 1993 Chardonnay for $30 val it = 0 : int - wineBot "minPrice 300"; Virtual Vinyard has a 1991 Chardonnay for $469 Virtual Vinyard has a 1989 Cabernet Sauvignon for $519 Virtual Vinyard has a 1984 Cabernet Sauvignon for $629 Wine Connection has a 1991 Cabernet Sauvignon for $350 Wine Connection has a 1988 Bordeaux for $950 val it = 0 : int - wineBot "maxPrice 17 vintage 1993 variety chard"; Virtual Vinyard has a 1993 Chardonnay for $15 Virtual Vinyard has a 1993 Chardonnay for $17 Wine Connection has a 1993 Chardonnay for $13 Wine Connection has a 1993 Chardonnay for $12 val it = 0 : int script done on Tue Oct 07 10:06:37 1997