4

I am in an Artificial Intelligence course and we were given a program to write. The program is apparently simple, and all other students did it in java. However I know that it can be done in LISP with less work. Well. Less typing. But I've been reading about LISP for a week now, and I am amazed by it. I am determined to learn more, and use LISP for a lot more than just this class. I'm 23 and am learning a language formed in 1958. It's kind of romantic. I am having a lot of fun avoiding my mousepad like the plague.

The example he gives tells the entire program. He notes that he uses recursion, and not prog. I understand what that means, at least.

(rewrite '(or a (and b (not (or c d)))))

--> (OR A (AND B (AND (NOT C) (NOT D))))

(rewrite '(and a (or b (not (and c (and d e))))))

--> (AND A (OR B (NOT C) (OR (NOT D) (NOT E)))))

I understand De Morgan's laws. I just don't get how I'm supposed to handle this! What I have so far is... embarrassing. My notebook is filled with pages of me trying to draw this out. I will give you my closest attempt at the simplest case which is:

(not (or a b))

I figure if I can handle this, I may be just fine to handle the rest. Maybe. I made a function called boom, and that above statement is what I call a boomable list.

(defun boom (sexp)

  (let ((op (car (car (cdr sexp)))) 

    (operands (cdr (car (cdr sexp))))))

  (if (equal op 'and)

      (setcar sexp 'or)

    (setcar sexp 'and))

  (print operands)

  (print sexp))

                ;end boom

I print at the end for debugging. Changes to the list operands does not reflect changes in original sexp (huge let down for me).

Tell me what I have is bogus, and guide me.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
kaleoh
  • 92
  • 1
  • 9
  • You say "DeMorgan-ify"; is the point just to distribute "not" over an internal "or" or "and"? – Joshua Taylor Feb 09 '16 at 15:45
  • Based on his output, that's what it seems like. The "and" and "or" could just easily be "foo" and "bar" for example. The logic doesn't seem to play any sort of role. – kaleoh Feb 09 '16 at 15:50
  • Dealing with artificial intelligence makes me think about the use of predicates, I mean your program should first learn some knowledges (ie how to interpret different operators according to DeMorgan laws) and then it could evaluate your entire expression by using its knowledges basis (ie chaining the predicates on the fly) - see example of predicates below – floppy12 Feb 09 '16 at 15:56
  • @floppy12 I agree. I am more interested in learning the AI solution to this issue rather than editing strings (like my Java colleagues) and editing lists (like me). Are you recommending I first teach my program what and and or mean? "Learn some knowledges" confuses me on implementation, but I do understand the point you are making. – kaleoh Feb 09 '16 at 15:58

4 Answers4

5

An Emacs Lisp solution using pattern matching, based on Rainer Joswigs Common Lisp solution:

(defun de-morgan (exp)
  (pcase exp
    ((pred atom) exp)
    (`(not (and ,a ,b)) `(or ,(de-morgan `(not ,a))
                             ,(de-morgan `(not ,b))))
    (`(not (or ,a ,b)) `(and ,(de-morgan `(not ,a))
                             ,(de-morgan `(not ,b))))
    (x (cons (car x) (mapcar #'de-morgan (rest x))))))

(de-morgan '(not (or 1 2))) ; => (and (not 1) (not 2))
(de-morgan '(not (and 1 2))) ; => (or (not 1) (not 2))
(de-morgan '(or a (and b (not (or c d))))) ; => (or a (and b (and (not c) (not d))))
Community
  • 1
  • 1
jkiiski
  • 8,206
  • 2
  • 28
  • 44
  • I understand what the backquote and comma combination do. I am trying to understand, anyway. Let me ask if I have this correct: '(not (or ,a ,b)) '(and (not ,(demorgan a) (not ,(demorgan b) Basically you are saying, if it matches (not (or ... )), you are running a and b both through demorgan function. Your last line has me bewildered, is there anyway you can explain it? What exactly is x bound to? – kaleoh Feb 09 '16 at 18:58
  • 1
    @kaleoh On the last line, `x` is bound to the expression being matched (same as `exp`) . It's basically the "otherwise" clause that will match anything if the other patterns fail. – jkiiski Feb 09 '16 at 19:10
  • If it matches a single atom, then that atom is kept as it is - good. If it matches a '(not (and/or ,a ,b)) , then it replaces with '(or/and (not ...)), and runs a and b both through the function, brilliant. And then if it does not have a 'not it will then push the rest through the function again, leaving what it just checked as is. Brilliant! This was exactly my intention, and now I know the tool that I wish I knew a week ago. – kaleoh Feb 09 '16 at 19:16
  • 1
    The middle lines indeed check if the expression matches `(not (and ))`, and will construct a return list in the form `(or (not ...) (not ...))`. Since `a` and `b` may contain sublists, they need to be passed through `de-morgan`. – jkiiski Feb 09 '16 at 19:16
  • 1
    I moved the call to `de-morgan` around the whole `(not ...)`. Otherwise it wouldn't handle a situation like `(not (and a (and b c)))` properly. – jkiiski Feb 10 '16 at 05:57
2

Common Lisp, without simplification:

(defun de-morgan (exp)
  (cond ;; atom
        ((atom exp) exp)
        ;; (not (and p q))  or  (not (or p q))
        ((and (consp exp)
              (equal (car exp) 'not)
              (consp (cadr exp))
              (or (equal (caadr exp) 'and)
                  (equal (caadr exp) 'or)))
         (list (case (caadr exp)
                 (and 'or)
                 (or 'and))
               (de-morgan (list 'not (car  (cdadr exp))))
               (de-morgan (list 'not (cadr (cdadr exp))))))
        ;; otherwise some other expression
        (t (cons (car exp) (mapcar #'de-morgan (rest exp))))))
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
  • 1
    I think this would be a bit cleaner with pattern matching (via optima). I think Emacs Lisp also has some kind of pattern matching with `pcase`, but I haven't used that enough to know if it would work for this. – jkiiski Feb 09 '16 at 16:45
  • @jkiiski: yep, a pattern matching library would help a lot. A would not write code without it, other than for tiny exercises. – Rainer Joswig Feb 09 '16 at 17:45
1

This two functions should distribute the not into parentheses:

(defun de-morgan (formula)
  (if (listp formula)
      (let ((op (first formula)))
        (case op
          (and `(and ,(de-morgan (second formula)) ,(de-morgan (third formula))))
          (or `(or ,(de-morgan (second formula)) ,(de-morgan (third formula))))
          (not (de-morgan-negate (second formula)))))
    formula))

(defun de-morgan-negate (formula)
  (if (listp formula)
      (let ((op (first formula)))
        (case op
          (and `(or ,(de-morgan-negate (second formula)) ,(de-morgan-negate (third formula))))
          (or `(and ,(de-morgan-negate (second formula)) ,(de-morgan-negate (third formula))))
          (not (de-morgan (second formula)))))
    `(not ,formula)))



(de-morgan 'a)
(de-morgan '(not a))
(de-morgan '(not (not a)))
(de-morgan '(and a b))
(de-morgan '(not (and a b)))
(de-morgan '(not (or a b)))
(de-morgan '(not (and (and (not a) b) (not (or (not c) (not (not d)))))))
choroba
  • 231,213
  • 25
  • 204
  • 289
  • 1
    You shouldn't be quoting the arguments in **case**. `(case 'quote ('and 'match) (t no-match))` returns **match** because `'and` is shorthand for `(quote and)`, and **case** expects a designator for a list of keyforms. You're providing a list of two symbols: **quote** and **and**. – Joshua Taylor Feb 09 '16 at 16:18
  • @JoshuaTaylor: Thanks, that shows how rarely I lisp these days. – choroba Feb 09 '16 at 16:20
0

It's not too hard to write a quick and dirty version of this. You just need to check whether your formula is a raw propositional variable (in this case, an atom), a binary connective, or a negation. If it's a negation, then you need to process the inside.

(defun demorganify (formula)
  (if (atom formula)
      formula
    (let ((operator (first formula)))
      (case operator
        ((and or)
         (list* operator (mapcar 'demorganify (rest formula))))
        ((not)
         (let ((subformula (second formula)))
           (if (atom subformula)
               formula
             (let* ((suboperator (first subformula))
                    (new-suboperator (case suboperator
                                       ((not) 'not)
                                       ((and) 'or)
                                       ((or) 'and)))
                    (demorganify-and-negate (lambda (f)
                                              (demorganify (list 'not (demorganify f))))))
               (list* new-suboperator (mapcar demorganify-and-negate (rest subformula)))))))))))

This could certainly be made a bit cleaner, though.

Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353