Help with logic

Started by rickyboy, November 26, 2006, 08:09:19 PM

Previous topic - Next topic

rickyboy

I've never noticed this before -- look at these two calls to 'or':
> (or nil 42)
42
> (or nil '())
nil


Oh no, I can't have this -- it screws up my code.  I need the second 'or' call to return a '(), not nil.  Although, '() and nil are both boolean false, they are not equivalent in any other sense (cf. section 8 of the manual).



Lutz, can you change the behavior of the 'or' (and the 'and' since it behaves similarly) or show me what I am doing wrong?



Thanks!  --Rick
(λx. x x) (λx. x x)

newdep

#1
Do you want to check for an empty list or return explicit an empty list?
-- (define? (Cornflakes))

nikus80

#2
If anything, you could write a macro to replace that or.



something like



(define-macro (nor)

(catch

(dolist (thisarg (args))

 (let (evalarg (eval thisarg))

 (if (or (isEmptyList? evalarg) evalarg)

   (throw evalarg))))....





or like



(define-macro (lor , evalarg)

(catch

(dolist (thisarg (args))

 (set 'evalarg (eval thisarg))

 (if evalarg

   (throw evalarg)))

  evalarg))....





I'm writing it directly, not very indented, but this is the idea. The first treats the empty list as true in the or. The second, if all elements fails, returns the last element which failed. oh and "isEmptyList?" is not the actual predicate (I don't know which it is)

Lutz

#3
From the manual: 'nil' and the empty list '() are always treated as logical false in a boolean context of 'or', 'and', 'while', 'if', 'unless' and 'cond'.



But there is a solution to your situation. There are the predicates  'nil?' and 'true?' which test for strictly 'nil' and 'not nil'. Mapping 'true?' over your list and then applying 'or' will give you the result intended.


(apply or (map true? '(nil ()))) => true

(apply and (map true? '(nil ()))) => nil


The fact that '() is treated as logical false with 'or' and 'and' is useful in logic programming:


(set 'foo (or aList '(a b c d)))

here foo gets set to '(a b c d) if aList is empty, this idiom is used frequently when initializing variables.



Lutz

rickyboy

#4
Sorry, but I did not make myself clear.  Mapping 'true?' over the list will *not* help, since I am trying to do something akin to Lutz's example:


(set 'foo (or aList '(a b c d)))

However, by analogy, my usage would look like:


(set 'foo (or (returns-a-list-or-nil) '()))

so that 'foo' would be guaranteed to be a list (even if the empty list).



Now, I could go the macro workaround which I did (lest I stop hacking newlisp -- perish the thought :-).  I actually went the route of nikus80:


(define-macro (myor)
  (catch
    (dolist (a (args))
     (let (v (eval a))
      (if v (throw v))))))

> (myor nil 42)
42
> (myor nil '())
()


And this is exactly what I want.



However, I'd like to appeal to Lutz to change the behavior of the intrinsic 'or' to that of 'myor' defined above.  That is, 'or' should be defined as



(or e1 e2 ... eN)



returns the first true value encountered in the list (e1 e2 ... eN-1), as each element is evaluated in turn, from left to right.  If none evaluates to a true value, return the value of eN.



Another way to think of it is that in
> (or nil '())
nil

we have lost a piece of information we could potentially use, namely the '().  The better implementation of 'or' is one which preserves this information.  For this reason '(or nil 42)' does not currently return 'true' -- it returns 42, as it should.
> (or nil 42)
42


Now, if 'or' is implemented in the manner described, it will not change its boolean interpretation[*], but will gain the added benefit of using any value's normal interpretation in subsequent (or surrounding) non-boolean expressions.  (The implementation of 'and' should also be similarly re-aligned.)  Of course, you could tell me to sod off and I'd still have my macro but I think this software change request will improve the newlisp core in a positive way.



Thanks!

--Rick



[*] -- Note that any value seems to have two interpretations, a normal one and a boolean one.  For instance, when evaluated, the expression '42' has a normal interpretation of the integer 42 and a boolean interpretation of true, A "true value" is any value that has a boolean interpretation of true, i.e. 'e' is a "true value" if the expression '(if e 1 0)' evaluates to 1.  For instance, 42 is a "true value".  Additionally, 'true' is a true value.
(λx. x x) (λx. x x)

rickyboy

#5
By the way, when you start to look for "value conservation" in logical expressions (e.g. 'myor' above "value conserves" whereas 'or' does not), you can find it currently in 'if'.  Observe:
> (if '() 'UNREACHABLE)
()

It did not respond with 'nil'!  So, 'if' "value conserves" and thus does not share this property with 'or'.  That is why and how the implemention of 'myor' given above returns the last expression's value when it needs to.



--Ricky
(λx. x x) (λx. x x)

Lutz

#6
I think you are right that (or nil '()) should return (), but I am not sure if our reasoning for this is the same ;). Let me restate it in different words:



The reason that: (if '() 'unreachable) returns: () is that the condition is the last expression which was evaluated. Is this what you mean with it, conservation = conserving the last evluated result?



By that reasoning then (last evaluation) we would have:


(or nil '()) => ()
(or '() nil) => nil


So with 'or' the value returned will be the last in  the list or the first which is not 'nil'. With 'and' it would be the first not not 'true' value:


(and nil '()) => nil
(and '() nil) => ()


'if', 'unless' and all loop constructs already behave that way, always returning the last expression evaluated. I made the change for 'or' and 'and', look for it in version 9.0.5 next week. I believe (hope) it will not affect anybody's code. It does not change usage of 'or' in my previous post and is the right thing to do ... thanks for this discovery.



Lutz

rickyboy

#7
Yes, what you said, Lutz, is what I was feebly trying to say, except that you said it in a tenth of my verbiage.  :-)



Thanks for your kind consideration.  You have to be the most benevolent of "benevolent dictators" -- I'd take you over Linus or Guido any day!



--Rick
(λx. x x) (λx. x x)