This is a prolog version of the code that works correctly:
rc2(Status,Income,Rate):-
itx2(Status,Min,Max,MA,SA), Income >= Min, Income < Max, Rate is
(Income * MA) - SA.
tx2(single,100000,146750,0.28,5373.00).
itx2(single,146750,319100,0.33,12710.50).
itx2(single,319100,10000000,0.35,19092.50).
itx2(married_filing_jointly,100000,117250,0.25,6525.00).
itx2(married_filing_jointly,117250,178650,0.28,10042.50).
itx2(married_filing_jointly,178650,319100,0.33,18975.00).
itx2(married_filing_jointly,319100,10000000,0.35,25357.00).
itx2(married_filing_separately,100000,159550,0.33,9487.50).
itx2(married_filing_separately,159550,10000000,0.35,12678.50).
itx2(head_of_household,100000,100500,0.25,4400.00).
itx2(head_of_household,100500,162700,0.28,7415.00).
itx2(head_of_household,162700,319000,0.33,15500.00).
itx2(head_of_household,319000,10000000,0.35,21932.00).
~
·········@yahoo.com writes:
> For example (citx2 single 150000) should return (single 146750 319100
> 0.33 12710.50)
You need to call it like (citx2 'single 150000).
> (defun citx2 (status income)
> (let ((lst (list status income))
> (find lst itx2 :test test-itx2))))
One closing parenthesis is in the wrong line.
Write test-itx2 as either 'test-itx2 as #'test-itx2 so that it
won't be taken as the name of a variable. (If test-itx2 were
a local function, you'd have to use #'test-itx2, but as it is
global, 'test-itx2 will work too.)
"Nelson Marcelino" <·········@gmail.com> writes:
> (defun citx2 (status income)
> (let ((lst (list status income)))
> (find lst itx2 :test #'test-itx2)))
>
> (defun test-itx2 (lst el)
> (and (eql (car lst) (car el))
> (<= (second el) (second lst))
> (< (second lst) (third el))))
>
> (defparameter itx2 '(...))
>
> CL-USER 1> (citx2 'single 150000)
> (SINGLE 146750 319100 0.33 12710.5)
>
> CL-USER 2> (citx2 'married-filing-jointly 150000)
> (MARRIED-FILING-JOINTLY 117250 178650 0.28 10042.5)
>
> now works correctly. Just curious, is there any other idiomatic (better
> or more concise) way of doing this?
Yes. The idiomatic name for generic list in lisp is list.
(defun citx2 (status income)
(let ((list (list status income)))
(find list itx2 :test #'test-itx2)))
(defun test-itx2 (list el)
(and (eql (car list) (car el))
(<= (second el) (second list))
(< (second list) (third el))))
When you use a variable only once, it may be good to avoid the binding:
(defun citx2 (status income)
(find (list status income) itx2 :test #'test-itx2))
The idiomatic convention for special (global) variables is to enclose
them in stars:
(defparameter *itx2* '( ...))
(defun citx2 (status income)
(find (list status income) *itx2* :test #'test-itx2))
What more concise than a one-liner do you want? Try APL!
--
__Pascal Bourguignon__ http://www.informatimago.com/
You never feed me.
Perhaps I'll sleep on your face.
That will sure show you.
·········@yahoo.com writes:
> I am trying to write a function citx2 that returns a list based upon
> status and income.
> For example (citx2 single 150000) should return (single 146750 319100
> 0.33 12710.50)
>
> The code below does not appear to work.
It would have helped if you told us how it was failing. ;)
> (defun citx2 (status income)
> (let ((lst (list status income))
> (find lst itx2 :test test-itx2))))
Kalle pointed out the bug in this function.
> (defun test-itx2 (lst el)
> (and (eql (car lst) (car el))
> (<= (second el) (second lst))
> (< (second lst) (third el))))
Hmmm. A tax application.
One thing that would help would be to introduce a bit more structure or
organization to your data. You can do this in a number of ways. One
would be to use DEFSTRUCT to build your data structures. That would
involve a little bit more work up-front.
Another would be to keep the list structure as your basic data
structure , but to define accessor macros. For example:
(defmacro filing-status (list) `(first ,list))
(defmacro income (list) `(second ,list))
(defmacro income-lower-bound (list) `(second ,list))
(defmacro income-upper-bound (list) `(third ,list))
This allows you to do things like write your test function in a more
self-documenting way:
(defun matching-tax-data (candidate table-entry)
(and (eql (filing-status candidate) (filing-status table-entry))
(<= (income-lower-bound table-entry) (income candidate))
(< (income candidate) (income-upper-bound table-entry))))
> (defparameter itx2 '(
> (single 100000 146750 0.28 5373.00)
> (single 146750 319100 0.33 12710.50)
> (single 319100 10000000 0.35 19092.50)
> (married-filing-jointly 100000 117250 0.25 6525.00)
> (married-filing-jointly 117250 178650 0.28 10042.50)
> (married-filing-jointly 178650 319100 0.33 18975.00)
> (married-filing-jointly 319100 10000000 0.35 25357.00)
> (married-filing-separately 100000 159550 0.33 9487.50)
> (married-filing-separately 159550 10000000 0.35 12678.50)
> (head-of-household 100000 100500 0.25 4400.00)
> (head-of-household 100500 162700 0.28 7415.00)
> (head-of-household 162700 319000 0.33 15500.00)
> (head-of-household 319000 10000000 0.35 21932.00)))
Another way of organizing this would involve using a two-level data
structure. You could do this with association lists (alists). Note
that by convention, Lisp programmers delimit global variables with "*"
characters.
(defparameter *tax-tables*
'((single
(100000 146750 0.28 5373.00)
(146750 319100 0.33 12710.50)
(319100 10000000 0.35 19092.50))
(married-filing-jointly
(100000 117250 0.25 6525.00)
(117250 178650 0.28 10042.50)
(178650 319100 0.33 18975.00)
(319100 10000000 0.35 25357.00))
(married-filing-separately
(100000 159550 0.33 9487.50)
(159550 10000000 0.35 12678.50))
(head-of-household
(100000 100500 0.25 4400.00)
(100500 162700 0.28 7415.00)
(162700 319000 0.33 15500.00)
(319000 10000000 0.35 21932.00))))
Then you can rewrite your search functions like:
(defmacro income-lower-bound (table-line)
`(first ,table-line))
(defmacro income-upper-bound (table-line)
`(second ,table-line))
(defmacro marginal-tax-rate (table-line)
`(third ,table-line))
(defmacro base-tax (table-line)
`(fourth ,table-line))
(defun citx2 (status income)
(find income (rest (assoc status *tax-tables*))
:test #'income-in-range))
(defun income-in-range (income table-line)
(<= (income-lower-bound table-line) income)
(< income (income-upper-bound table-line)))
NB. You might want to include the lower levels of the table as well :)
Then you could easily write
(defun compute-tax (status income)
(let ((table-line (citx2 status income)))
(+ (base-tax table-line)
(* (marginal-tax-rate table-line)
(- income (income-lower-bound table-line))))))
--
Thomas A. Russ, USC/Information Sciences Institute
>>>>> "TAR" == Thomas A Russ <···@sevak.isi.edu> writes:
[...]
TAR> One thing that would help would be to introduce a bit more
TAR> structure or organization to your data. You can do this in a
TAR> number of ways. One would be to use DEFSTRUCT to build your
TAR> data structures. That would involve a little bit more work
TAR> up-front.
Yes. But
TAR> Another would be to keep the list structure as your basic
TAR> data structure , but to define accessor macros. For example:
TAR> (defmacro filing-status (list) `(first ,list)) (defmacro
TAR> income (list) `(second ,list))
TAR> (defmacro income-lower-bound (list) `(second ,list))
TAR> (defmacro income-upper-bound (list) `(third ,list)) [...]
A similar thing can be accomplished by defstruct:
CL-USER> (defstruct (tax-datum (:type list) (:conc-name nil))
filing-status income-lower-bound income-upper-bound
other-entry)
TAX-DATUM
CL-USER> (filing-status
'(married-filing-separately 159550 10000000 0.35 12678.50))
MARRIED-FILING-SEPARATELY
CL-USER> (income-lower-bound
'(married-filing-separately 159550 10000000 0.35 12678.50))
159550
cheers,
BM