[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.lisp

Need help creating a doctoring.

Jim Newton

1/13/2016 9:07:00 AM

Can someone please suggest a correct and succinct docstring for the following macro.
Every time I write one it is too long and complicated and confusing.

It is somewhat similar to COND except that every test that is true results in the
corresponding expressions to be evaluated UNTIL some expression evaluates to something
"different" than OBJECT. "different" means not EQUAL.

E.g.,
....
(rule-case 12
((oddp x) (foo) (* 2 x))
((primep x) (bar) (* 3 x))
((frobp (* x x)) (foo) (bar)))

This executes as follows.
If (oddp x) is false, skip this clause.
If (oddp x) is true, evaluate (progn (foo) (* 2 x)) it that evaluates to (EQUAL to) 12, skip the rest of
the clauses.
If it does evaluate to 12, then look at the next clause.
If (primep x) is false, skip to the next clause.
If (primep x) is true, then evaluate (progn (bar) (* 3 x)),
If that evaluates to 12, then skip the rest.
If it evaluates to something different than 12, then go to the next clause.
If (frobp (* x x)) is true, then evaluate (progn (foo) (bar)) .

The return value is 12 (the OBJECT) if no test returned true.
If some test(s) return true, the return value is the last value of the corresponding body.


Why the weird semantics?
I use this as a sequence of transforms. a transform might transform object (not destructively but by
returning a newly calculated object). If the new object is still EQUAL to the old object, then continue
to the next transform. However, only apply the transforms if the test is true.
Once some transform calculate a new object (one that is not EQUAL to OBJECT), then
we're finished, that new value is returned.

The reason I use EQUAL rather than EQL is becaues my OBJECT is normally a list corresponding
to lisp code, and I want to detect when some transform as successfully transformed the code to
something different.


(defmacro rule-case (object &body clauses)
(let ((new (gensym "new"))
(old (gensym "old")))
(labels ((expand-clause (clause)
(destructuring-bind (test &body body) clause
(assert body () "invalid test/body used in RULE-CASE: ~A" clause)
`(when (and (equal ,new ,old)
,test)
(setf ,new (progn ,@body)))))
(expand-clauses ()
(mapcar #'expand-clause clauses)))
`(let* ((,new ,object)
(,old ,new))
,@(expand-clauses)
,new))))
3 Answers

Kenneth Tilton

1/13/2016 10:11:00 AM

0

On Wednesday, January 13, 2016 at 4:07:02 AM UTC-5, Jim Newton wrote:
> Can someone please suggest a correct and succinct docstring for the following macro.

Let's figure out the doc* before we work on the docstring.

* We may have to figure out what you want this function to do before we can figure out the doc. More below.

> Every time I write one it is too long and complicated and confusing.

I think I see why: what you wrote below is contradictory.

>
> It is somewhat similar to COND except that every test that is true results in the
> corresponding expressions to be evaluated UNTIL some expression evaluates to something
> "different" than OBJECT. "different" means not EQUAL.
>
> E.g.,
> ...
> (rule-case 12
> ((oddp x) (foo) (* 2 x))
> ((primep x) (bar) (* 3 x))
> ((frobp (* x x)) (foo) (bar)))
>
> This executes as follows.
> If (oddp x) is false, skip this clause.

So far so good. Here comes the trouble:

> If (oddp x) is true, evaluate (progn (foo) (* 2 x)) it that evaluates to (EQUAL to) 12, skip the rest of
> the clauses.
> If it does evaluate to 12, then look at the next clause.

(a) I think you meant "if that evaluates". Not being a jerk: I also think you meant "if that does *not* evaluate...". Just guessing the single malt was cutting in as you wrote this. If I am wrong about the single malt...
(b) You just specified the behavior for "evaluates to OBJECT" twice and incompatibly.

But based on the preamble "UNTIL some expression evaluates to something different than OBJECT", I am back to my single-malt theory and think you meant: "if that does not evaluate to OBJECT skip the rest of the clauses *and return the value*". Which we see later:

"Once some transform calculate a new object (one that is not EQUAL to OBJECT), then we're finished, that new value is returned."

But we also see:

"If it evaluates to something different than 12, then go to the next clause.."

Nope, you just said "we're finished, that new value is returned.

Hmmm. OK, there is a second place you state that when we get a new value we're done, so now my guess is that the single-malt explains the example you gave and that the docstring should be "return the first clause-value not equal OBJECT of the first clause whose test is true, or return OBJECT."

-hk

> If (primep x) is false, skip to the next clause.
> If (primep x) is true, then evaluate (progn (bar) (* 3 x)),
> If that evaluates to 12, then skip the rest.
> If it evaluates to something different than 12, then go to the next clause.
> If (frobp (* x x)) is true, then evaluate (progn (foo) (bar)) .
>
> The return value is 12 (the OBJECT) if no test returned true.
> If some test(s) return true, the return value is the last value of the corresponding body.
>
>
> Why the weird semantics?
> I use this as a sequence of transforms. a transform might transform object (not destructively but by
> returning a newly calculated object). If the new object is still EQUAL to the old object, then continue
> to the next transform. However, only apply the transforms if the test is true.
> Once some transform calculate a new object (one that is not EQUAL to OBJECT), then
> we're finished, that new value is returned.
>
> The reason I use EQUAL rather than EQL is becaues my OBJECT is normally a list corresponding
> to lisp code, and I want to detect when some transform as successfully transformed the code to
> something different.
>
>
> (defmacro rule-case (object &body clauses)
> (let ((new (gensym "new"))
> (old (gensym "old")))
> (labels ((expand-clause (clause)
> (destructuring-bind (test &body body) clause
> (assert body () "invalid test/body used in RULE-CASE: ~A" clause)
> `(when (and (equal ,new ,old)
> ,test)
> (setf ,new (progn ,@body)))))
> (expand-clauses ()
> (mapcar #'expand-clause clauses)))
> `(let* ((,new ,object)
> (,old ,new))
> ,@(expand-clauses)
> ,new))))

Jim Newton

1/13/2016 11:03:00 AM

0

Hi Kenny, thanks for giving a shot.

Sorry for the contradiction. Yes, there indeed is one.

What I want is that the clauses evaluate until one transforms OBJECT to something different.
at that point we are finished because the transform was successful.

But don't do any clause where the test fails.

Jim Newton

1/13/2016 11:28:00 AM

0

Indeed each clause is a perspecitive transformation. the evaluation proceeds as long as the transformations are idempotent, and stops once a non-idempotent transformation is encountered.