(define-namespace-anchor a)
(map (λ(str) (eval str (namespace-anchor->namespace a)))'((eval *med-name*) line *quant-str* pl) )
- Henry Lenzi
> (map eval '(*med-name* line *quant-str* pl))
'(hctz25 "-----------" "30" "pills")
Why wasn't the symbol "hctz25" expanded? I'm even more confused,
because this works (again, in the REPL).
> (eval *med-name*)
"Hydroclorothiazide 25mg"
TIA,
Henry Lenzi
Because *med-name* is different. The other three are the names of variables that hold strings. But *med-name* is the name of a variable that holds the name of another variable that holds a
Hi Neil --
So how do you export hash keys as symbols to a file that can be read
in again, not as string?
Now, I haven't gotten around to reading the whole of Racket Scheme's
documentation... Things are looking kind of hard.
What I'm attempting to do is then read back the symbols defined, such
as the one below:
(define hctz25 "Hydrochlorothiazide 25mg")(close-input-port in)'|'hctz25|
(define in (open-input-file "Recipe.txt"))
(string->symbol (read-line in))
But what I really want is the "hctz25" symbol that evaluates to a
string. If I don't use string->symbol, I get the string "hctz25". And
why the bars ("|”)?
This should be simple...I write
> (define out (open-output-file "Recipe.txt"))
> (print *med-name* out)
> (close-output-port out)
and what I see in the file is:
'hctz25
How can I import this from a file?
If I use (read in), I get:
''hctz25
(two quotes)
The issue here was that there was a definition
(define hctz25 "Hydrochlorothiazide 25mg)
and hctz25 thus evaluates to a string.
How can I get it back from a file, retaining the capacity to evaluate
it to its definition. That's sort of the issue.
TIA
- Henry
Thanks, Vincent. But a recipe is more than those simple definitions.
Actually, it would be something like:
MEDICATION DOSE UNITS ----------------- QUANTITY FORM
INSTRUCTIONS FOR TAKING MEDICATION
In my previous Forth version, this was extremely easy to achieve, as
Forth is syntax-less, and "code is data, and data is code". So the
recipe turned out to be a mini language written in Forth itself.
(read in) doesn't work.
This should be simple...I write(define out (open-output-file "Recipe.txt"))
(print *med-name* out)
(close-output-port out)
and what I see in the file is:
'hctz25
How can I import this from a file?
If I use (read in), I get:
''hctz25
(two quotes)
Do you mean the Forth files?
I don't belienve they would make much sense to you, but it would go
something like this (as you can see, that is a FORTH definition):
: NAME S"John Doe"
CU4
HCTZ25 30P 1CPM
OMZ20 30P 1CPM INSTOMZ
SIMVA20 30P 1CPN
L\D ;
Expands to (NOTE: Some things are germane to our public health
system, such as renewing "continuous use" gratis medications, recipe
valid for 4 months):
John Doe
Continuous use - 4 months
Hydrochlorothiazide 25mg ---------------- 30 pills
Take 1 pill P.O. in the morning.
Omeprazol 20mg ----------------------------- 30 pills
Take 1 pill P.O. in the morning, 1/2 hour
before breakfast.
Simvastatin 20mg ---------------------------- 30 pills
Take 1 pill P.O. at night.
City, xx/xx/xxxx
So what´s happening here is that inside the FORTH definition,
everything delimited by ":" and ";" is a FORTH word, as they say, that
is to say, valid FORTH code.
The very cheap trick here is simply writing a file with plain text
(but called .fth, .f or other FORTH designations for filetypes)
begining with a ":", ending with a ";", and everything in between,
which are the FORTH words.
The FORTH reader than opens this file. As soon as it hits the ":", it
recognizes it's FORTH code. It's all amazingly stupid. However, what
you get is: a DSL hassle-free (no parsing/lexing), a flat-file
database for free (the name of the files), an interpreter (comes with
the territory). And code is data, data is code, in a very, very
concrete way.
Cheers,
Henry Lenzi
____________________
Best,
Robby
This is all internal, nothing is or will be published (but I wouldn't
care/mind releasing the code, when and if I have it). The way to think
of this is: I could be typing stuff out on Word, or I could use the
hospital's buggy and horrible software. But I'll just roll my own,
like I did last year on another job (the FORTH thing - which is
abandoware right now, although it served me well).
This is isn't complex software, you know? This is very stupid stuff.
(But I do realize people make money selling stupid software for
physicians...). What I think I can achieve with Scheme is printing
directly on a .jpeg image (the hospital's layout form for recipes),
using Racket's imaging library. This would be...fun! AND useful. In a
more advanced stage, I can play with optimization and constraints
(e.g., calculating how to properly fit stuff on the page, etc.)
Another thing I want to write is software to monitor our 300
diabetic/hypertensive patients. I had some great ideas looking at
Scheme's graphic capabilities. This would be something
spreadsheet-like, with various (graphical) bells and whistles for
alerts (such as renal function parameters, global cardiovascular risk,
etc.)
We're not a a big hospital complex in the USA. We're in a big public
health hospital complex in southern Brazil. Our hospital doesn't have
the cash for the latest and greatest for medical software. However, I
still prefer my shorthand DSL than anything else I've seen. You show
"HCTZ25 30P 1XD or OMZ20 30P 1P/M" to *any* doctor and I'm willing to
bet they'll IMMEDIATELY grok what it means. That's the power of a
proper DSL.
And, when and if I write this thing, it'll be for me and my colleague
only (that's 3.000 patients), possibly extending to the rest of the
team (that would be 9.000 patients). So, internal, in-house.
Who knows when I'll have this. I'm a busy physician (like all of us).
If I have to start reading advanced mumbo-jumbo or papers with
denotational semantics in order to understand what in the world is the
matter with EVAL in Racket Scheme, I might just give up and go back to
Win32 API (easier, right?) and my proprietary/licensed FORTH that can
ship executables (royalty-free).
(I'm wasting too much time here, when I should be testing the answers
the fine Racketeers have provided me with) ;-)
Cheers,
Henry Lenzi
Cheers,
Henry Lenzi
Cheers,
Henry Lenzi
What you saw was precisely the database file (it's a file written in
the programming language FORTH).
Cheers,
Henry
On Wed, Jul 30, 2014 at 10:48 PM, Daniel Prager
____________________
: NAME S"John Doe"CU4HCTZ25 30P 1CPMOMZ20 30P 1CPM INSTOMZSIMVA20 30P 1CPN
L/D;
'(script(name "John Doe")(directions (CU 4))(medication (HCTZ 25) (P 30) (CPM 1))(medication (OMZ 20) (P 30) (CPM 1) (INSTOMZ))(medication (SIMVA 20) (P 30) (CPN 1)))
(shorthand->script
"John DoeCU4HCTZ25 30P 1CPMOMZ20 30P 1CPM INSTOMZ
SIMVA20 30P 1CPN")
John Doe
Continuous use - 4 months
Hydrochlorothiazide 25mg ---------------- 30 pills
Take 1 pill in the morning.Omeprazol 20mg ---------------- 30 pillsTake 1 pill in the morning, 1/2 hour before breakfast.Simvastatin 20mg ---------------- 30 pillsTake 1 pill at night.
; 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
____________________
http://pasterack.org/pastes/14535
-- Henry Lenzi
Thanks. But what would "thing" be?
> (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
-- Henry
On Sun, Aug 3, 2014 at 9:38 PM, Alexander D. Knauth
This is nice, the problem is separating the output into the proper
formatting. I feel this has to be done a step before the expansion
into the string form.
One thing I'm considering is that that the DSL is made of MED QUANT
FORM POS or MED QUANT FOR POS INST, so 4 or 5 items.
If we have a symbol list (before string expansion), than we can treat
INST as the divider marker. Otherwise, there's a linebreak at every
fourth item.
I'm looking into how to to this using a position function:
(define (position item list)
(- (length list)
(length (member item list))))
Knowing where the INST instructions occur help up decided whether we
can break a line into the
MED QUANT FORM
POS
or
MED QUANT FORM
POS
INST
forms (see the code snippet on http://pasterack.org/pastes/14535 )
Cheers,
Henry
____________________
Matthew Flatt wrote at 08/04/2014 02:40 AM:While he didn't say so explicitly, I don't think that Neil is worried
about the implementation of `eval` within Racket. After all, `eval` is
at the heart of the implementation, and any program that you give to
Racket is going through `eval` whether or not the program calls the
`eval` function.
Correct. I breathe oxygen constantly, without thinking about it, but I'd never use a fuel-air explosive to mow my lawn.
Matthew Flatt wrote at 08/04/2014 02:40 AM:While he didn't say so explicitly, I don't think that Neil is worried
about the implementation of `eval` within Racket. After all, `eval` is
at the heart of the implementation, and any program that you give to
Racket is going through `eval` whether or not the program calls the
`eval` function.
Correct. I breathe oxygen constantly, without thinking about it, but I'd never use a fuel-air explosive to mow my lawn.