Oh, OK. :-) -- Henry
On Sun, Aug 3, 2014 at 9:38 PM, Alexander D. Knauth <alexan...@knauth.org> wrote: > > On Aug 3, 2014, at 7:58 PM, Henry Lenzi <henry.le...@gmail.com> wrote: > >> Alexander's idea is interesting, but it onlt works if the >> prescription file is not numbered (which is actually more natural), >> such as if it were: >> hctz25 30 pl 1xd >> simva20 30 pl 1xn >> >>> (define in2 (open-input-file "Recipe3.txt")) >>> (port->list (compose1 eval read) in2) >> '("Hydrochlorothiazide 25mg" >> 30 >> "cps" >> "Take 1 pill P.O. 1x/day" >> "Simvastatin 20mg" >> 30 >> "cps" >> "Take 1 pill P.O. 1x at night") >> >> The issue would then be about extracting and joining 4 or 5 (if it has >> an INST instruction) items from that list. >> string-join, however, will bork at numbers. So it's kind of the same >> issue as previously than with |30|. > > Then just do this: (string-join (map ~a (port->list (compose1 eval read) > in2)) instead. > >> >> in what regards the presence of INSTs, maybe this could be approached >> by first scanning the list for an INST instruction using REGEXPs, but >> I don't know how to do that yet. >> >> >> Thanks, >> >> Henry Lenzi >> >> On Sun, Aug 3, 2014 at 5:40 PM, Alexander D. Knauth >> <alexan...@knauth.org> wrote: >>> >>> On Aug 3, 2014, at 4:29 PM, Alexander D. Knauth <alexan...@knauth.org> >>> wrote: >>> >>>> >>>> On Aug 3, 2014, at 3: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| >>>> >>>> What you want here is something like this: >>>> ;; Instead of (map string->symbol (string-split (read-line in))) >>>> (for/list ([thing (in-port read in)]) thing) >>> >>> Actually come to think of it you can do this: >>> (sequence->list in) >>> Or this: >>> (port->list read in) >>> >>>> ;; and then you can do (map eval …) to that if you want. >>>> ;; Or you could do both at once like this: >>>> (for/list ([thing (in-port read in)]) >>>> (eval thing namespace)) >>> >>> Or for that: >>> (port->list (compose1 eval read) in) >>> >>>> >>>>> ; 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”). >>> >>> look at list-set from unstable/list >>> >>>>> ; 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 >>>> >>>> >>>> ____________________ >>>> Racket Users list: >>>> http://lists.racket-lang.org/users >>> >> >> ____________________ >> Racket Users list: >> http://lists.racket-lang.org/users > ____________________ Racket Users list: http://lists.racket-lang.org/users