3

I am learning about racket/scheme and came across an online resource that said, if a function written using a cond gives true or false, it can be rewritten using only not, and, and or. I have worked out some simple examples where I was able to to convert cond statements into a statement only involving not and and or. My question is if there is a way that the logic can be "seen" right away when converting between these two types of statements. I understand that it is not always practical to convert every cond statement into a combination of not's and's and or's but I am interested in learning about the logic behind the process of converting. Thanks in advance.

(If something about the question does not make sense, leave a comment and I will try clarifying what I want to understand)

3 Answers3

3

All conditional expressions (and not only those evaluating to true/false) can be rewritten using only boolean combinators. This is because of how logical operators are evaluated in Scheme/Racket. For instance, logically (and a b) would be true if both a and b are true, and otherwise false. But in Racket, the result of (and a b) is b if both a and b are truthy, and otherwise false. That is, evaluation proceeds to the right until either the last argument or a falsy value is encountered. At that point, evaluation stops and that value (which could be a boolean but needn't be) is returned. It's because and and or don't simply produce boolean output that they can be used to stand in for conditional expressions.

E.g.

(if #t 'hello 'bye) ;=> hello
(or (and #t 'hello) 'bye) ;=> hello
(if #f 'hello 'bye) ;=> bye
(or (and #f 'hello) 'bye) ;=> bye
(cond [#f 'hello]
      [#f 'bye]
      [#t 'aloha]) ;=> aloha
(or (and #f 'hello)
    (and #f 'bye)
    (and #t 'aloha)) ;=> aloha

But you wouldn't usually want to use them that way since they're hard to read. As a general guideline, use if and cond in most cases, rather than elementary boolean operators. If you only care about taking action on a positive or negative result of the conditional, then you could use when or unless. If you do care about handling both positive and negative results, but one of them is a boolean result such as this example:

(if (positive? n)
  #t
  (even? n))

... then this would be a case where a boolean operator would be preferable, like so:

(or (positive? n) (even? n))

If both arms of the if conditional are boolean values, like this:

(if (> n 3)
  #t
  #f)

... then just replace the entire conditional expression with the condition itself:

(> n 3)

Otherwise, stick to if and cond.

mindthief
  • 12,755
  • 14
  • 57
  • 61
  • You can't write, e.g., `(if some-condition #f #t)` as `(or (and some-condition #f) #t)`. Of course, a conditional is the wrong way to express negation, but OP seems to be asking about `cond` expressions that evaluate to a boolean value. – ad absurdum Sep 28 '20 at 00:34
  • That's a good point, `#f` values in the consequent position would warrant special handling. For this particular example, following the guideline in the answer it would be written `(not some-condition)` instead, but you're right that this doesn't address the general case of falsy consequent expressions. – mindthief Sep 28 '20 at 03:05
  • 1
    Thank you very much. I appreciate all the examples. – user14346904 Sep 28 '20 at 06:09
  • 1
    re false consequent, indeed `(or (and #t #f) 'bye) ;=> bye` but `(or (and #t #f) (and (not #t) 'bye)) ;=> #f`, correctly. probably also need to introduce a temp var via `let`, in general. – Will Ness Nov 11 '20 at 12:24
2

Once you convert a cond to nested ifs, you can always turn it into and or and not like this:

(if A B C) --> (or (and A B) (and (not A) C))

However, if you do this blindly, you will get a much more complicated expression than what you could get, so I would add a couple more transformations you can use:

(if A B #f) --> (and A B)
(if A B #t) --> (or (not A) B)
(if A #f C) --> (and (not A) C)
(if A #t C) --> (or A C)

(note: that or above might return a different truthy-value other than #t, making it technically different but equivalent-when-used-as-a-boolean)

Another thing I should note is that sometimes you can transform a multi-branch cond into and or not without transforming into ifs first. For example a 3-branch cond:

(cond [A B]
      [C D]
      [else E])
-->
(or (and A B)
    (and (not A) C D)
    (and (not A) (not C) E))

Or a 4-branch cond:

(cond [A B]
      [C D]
      [E F]
      [else G])
-->
(or (and A B)
    (and (not A) C D)
    (and (not A) (not C) E F)
    (and (not A) (not C) (not E) G))

Each and corresponds to a cond-branch, and each cond-branch's and has nots in it for every previous condition, in addition to its own condition.

A more generic rule you can apply:

for i from 1 through n,
(cond [Q_i A_i]
      ...
      [else E])
-->
on each i, for j from 1 through i-1,
(or (and (not Q_j) ... Q_i A_i)
    ...
    (and (not Q_i) ... E)
Alex Knauth
  • 8,133
  • 2
  • 16
  • 31
1

First of all, you need to desugar cond language into a sequence of if-then-else sequences, which is trivial.

After that, you can rewrite if conditionals into boolean operators. You can look into a manual of propositional logic to learn this. Or look here.

Btw. It is forbidden to paste your homework on stack overflow.

alinsoar
  • 15,386
  • 4
  • 57
  • 74
  • Thank you very much. Yup I'll make sure not to ask homework questions on stack overflow. – user14346904 Sep 28 '20 at 05:58
  • @user14346904 You can ask questions, but you need to show what did you try, not only the question itself. Read https://stackoverflow.com/help/how-to-ask. – alinsoar Sep 28 '20 at 07:49