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