[racket] Use of map and eval to evaluate symbol in namespace
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|.
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
<alexander at knauth.org> wrote:
>
> On Aug 3, 2014, at 4:29 PM, Alexander D. Knauth <alexander at knauth.org> wrote:
>
>>
>> On Aug 3, 2014, at 3:29 PM, Henry Lenzi <henry.lenzi at 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.lenzi at 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
>