It's best if I use pasteracket ("Hank's medication thingy") http://pasterack.org/pastes/14535
-- Henry Lenzi On Sun, Aug 3, 2014 at 4:30 PM, Henry Lenzi <henry.le...@gmail.com> wrote: > Uh oh, the GOOG botched formatting. Sorry about that! > > On Sun, Aug 3, 2014 at 4:29 PM, Henry Lenzi <henry.le...@gmail.com> wrote: >> ; Hello all -- >> ; So here's how I solve all those little problems regarding symbols >> and evaluation of medication definitions. >> ; Would you please bear with me? I apologize for the length. >> ; This is the approach I've taken. I've chosen no to use any macrology >> or parser/lexer technique because I don't grok them and they >> ; don't really seem necessary, for reasons explained in the code comments. >> ; I have not decided to hash tables, for the following reason: there's >> a part of the code (the drug definitions, the instructions), that >> ; should be easy enough for non-programmers to edit. If they are kept >> very simple, it's possible, because the users have to edit those >> ; files. So, even though it is source code, it's not as intimidating >> as editing source code if hash tables. >> ; Another aspect is that I hope modules provided some sort of safety >> in terms of syntax checking. That is to say, if you used make a >> ; typo in the medication part of the DSL, the system will (hopefully) >> bork because no such module exists. I believe this also creates >> ; an opportunity for "syntax validation" if a proper input phase is >> designed. But Lisp/Scheme being a dynamic language, the run-time >> ; will bork immediately once it sees funny things. This is a way to >> guarantee the DSL is correct, which we get for free by using Racket. >> ; A fourth aspect is that, if each drug is kept a different module >> (which I haven't done here, BTW), then we can make for easier >> ; internationalization, by keeping modules by languages, e.g., >> hctz25-en, hctz25-pt_br. I believe Dan has an interest in this project >> too, >> ; so it's best to design with that in mind. >> ; Final comment regards "database". We get "database" for free, by >> registering prescriptions with patient register numbers. The OS >> ; takes care of pretty musch anything else. And there's no need for >> atomicity and concurrency. Like I said, this is stupid code. >> ; >> ; >> #lang racket >> >> ; code-review-for-racketeers-2014-08-03-a.rkt >> ; >> ; For this exercise, suppose a Recipe.txt file. Let´s suppose the idea >> is that the physician >> ; has two options: 1) he or she opens Notepad and writes the >> prescription file (Recipe.text); >> ; 2) or, the software asks for inputs and writes the file (this will >> not be covered in this >> ; exercise). The written prescription in the shorthand DSL would look >> like below, with the >> ; exception of a first field with patient ID data not included (to be >> done later). >> ; The prescription has a rigid syntax would look like this (line >> breaks included): >> ; 1- >> ; hctz25 30 pl 1xd >> ; >> ; 2- >> ; simva20 30 pl 1xn >> >> >> ; Needed for EVAL, used later on >> (define-namespace-anchor a) >> >> ; These definitions should be in a different module. >> ; This way we get syntax checking for free. >> ; MED - medication. Includes dosage. >> (define hctz25 "Hydrochlorothiazide 25mg") >> (define simva20 "Simvastatin 20mg") >> ; FORM - whether the patient will take home pills, a tube, a flask, capsules >> (define pl "pills") >> ; POS - posology, whether the patient will take 1 pill 3x a day, or 2 >> pills 2x a day, etc. >> (define 1xd "Take 1 pill P.O. 1x/day") >> (define 1xn "Take 1 pill P.O. 1x at night") >> ; INSTs - special instructions. INST is just a prefix INST+MED without >> the dosage. >> (define INSTOMZ "half an hour before breakfast, with a glass of water") >> ; Formatters - simple for now, but should be a function of the space >> available. >> (define line "-----------") >> >> >> >> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; >> ; The main part of a prescription DSL is pretty rigid in syntax, being >> composed of blocks of theses parts: >> ; MEDICATION QUANTITY FORM POSOLOGY INSTRUCTION, or MED QUANT FORM POS INST. >> ; Please note that, in this DSL, the MED part includes the drug dosage >> (e.g., HCTZ25, where >> ; the HCTZ designates the drug, and the 25 the dosage). >> ; An example would be: >> ; HCTZ25 30 PL 1XD >> ; meaning: Hydrochlorothiazide 25mg -------------- 30 pills >> ; Take 1 pill P.O. 1X day >> ; INST are special instructions. They basically are more detailed >> explanation to the patient about >> ; how to use the medication properly. Not always there's a INST in the >> prescription DSL. >> ; INSTs are, in fact, a PREFIX for the MED without the dose. For >> example, OMZ20 is Omeprazol 20mg. >> ; The instruction for OMZ would be INSTOMZ ("half an hour before >> breakfast, with a glass of water"). >> ; In this case, the DSL line would be: >> ; OMZ20 30 PL 1XD INSTOMZ >> ; meaning: Omeprazol 20mg ------------------- 30 pills >> ; Take 1 pill P.O. 1X day >> ; half an hour before breakfast, with >> ; a glass of water >> ; Questions regarding proper formatting of INST are not addressed at >> this moment. >> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; >> ; Now follows a description of some problems I encountered and the >> choices made in solving them: >> ; (define in (open-input-file "Recipe.txt")) >> ; If you just (string-split (read-line in)) you'll get: >> ; => '("hctz25" "30" "cp" "1xd") >> ; and that will not evaluate the symbols to their string descritptions. >> ; Because of that, you need to do a: >> ; > (map string->symbol (string-split (read-line in))) >> ; which will evaluate to >> ; => '(hctz25 |30| cp 1xd) >> ; This would be ideal to MAP EVAL to, but the problem is the |30| >> ; So, the idea is SET!ing that list to a name we can call easily, i.e., >> ; med-line-holder, because then we can extract the pieces (since we >> can't do list >> ; surgery easily, such a "replace the the element at position 1 with >> so-and-so element"). >> ; Since the prescription syntax is pretty rigid, we can get away with this >> ; simple approach. >> >> (define med-line-holder '()) ; initial value of med-line-holder is an empty >> list >> (define med-name-holder '()) >> (define med-quant-holder '()) >> (define med-form-holder '()) >> (define med-pos-holder '()) >> (define med-inst-holder '()) ; remember, not always INSTructions >> happen in a DSL prescription . >> >> (define in (open-input-file "Recipe.txt")) >> (port-count-lines! in) >> (define (clpr) (close-input-port in)) >> >> ; a med-line-holder is a list that has MED QUANT FORM POS (and sometimes >> INST) >> ; This is obtained from a plain text file. When it is read, it becomes >> something >> ; like this: '(hctz25 |30| cp 1xd) >> (define (set-med-line-holder) >> (set! med-line-holder (map string->symbol (string-split (read-line in))))) >> >> (define (set-med-name-holder) >> ; (set! med-name-holder (eval (car med-line-holder))) ;; in the REPL >> (set! med-name-holder (eval (car med-line-holder) >> (namespace-anchor->namespace a)))) >> >> (define (set-med-quant-holder) ; the CADR of the med-line-holder >> ; (set! med-quant-holder (eval (symbol->string (cadr med-line-holder)))) >> (set! med-quant-holder (eval (symbol->string (cadr med-line-holder)) >> (namespace-anchor->namespace a)))) >> >> (define (set-med-form-holder) ; the CADDR of the med-line-holder - >> gets the FORM, e.g., pills, etc. >> ; (set! med-form-holder (eval (symbol->string (caddr med-line-holder)))) >> (set! med-form-holder (eval (caddr med-line-holder) >> (namespace-anchor->namespace a)))) >> >> (define (set-med-pos-holder) ; the CADDDR of the med-line-holder - >> gets the POS, e.g., 1xd >> ; (set! med-pos-holder (eval (symbol->string (cadddr med-line-holder)))) >> (set! med-pos-holder (eval (cadddr med-line-holder) >> (namespace-anchor->namespace a)))) >> >> >> (define (set-med-inst-holder) ; the LAST of the med-line-holder - gets the >> INST >> ; (set! med-pos-holder (eval (symbol->string (last med-line-holder)))) >> (set! med-pos-holder (eval (last med-line-holder) >> (namespace-anchor->namespace a)))) >> >> ; One problem here regards the optional INST instructions. >> ; How to create a SETter function that will only SET! med-inst-holder >> ; if there's an INST instruction? Note that INST is a prefix. A real >> instruction is, e.g., >> ; INSTOMZ (for OMZ20). >> (define (look-for-line) >> (if (regexp-match #px"\\d\\-" (read-line in)) >> (begin >> (set-med-line-holder) >> (set-med-name-holder) >> (set-med-quant-holder) >> (set-med-form-holder) >> (set-med-pos-holder)) >> 'NO-LINE)) >> >> (define (display-stuff) >> (newline) >> (display med-line-holder) (newline) >> (display med-name-holder) (newline) >> (display med-quant-holder) (newline) >> (display med-form-holder) (newline) >> (display med-pos-holder) (newline)) >> ; The problem remains of what to do with the eventual INST. >> >> >> ; Successive calls to (look-for-line) would read the next lines. >> ; Output would alternate between a DSL line, or a NO-LINE (from >> look-for-line, >> ; if it hits a line with no text in Recipe.txt >> (look-for-line) >> ;(display-stuff) >> >> >> (define (output-a-line) >> (string-join (list med-name-holder line med-quant-holder med-form-holder >> "\n" >> med-pos-holder "\n"))) >> >> (define (format-a-line) >> (display (output-a-line))) >> >> ;(define (output-a-line) >> ; (display (string-join (list med-name-holder line med-quant-holder >> med-form-holder "\n" >> ; med-pos-holder "\n")))) >> (newline) >> ;(output-a-line) >> >> (format-a-line) >> >> >> >> ; PROBLEMS >> ; 1) How do we find out how many lines to (look-for-line)? >> ; This is one of the resons I specified the "1-", "2-" in the Recipe.txt. >> Not >> ; only it makes for easy visual understanding, but it may be used >> to provide a hint >> ; for this problem. >> ; Possible approaches: >> ; - Maybe this can be solved with REGEXPS? This information could >> provide a sentinel >> ; variable for an iterator function? >> ; - Is there some sort if line counting function? (Note that I have set >> ; (port-count-lines! in) somewhere above in the code. >> ; 2) How do we know we've reached the end of the file? >> ; 3) How to deal with the not-always-present INST? >> ; - How do we check for INSTs? With a REGEXP? >> ; - Choosing between INSTs with REGEXPS is not necessary, as they >> will be loaded in a module, >> ; so the system will "know" which one to choose. >> ; 4) Another idea would be "slurp" the whole of the prescription, and >> then deal with evaluation. How? >> ; (define f1 >> ; (file->string >> ; "C:\\Path\\to\\sources\\Recipe.txt")) >> ;> (string-normalize-spaces f1) >> ;"1- hctz25 30 pl 1xd 2- simva20 30 pl 1xn" >> ; >> ; That's all for now, folks! >> ; Many thanks for all the help so far, Racketeers! >> ; Cheers, >> ; Henry Lenzi >> >> >> >> >> On Sat, Aug 2, 2014 at 12:44 AM, Henry Lenzi <henry.le...@gmail.com> wrote: >>> Hello everyone - >>> >>> First of all, a big Thank You to all of you and for taking the time for >>> responding. >>> >>> I'll have to set aside sometime during this weekend to see if I can >>> understand the ideas you've been so kind to offer. >>> >>> However, I should confess that I've made some progress with way simpler >>> stuff which I hope to post later on. Like I've said, this is stupid >>> software. Anyways, none of this is final. >>> >>> It really just used a plain text solution, since the format if a recipe is >>> so rigid. The question of expanding the symbols from files to run-time was >>> easier than I thought. >>> >>> The idea of using modules might have the nice collateral effect if some sort >>> of primitive type (or syntax) checking for free. I like the idea someone >>> offered of using modules for medication definitions. Actually, one module >>> per definition makes it very easy for future users to add new medications. >>> The ease of syntax is important because it allows for the customization by >>> non-sophisticated users (physicians, nurses). >>> >>> Cheers, >>> Henry Lenzi. ____________________ Racket Users list: http://lists.racket-lang.org/users