How to obtain user input (as a number) to be applied to an equation - user-input

I am trying to program a scenario where a user will input two pieces of information, a 6-minute walk distance in meters at baseline (6MWDbaseline) and then a 6-minute walk distance in meters at 24-weeks (6MWD24weeks). I want the user to supply those pieces of information rather than me asserting them within the program. Once those numbers are entered they need to be applied to the equation:
(6MWD24weeks - 6MWDbaseline) = x
and then to this equation:
x / 6MWDbaseline = y
Where, if y >/= 0.2 then the program will denote success.
If y is between 0.05-0.19 then the program will denote clinical improvement.
If y is <0.049 then the program will denote failure.
I get an error early on in my script testing, before I can even try to program my 'clinical improvement' or 'failure' lines, that my user inputs of 6MWDbaseline and 6MWD24weeks are expected to be integers or floats. Any guidance on what I might be doing wrong?
CLIPS> (clear)
CLIPS> (defrule MAIN::6WMDbaseline-check
=>
(printout t "What is the distance on the baseline 6-minute walk distance in meters?" crlf)
(assert (6MWDbaseline (read))))
CLIPS> (defrule MAIN::6MWD24week-check
=>
(printout t "What is the distance on the 24-week 6-minute walk distance in meters?" crlf)
(assert (6MWD24week (read))))
CLIPS> (defrule MAIN::success-decision
(6MWDbaseline ?6MWDbaseline)
(6MWD24week ?6MWD24week)
=>
(if (- 6MWD24week 6MWDbaseline = x) and (/ x 6MWDbaseline >0.2))
then
(printout t "Primary outcome met, greater than 20% improvement in 6-minute walk distance" crlf))
[ARGACCES2] Function '-' expected argument #1 to be of type integer or float.
ERROR:
(defrule MAIN::success-decision
(6MWDbaseline ? 6MWDbaseline)
(6MWD24week ? 6MWD24week)
=>
(if (- 6MWD24week 6MWDbaseline = x)
CLIPS>
Thanks in advance for any assistance!
Marnie

Use the bind function to assign values to variables in the actions of a rule. In addition, variable names must begin with a letter.
CLIPS (6.4 2/9/21)
CLIPS>
(defrule 6WMDbaseline-check
=>
(printout t "What is the distance on the baseline 6-minute walk distance in meters?" crlf)
(assert (6MWDbaseline (read))))
CLIPS>
(defrule 6MWD24week-check
=>
(printout t "What is the distance on the 24-week 6-minute walk distance in meters?" crlf)
(assert (6MWD24week (read))))
CLIPS>
(defrule success-decision
(6MWDbaseline ?baseline)
(6MWD24week ?week24)
=>
(bind ?x (- ?week24 ?baseline))
(bind ?y (/ ?x ?baseline))
(switch TRUE
(case (> ?y 0.2)
then
(printout t "Primary outcome met, greater than 20% improvement in 6-minute walk distance" crlf))
(case (and (>= ?y 0.05) (<= ?y 0.2))
then
(printout t "Primary outcome improved, between 5% and 20% improvement in 6-minute walk distance" crlf))
(case (< ?y 0.05)
then
(printout t "Primary outcome failed, less than 5% improvement in 6-minute walk distance" crlf))))
CLIPS> (reset)
CLIPS> (run)
What is the distance on the baseline 6-minute walk distance in meters?
100
What is the distance on the 24-week 6-minute walk distance in meters?
121
Primary outcome met, greater than 20% improvement in 6-minute walk distance
CLIPS> (reset)
CLIPS> (run)
What is the distance on the baseline 6-minute walk distance in meters?
100
What is the distance on the 24-week 6-minute walk distance in meters?
115
Primary outcome improved, between 5% and 20% improvement in 6-minute walk distance
CLIPS> (reset)
CLIPS> (run)
What is the distance on the baseline 6-minute walk distance in meters?
100
What is the distance on the 24-week 6-minute walk distance in meters?
104
Primary outcome failed, less than 5% improvement in 6-minute walk distance
CLIPS>

Related

Undesired output in Racket

I'm working on a homework assignment and am a little bit confused as to what produced this output. Here is the code:
#lang racket
(define (my_calc calcType radius); define the function and set up the parameters
(define PI 3.1416); define PI
(cond ((and (= calcType 1) (positive? radius))); check to see whether the calc function is one and the radius is positive
((* 2 PI radius radius)); return the area of a circle
((and (= calcType 2) (positive? radius))); check to see whether the calc function is 2 and the radius is positive
((* (/ 4 3) PI radius radius radius)); return the volume of a sphere
(else #f); if calc function is not 1 or 2 or the radius is not positive, return false
)
)
(my_calc 1 10)
(my_calc 2 10)
(my_calc 0 1)
(my_calc 1 0)
and here is the output:
#t
628.32
6.2832
0
I have no idea why the first one would return true, it should return the area of a circle. There isn't even a return statement that would allow that. The second one seems to work just fine. The third one should produce false, but it produces the second answer divided by a hundred for some reason. And the 0 at the bottom is beyond me. Can anyone help me? Thank you.
Your first procedure in a cond case has to be the test expression followed by the result. See the documentation, https://docs.racket-lang.org/reference/if.html.
For example:
(cond
[; test expression
(and (= calcType 2) (positive? radius))
; following result
((* (/ 4 3) PI radius radius radius))]
[else whatever]
)
I would recommend using square brackets for each cond case for legibility.
In your case,
[(and (= calcType 1) (positive? radius))]
The test expression is (and (= calcType 1) (positive? radius)) but there is no following procedure.
You have the same problem for all of your cond cases. Good luck.

Getting the measurements for stretching

I am creating an algorithm to help me expand boxes to the correct size, as such:
I made a code that asks two points that should be the new depth and measures them. Then subtracts to the original depth of the block (0.51) and then asks the side to stretch.
(defun mystretchh ( dis / pt1 pt2 sel )
(while
(and
(setq pt1 (getpoint "\nFirst point of selection window: "))
(setq pt2 (getcorner pt1 "\nSecond point of selection window: "))
(not (setq sel (ssget "_C" pt1 pt2)))
)
(princ "\nNo objects where found in the selection window.")
)
(if sel
(progn
(command "_.stretch" sel "" "_non" '(0 0) "_non" (list dis 0))
t
)
)
)
(defun c:test (/ a x c p)
;ungroup
(command "pickstyle" 0)
;variables
(initget (+ 1 2 4))
(setq p (getpoint "\nSelect first point: "))
(setq c (getpoint "\nSelect second point: "))
(command "_.dist" p c)
(setq x (rtos (getvar 'distance) 2 3))
;calculate distance to stretch
(setq a (- x 0.51))
;stretch
(mystretchh a)
;regroup
(command "pickstyle" 1)
(print "Module modified.")
(princ)
)
I have two problems:
The stretch is working backwards, I tried using negative values to no avail.
It reports a syntax error, but I cannot find it.
I haven't touched AutoLISP for half a year - maybe some of you can find the problems in a blink of an eye.
You can calculate the distance between two points using the standard distance function, rather than calling the DIST command and then retrieving the value of the DISTANCE system variable.
Hence, this:
(command "_.dist" p c)
(setq x (rtos (getvar 'distance) 2 3))
Can become:
(setq x (rtos (distance p c) 2 3))
However, you are receiving a syntax error because you have converted the distance to a string using rtos, and then you are attempting to perform arithmetic on the string here:
(setq a (- x 0.51))
There is no need to convert the distance to a string, and so these expressions can become:
(setq a (- (distance p c) 0.51))
You may also want to check whether (distance p c) is greater than 0.51 before performing this subtraction to avoid unexpected results.
To determine the appropriate direction, since your current code can only stretch along the x-axis, you'll need to check whether or not the x-coordinate of point p is greater than that of point c.

Why are circle center coordinates (key 10) not relative to the origin in DXF data?

I need this piece of information for a filter that I'm creating.
So let's say I set my grid and snap to 1 for example and then I place the origin with UCS.
And then I draw a circle with center 5, 0.
Here is what I get:
(
(-1 . <Entity name: 1f3dbb9d580>)
(0 . "CIRCLE")
(330 . <Entity name: 1f3dbba51f0>)
(5 . "270")
(100 . "AcDbEntity")
(67 . 0)
(410 . "Model")
(8 . "0")
(100 . "AcDbCircle")
(10 2495.0 1180.0 0.0)
(40 . 3.16228)
(210 0.0 0.0 1.0)
)
Why at number 10 I have those numbers?
Shouldn't it be like (10 5.0 0.0 0.0)?
The coordinates defining the geometry of the majority of planar entities (such as arcs, circles, 2D polylines etc.) are defined relative to a coordinate system known as the Object Coordinate System (OCS).
The OCS shares its origin with the World Coordinate System (WCS), with its Z-axis corresponding to the normal vector (aka extrusion vector) associated with the entity (represented by DXF group 210), and its X & Y axes defined by the Arbitary Axis Algorithm applied to the normal vector.
The Arbitrary Axis Algorithm is implemented in the standard AutoLISP trans function, which facilitates easy transformation of points from one coordinate system to another.
In your particular example, the normal vector is (0.0 0.0 1.0), which is equal to the normal vector of the WCS plane, and so for this particular example, the OCS is equal to the WCS.
However, in general, to translate points from an arbitrary OCS to either the WCS or the active User Coordinate System (UCS), you would supply the trans function with either the OCS normal vector or the entity name of the entity in question.
For example, translating from OCS to the active UCS using the OCS normal vector:
(trans (cdr (assoc 10 <dxf-data>)) (cdr (assoc 210 <dxf-data>)) 1)
Or, translating from OCS to the active UCS using the entity name:
(trans (cdr (assoc 10 <dxf-data>)) (cdr (assoc -1 <dxf-data>)) 1)
Implemented in a sample program, this might be:
(defun c:test ( / ent enx )
(cond
( (not (setq ent (car (entsel "\nSelect circle: "))))
(princ "\nNothing selected.")
)
( (/= "CIRCLE" (cdr (assoc 0 (setq enx (entget ent)))))
(princ "\nThe selected object is not a circle.")
)
( (princ "\nThe circle center relative to the UCS is: ")
(princ (trans (cdr (assoc 10 enx)) ent 1))
)
)
(princ)
)
Addressing the issues you are encountering as described in your comments, you'll need to transform the coordinates from/to the OCS & UCS to achieve the desired result, for example:
(defun c:test ( / ent enx new old xco )
(cond
( (not (setq ent (car (entsel "\nSelect circle: "))))
(princ "\nNothing selected.")
)
( (/= "CIRCLE" (cdr (assoc 0 (setq enx (entget ent)))))
(princ "\nThe selected object is not a circle.")
)
( (setq old (assoc 10 enx)
xco (car (trans (cdr old) ent 1))
new (cons 10 (trans (list xco 0.0 0.0) 1 ent))
enx (subst new old enx)
)
(entmod enx)
)
)
(princ)
)
The operation could also be condensed to a single expression, e.g.:
(setq old (assoc 10 enx)
enx (subst (cons 10 (trans (list (car (trans (cdr old) ent 1)) 0) 1 ent)) old enx)
)
(entmod enx)
However, this is less readable.
In this list coordinates are in WCS. So if You draw circle using command and active UCS is differend than WCS, everything is OK the result is as expected.
To translate coordinates between coordinate systems You can use
(trans (assoc 10 YourList) 0 1 nil)

Simplifying a Racket function

I have the following function "change" which takes a certain amount of money to be paid, the size of the bill/coin used to pay, and returns a list with the number of "coins" ($50, $20 $10 $5 $2 and $1) one would receive after completing the transaction:
(define (change total payment)
(let [(x (- payment total))]
(change-aux x '(50 20 10 5 2 1))))
(define (change-aux change coins)
(cond
[(empty? coins) empty]
[else (let [(num-coins (floor (/ change (car coins))))]
(append (list num-coins)
(change-aux (- change (* (car coins) num-coins)) (cdr coins))))]))
So, if I input these parameters:
> (change 44 200)
It returns the output:
'(3 0 0 1 0 1)
That's 200-44 = 156, which corresponds to 3 coins worth $50, 1 worth $5 and 1 worth $1.
My question would be if there's a more elegant, simplified way to write a similar procedure without relying on auxiliary functions, and rather use lambda, filter, map, foldr, foldl etc?
Thanks in advance.
Here is a solution in a different Lisp dialect which shows how to do it with a left fold (reduce) without any mutation of an accumulator variable, as a kind of functional counterpoint to the existing solution.
(defun change (amount coins)
(reduce-left (tb ((counts . rem) next-coin)
(let* ((coin-count (floor rem next-coin))
(coin-value (* coin-count next-coin)))
(cons (cons coin-count counts)
(- rem coin-value))))
coins
(cons '() amount)))
3> (change 156 '(50 20 10 5 2 1))
((1 0 1 0 0 3) . 0)
4> (change 88 '(50 20 10 5 2 1))
((1 1 1 1 1 1) . 0)
Note that the values end up reported in reverse order and wrapped in an extra cons cell; a "porcelain" function could be used around this "plumbing" to report the result in the expected form.
The idea is that we have an accumulator which looks like this: (counts . remainder). The counts part of the accumulator stored in the car is the list of coins accumulated so far. When the reduce is done, this holds the final list. The cdr field holds the remaining amount to be processed; since the last coin is 1, this will always emerge as zero.
Using this accumulator structure, we process the list of coins.
On each call to our reduce kernel function, the left argument is the accumulator, and the right argument, next-coin, is the next coin denomination value.
I used a macro called tb ("tree bind") macro, which is a kind of lambda that provides built-in destructuring, to make it look like we have three parameters.
The initial value for the reduce job is the starting accumulator, which has an empty list of coins, and the full original amount: (cons nil amount) (which I rewrote to (cons '() amount) for better Scheme compatibility).
The reduce function is very simple: greedily calculate how many of the next coin value are needed to represent the remainder, and then calculate the new remainder, packaging these up into a new accumulator cons cell that is returned, and is passed to the next invocation of the function, or returned when the list of coin values has been processed.
Hopefully this points the way to "a more elegant, simplified way to write a similar procedure without relying on auxiliary functions, and rather use lambda, filter, map, foldr, foldl etc" that you can work out in Racket. Good luck!
Sure, you can.
Final solution
(define (change total payment (coins '(50 20 10 5 2 1)))
(let ((rest-diff (- payment total)))
(map (lambda (coin)
(let ((num-coins (floor (/ rest-diff coin))))
(set! rest-diff (- rest-diff (* num-coins coin)))
num-coins))
coins)))
Step by step
First of all, using inner define, you can get rid of the auxiliary function from the global namespace.
(define (change total payment)
(define (change-aux change coins)
(cond
[(empty? coins) empty]
[else (let [(num-coins (floor (/ change (car coins))))]
(append (list num-coins)
(change-aux (- change (* (car coins) num-coins)) (cdr coins))))]))
(let [(x (- payment total))]
(change-aux x '(50 20 10 5 2 1))))
Then, you can pull some variables of the helper function's to the global function's lambda list.
(define (change total payment (coins '(50 20 10 5 2 1)))
(define (change-aux change) ;; eliminate coins in the inner lambda list
(cond
[(empty? coins) empty] ;; coins in function body looked up from outer arguments
[else (let [(num-coins (floor (/ change (car coins))))]
(append (list num-coins)
(change-aux (- change (* (car coins) num-coins)) (cdr coins))))]))
(let [(x (- payment total))]
(change-aux x))) ;; eliminate coins in the call
Then, looking at the code of change-aux, one understands this is actually
a looping through and trying to fit maximal multiples of current value
into the rest of the difference remaining - and collecting those reults. One could loop using map and use set! to mutate the rest.
(define (change total payment (coins '(50 20 10 5 2 1)))
(let ((rest-diff (- payment total)))
(map (lambda (coin)
(let ((num-coins (floor (/ rest-diff coin))))
(set! rest-diff (- rest-diff (* num-coins coin)))
num-coins))
coins)))
Then, you call like above:
(change 44 200)
;; '(3 0 0 1 0 1)

How to sum all numbers that are divisible by 3 or 5 below 1000 in Lisp?

I started programming in Common Lisp yesterday. Now I want to find the sum of all the multiples of 3 or 5 below 1000. I came up with:
(loop for n from 1 to 1000 when
(or
(eq (mod n 5) 0)
(eq (mod n 3) 0))
(sum n)))
I know that the looping part works (loop for n from 1 to 1000 sum n) to sum the first 1000 numbers. I know that the ((eq (mod n 5) 0) (mod n 3) 0)) parts works. And I know that (or (eq (mod n 5) 0) (eq (mod n 3) 0)) works. So it looks like a robust program to me, but when I run it I get the error:
1=(SUM N) found where keyword expected getting LOOP clause after WHEN current LOOP context: WHEN (OR (EQ (MOD 1000 5) 0)
(EQ (MOD 1000 3) 0))
1#. [Condition of type SB-INT:SIMPLE-PROGRAM-ERROR]
I suspect something is wrong with the (sum n) after the or-statement. But I do not know why that is or how I can solve it. Can someone help me out and get my first Lisp program to work?
sum n, not (sum n)
Don't put sum n in parentheses. The loop macro is its own domain specific language with its own grammar. With it, you'd (loop for ... sum n). The grammar is given in HyperSpec entry on loop in this production:
numeric-accumulation::= {count | counting | sum | summing | }
maximize | maximizing | minimize | minimizing {form | it}
[into simple-var] [type-spec]
If it sounds any better to, you can also write (loop for … summing n). That might read more like a natural English sentence.
=, eql, or zerop, but not eq
It's good practice to get into looking up functions, macros, etc., in the HyperSpec. As Rainer Joswig points out, you shouldn't use eq for comparing numbers. Why? Let's look it up in the HyperSpec. The examples include:
(eq 3 3)
=> true
OR=> false
(eq 3 3.0) => false
(eq 3.0 3.0)
=> true
OR=> false
(eq #c(3 -4) #c(3 -4))
=> true
OR=> false
and the Notes section says (emphasis added):
Objects that appear the same when printed are not necessarily eq to
each other. Symbols that print the same usually are eq to each other
because of the use of the intern function. However, numbers with the
same value need not be eq, and two similar lists are usually not
identical.
An implementation is permitted to make "copies" of characters and
numbers at any time. The effect is that Common Lisp makes no guarantee
that eq is true even when both its arguments are "the same thing" if
that thing is a character or number.
For numbers you need something else. = is a good general numeric comparison, although it does more work here than what you need, because it can compare numbers of different types. E.g., (= 5 5.0) is true. Since you're only concerned about 0, you could use zerop, but that will still do a bit more work than you need, since it will check other numeric types as well. E.g., (zerop #c(0.0 0.0)) is true. In this case, since (mod n …) will be giving you an integer, you can use eql:
The value of eql is true of two objects, x and y, in the folowing
cases:
If x and y are eq.
If x and y are both numbers of the same type and the same value.
If they are both characters that represent the same character.
Thus, you can use (or (eql (mod n 3) 0) (eql (mod n 5) 0)).
Other ways of doing this
Now, your question was about a particular piece of loop syntax, and there were some points to be made about equality operators. However, since some of the other answers have looked at other ways to do this, I think it's worth pointing out that there are much more efficient ways to do this. First, let's look at a way to sum up all the multiples of some number beneath a given limit. E.g., for the number 3 and the inclusive limit 26, we have the sum
?
= 3 + 6 + 9 + 12 + 15 + 18 + 21 + 24
= (3 + 24) + (6 + 21) + (9 + 18) + (12 + 15)
= 27 + 27 + 27 + 27
In general, if you try with a few different numbers, you can work out that for an inclusive limit l and a number n, you'll be adding up pairs of numbers, with an optional half pair if there's a odd number of multiples of n that are less than l. I'm not going to work out the whole derivation, but you can end up with
(defun sum-of-multiples-below-inclusive (limit divisor)
(multiple-value-bind (quotient remainder)
(floor limit divisor)
(let ((pair (+ (- limit remainder) divisor)))
(multiple-value-bind (npairs half-pair)
(floor quotient 2)
(+ (* npairs pair)
(if (oddp half-pair)
(floor pair 2)
0))))))
Then, to find out the sum of the number of multiples less than a given number, you can just substract one from limit:
(defun sum-of-multiples-below (limit divisor)
(sum-of-multiples-below (1- limit) divisor))
Then, to expand to your case, where there are multiple divisors, you'll need to add some of these numbers, and then subtract out the ones that are getting counted twice. E.g., in your case:
(+ (sum-of-multiples-below 1000 3)
(sum-of-multiples-below 1000 5)
(- (sum-of-multiples-below 1000 15)))
;=> 233168
(loop for i from 1 below 1000
when (or (eql 0 (mod i 3))
(eql 0 (mod i 5)))
sum i)
;=> 233168
Now, using time naively can lead to misleading results, but SBCL compiles forms before it evaluates them, so this isn't too terrible. This is a very, very, small micro-benchmark, but take a look at the number of cycles used in each form:
(time (+ (sum-of-multiples-below 1000 3)
(sum-of-multiples-below 1000 5)
(- (sum-of-multiples-below 1000 15))))
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
11,327 processor cycles
0 bytes consed
(time (loop for i from 1 below 1000
when (or (eql 0 (mod i 3))
(eql 0 (mod i 5)))
sum i))
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
183,843 processor cycles
0 bytes consed
Using the closed form is much faster. The different is more pronounced if we use a higher limit. Let's look at 100,000:
(time (+ (sum-of-multiples-below 100000 3)
(sum-of-multiples-below 100000 5)
(- (sum-of-multiples-below 100000 15))))
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
13,378 processor cycles
0 bytes consed
(time (loop for i from 1 below 100000
when (or (eql 0 (mod i 3))
(eql 0 (mod i 5)))
sum i))
Evaluation took:
0.007 seconds of real time
0.004000 seconds of total run time (0.004000 user, 0.000000 system)
57.14% CPU
18,641,205 processor cycles
0 bytes consed
For 10,000,000 the numbers are even more staggering:
(time (+ (sum-of-multiples-below 10000000 3)
(sum-of-multiples-below 10000000 5)
(- (sum-of-multiples-below 10000000 15))))
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
13,797 processor cycles
0 bytes consed
(time (loop for i from 1 below 10000000
when (or (eql 0 (mod i 3))
(eql 0 (mod i 5)))
sum i))
Evaluation took:
0.712 seconds of real time
0.712044 seconds of total run time (0.712044 user, 0.000000 system)
100.00% CPU
1,916,513,379 processor cycles
0 bytes consed
Some of these Project Euler problems are pretty interesting. Some of them have some pretty straightforward naïve solutions that work for small inputs, but don't scale well at all.
I would format the code like this:
(loop for n from 1 below 1000
when (or (zerop (mod n 3))
(zerop (mod n 5)))
sum n))
one line less
when at the start of a line
no need to have or alone on a line
clauses in LOOP don't have parentheses
use below
This kind of Loop macro goes back to the early 70s to Interlisp, long before Common Lisp existed.
Here is a another solution without loop
(defun sum-to-thousand (count result)
(cond ((> count 1000) result)
((= (mod count 3) 0) (sum-to-thousand (+ count 1) (+ count result)))
((= (mod count 5) 0) (sum-to-thousand (+ count 1) (+ count result)))
(t (sum-to-thousand (+ count 1) result))))
May I propose more "lispier" variant:
CL-USER> (defun my-sum (&key (from 1) to dividers (sum 0))
(if (>= from to)
sum
(my-sum :from (1+ from)
:to to
:dividers dividers
:sum (if (some (lambda (x) (zerop (mod from x))) dividers)
(+ sum from)
sum))))
MY-SUM
CL-USER> (my-sum :to 1000 :dividers '(3 5))
233168