; Modules to be included (require (lib "xml.ss" "xml")) (require (lib "13.ss" "srfi")) ; ---- CONSTANTS ---- (define eps 2) (define inPort (open-input-file "E:\\Course Document\\Program Language\\Project2\\test0203.xml")) (define docElem (document-element(read-xml inPort))) ; ---- MAIN FUNCTION ---- ; Check if the forecast is good or bad ; string number -> string (define (test-forecast datestr n) (let ((rst (check-forecast docElem datestr n))) (cond [(> rst 10000) "Sorry, we could not make a decision about the forecast."] [(<= rst eps) "Good forecast!"] [else "Bad forecast!"]))) ; Get the difference between the forecasted and the actural weather ; Element string number -> number (define (check-forecast e datestr n) (let ((el (get-sub-elements e '|forecast|))) (make-decision (filter (lambda (x) (dif x datestr n)) el) datestr (get-real-temp el datestr) n))) ; Get the sum of number list ; list-of-number -> number (define (addall nl) (cond [(empty? nl) 0] [else (+ (car nl) (addall (cdr nl)))])) ; Get the real temperature according to the date string ; list-of-element string -> number (define (get-real-temp el datestr) (cond [(eq? '() el) 1001] [else (let ((fstEle (element-attributes (car el)))) (cond [(isPartOf datestr (get-attr fstEle 'date)) (string->number (string-trim (get-attr fstEle 'temperature)))] [else (get-real-temp (cdr el) datestr)]))])) ; Check if the first string is part of the second one ; string string -> bool (define (isPartOf fst snd) (let* ((daynum (string->number (car (regexp-match "[0-9]+" fst)))) (daystr (number->string daynum)) (monthstr (car (regexp-match "[a-zA-Z]+" fst))) (newFst (string-append monthstr (cond [(< daynum 10) (string-append " " daystr)] [else (string-append " " daystr)]))) (childStr (regexp-match newFst snd))) (cond [(not childStr) #f] [else #t]))) ; Get the difference between the forecasted and the actural weather ; list-of-element string number number -> bool (define (make-decision el datestr temp n) (let ((listL (listLen el))) (cond [(> temp 1000) 10001] [(= listL 0) 0] [(/ (addall (map (lambda (e) (cal-diff e datestr temp n)) el)) listL)]))) ; Get the length of the list ; list -> number (define (listLen l) (cond [(empty? l) 0] [else (+ 1 (listLen (cdr l)))])) ; Get the specified attribute value from an element and compare with a reference ; element string number number -> number (define (cal-diff e datestr temp n) (get-diff-value (filter element? (element-content e)) datestr temp n)) (define (get-diff-value el datestr temp n) (cond [(empty? el) 0] [(eq? '() el) 0] [(equal? (get-attr (element-attributes (car el)) 'date) (trim-datestr1 datestr)) (get-value (car el) temp)] [else (get-diff-value (cdr el) datestr temp n)])) ; Check if one of the list memeber is in the element attribute value ; element list-of-string -> bool (define (isValid e strl) (isIn (get-attr (element-attributes e) 'date) strl)) (define (isIn str strlist) (cond [(empty? strlist) #f] [(equal? (trim-datestr1 str) (car strlist)) #t] [else (isIn str (cdr strlist))])) ; Change the format of the date string ; For example, "Feb 2" -> "Feb 02", "Feb02" -> "Feb 02" ; string -> string (define (trim-datestr1 str) (let* ((daynum (string->number (car (regexp-match "[0-9]+" str)))) (daystr (number->string daynum)) (monthstr (car (regexp-match "[a-zA-Z]+" str)))) (string-append monthstr (cond [(< daynum 10) (string-append " 0" daystr)] [else (string-append " " daystr)])))) ; Get the 'temperature attribute value from an element, and compare with a reference ; element number -> nubmer (define (get-value e temp) (let ((diftemp (- temp (string->number (get-attr (element-attributes e) 'temperature))))) (abs diftemp))) ; Get the specified attribute value from a list of attribute ; list-of-attribute string -> string (define (get-attr attrs s) (cond [(equal? attrs '()) ""] [(equal? (attribute-name (car attrs)) s) (attribute-value (car attrs))] [else (get-attr (cdr attrs) s)])) ; Check if the date string is a part of the element 'date attribute ; element string number -> bool (define (dif e datestr n) (let ((daylist (preDays datestr n))) (checkIn daylist (get-attr (element-attributes e) 'date)))) ; list-of-string string -> bool (define (checkIn daylist datestr) (cond [(empty? daylist) #f] [(eq? '() daylist) #f] [(eq? datestr "") #f] [(isPartOf (car daylist) datestr) #t] [else (checkIn (cdr daylist) datestr)])) ; Get the previous n days date strings ; string number -> list-of-string ; For example, (preDays '|Feb 02| 3) "should be" list ('|Feb 01| '|Jan 31| '|Jan 30|) (define (preDays datestr n) (let ((daynum (string->number (car (regexp-match "[0-9]+" datestr)))) (monthstr (car (regexp-match "[a-zA-Z]+" datestr)))) (cond [(> daynum n) (get-direct-list monthstr daynum n)] [else (get-undirect-list monthstr daynum n)]))) ; Get the previous day string in the same month ; string number number -> strin (define (get-direct-list str m n) (map (lambda (no) (string-append (string-append (string-trim str) " ") (cond [(> no 9) (number->string no)] [else (string-append "0" (number->string no))]))) (minusN m n))) ; Get a list of number ; number number -> list-of-number ; For exmaple, (minusN 5 2) "should be" (3 4) (define minusN (lambda (m n) (let iter ((counter 1) (outList empty)) (if (< n counter) outList (iter (+ counter 1) (cons (- m counter) outList) ))))) ; Get a list of date string in different months ; string number number -> list-of-string (define (get-undirect-list str m n) (append (get-direct-list str m (- m 1)) (get-direct-list "Jan" 32 (- n (- m 1))))) ; Get all the sub elements with specified name from the specified element e ; Element string -> List-of-Element (define (get-sub-elements e name) (cond [(empty? e) empty] [(filter not-empty (map (lambda (ele) (select-element ele name)) (filter element? (element-content e))))])) ; Check if the argument is empty ; X -> bool (define (not-empty x) (cond [(empty? x) #f] [else #t])) ; Get the element if its name is the same with the argument ; Element string -> Element (define (select-element e name) (cond [(empty? e) empty] [(eq? (element-name e) name) e] [else empty])) ; ---- TEST CASES ---- (display (test-forecast "Feb 1" 1))