Οι νόμοι του De Morgan#

Δύο ταυτότητες, που οφείλονται στον Augustus De Morgan, γραμμένες εδώ και πάνω από έναν αιώνα σε κάθε εγχειρίδιο τυπικής λογικής, και το πιο χρήσιμο μεμονωμένο ζεύγος εξισώσεων που μπορεί να γνωρίζει ένας ενεργός προγραμματιστής.

¬(a ∧ b) ≡ ¬a ∨ ¬b

¬(a ∨ b) ≡ ¬a ∧ ¬b

Με λόγια: το πέρασμα ενός not μέσα από μια έκφραση σε παρενθέσεις αντιστρέφει κάθε τελεστή στο εσωτερικό (το γίνεται , το γίνεται ) και αρνείται κάθε τελεστέο. Ισοδύναμα: η εξαγωγή ενός not προς τα έξω αντιστρέφει κάθε τελεστή και αφαιρεί τις αρνήσεις από τους τελεστέους.

Γιατί έχει σημασία στην Perl#

Το unless είναι η κανονική περίπτωση. Το unless (X) είναι if (!X) — και τη στιγμή που το X είναι το ίδιο σύνθετο, έχετε ένα προπορευόμενο not πάνω από μια έκφραση σε παρενθέσεις: ακριβώς το σκηνικό του De Morgan.

unless ($a == $x && $b == $y) { ... }

Η συνθήκη κάτω από το unless είναι !($a == $x && $b == $y). Εφαρμόστε De Morgan: αυτό είναι !($a == $x) || !($b == $y). Κάθε != είναι η άρνηση του αντίστοιχου ==, επομένως η καθαρή μορφή είναι:

if ($a != $x || $b != $y) { ... }

Συνέβησαν τρεις μετασχηματισμοί, καθένας μηχανικός:

  1. Το unless αντιστράφηκε σε if (το εξωτερικό not).

  2. Το && αντιστράφηκε σε || (De Morgan, στο εσωτερικό).

  3. Το == αντιστράφηκε σε != σε κάθε τελεστέο (οι αρνήσεις που τοποθέτησε ο De Morgan στους τελεστέους ακυρώνονται στην αντίθετη σύγκριση).

Αυτό δεν είναι στιλιστική προτίμηση. Κάθε προγραμματιστής που έχει αποσφαλματώσει μια λανθασμένη συνθήκη έχει κοιτάξει επίμονα ένα unless (! ... && ...) για ένα λεπτό παραπάνω· το επίπεδο if διαβάζεται σε ένα πέρασμα.

Ένα επεξεργασμένο παράδειγμα#

Μια πραγματική μορφή, ελαφρώς μπερδεμένη:

unless ($x !~ /^\d+$/ && $y !~ /^[a-z]+$/) {
    print "at least one of \$x and \$y looks valid\n";
}

Αυτό λέει: εκτός αν το $x είναι μη αριθμητικό και το $y είναι μη αλφαβητικό, τύπωσε. Πέντε αρνήσεις στοιβαγμένες σε δύο γραμμές. Διατρέξτε το:

Η συνθήκη κάτω από το unless είναι

!( $x !~ /^\d+$/  &&  $y !~ /^[a-z]+$/ )

Ο De Morgan αντιστρέφει το && σε || και αρνείται κάθε πλευρά:

!($x !~ /^\d+$/)  ||  !($y !~ /^[a-z]+$/)

Το !($x !~ ...) είναι απλώς $x =~ ... (διπλή άρνηση μιας αντιστοιχίας regex). Το ίδιο για τη δεξιά πλευρά:

$x =~ /^\d+$/  ||  $y =~ /^[a-z]+$/

Έτσι η καθαρισμένη εκδοχή είναι:

if ($x =~ /^\d+$/ || $y =~ /^[a-z]+$/) {
    print "at least one of \$x and \$y looks valid\n";
}

Πέντε αρνήσεις κατέβηκαν στο μηδέν. Η συνθήκη τώρα διαβάζεται ακριβώς όπως το έλεγε το σχόλιο: αν το $x είναι αριθμητικό ή το $y είναι αλφαβητικό.

Το ίδιο τέχνασμα για το OR#

Η δεύτερη μορφή του νόμου είναι η συμμετρική περίπτωση:

unless ($a == 0 || $b == 0) { divide($a, $b) }

γίνεται

if ($a != 0 && $b != 0) { divide($a, $b) }

— ίδια μηχανικά βήματα, μόνο που αυτή τη φορά ο εσωτερικός τελεστής είναι το || που αντιστρέφεται σε &&.

NAND, NOR — η ίδια ταυτότητα, διαβασμένη από την άλλη πλευρά#

Η γραμμή NAND στον πίνακα αληθείας από το προηγούμενο κεφάλαιο έχει δύο καταχωρίσεις στη στήλη «Perl ιδιωματικά»:

14  NAND   !($a && $b)   /   !$a || !$b

Αυτές είναι ίσες λόγω του πρώτου νόμου του De Morgan. Το NAND είναι ακριβώς η άρνηση του AND, και το πέρασμα της άρνησης προς τα μέσα αντιστρέφει τον τελεστή και αρνείται τους τελεστέους. Η γραμμή NOR λειτουργεί με τον ίδιο τρόπο για τον δεύτερο νόμο:

8   NOR    !($a || $b)   /   !$a && !$b

Έτσι ο De Morgan δεν είναι ξεχωριστό γεγονός που πρέπει να θυμάστε δίπλα στα NAND και NOR — είναι η ταυτότητα που μετατρέπει τη μία γραφή στην άλλη.

Μηχανική, σε μία παράγραφο#

Η άρνηση κατανέμεται πάνω στα και αντιστρέφοντας τον σύνδεσμο και αρνούμενη κάθε τελεστέο. Η άρνηση επίσης αυτο-ακυρώνεται: ¬¬x x. Οι δύο κανόνες μαζί σας επιτρέπουν να πιέσετε ένα not αυθαίρετα βαθιά, ή να το εξάγετε αυθαίρετα προς τα έξω, από οποιαδήποτε έκφραση δομημένη από , , και ¬. Αυτό είναι όλο το περιεχόμενο των νόμων του De Morgan — κάθε «περίπλοκο unless» που ξαναγράφετε είναι μία εφαρμογή αυτών των δύο κανόνων συν την ακύρωση διπλής άρνησης.

Πρακτική διαδικασία#

Όταν κοιτάζετε επίμονα μια συνθήκη που δεν σας αρέσει:

  1. Αν η εξωτερική δεσμευμένη λέξη είναι unless, προσθέστε ένα not νοητικά μπροστά και κάντε το if.

  2. Αν το προπορευόμενο not βρίσκεται πάνω από ένα && ή || σε παρενθέσεις, κατανείμετέ το: αντιστρέψτε τον σύνδεσμο, αρνηθείτε κάθε τελεστέο.

  3. Αν μια υποέκφραση είναι !(x == y), αντικαταστήστε με x != y. Το ίδιο για !~=~, !defineddefined, κ.λπ.

  4. Σταματήστε όταν δεν υπάρχουν άλλες προπορευόμενες αρνήσεις να πιέσετε ή άλλες διπλές αρνήσεις να ακυρώσετε.

Στην πράξη θα κάνετε και τα τέσσερα βήματα στο μυαλό σας σε ένα πέρασμα. Το νόημα της διαδικασίας είναι ότι υπάρχει μία — δεν αναζητάτε τη σωστή μορφή, την παράγετε.

Τι πρέπει να θυμάστε από αυτό το κεφάλαιο#

  • Δύο ταυτότητες: το πέρασμα του ¬ μέσα από το αντιστρέφει σε (και αντίστροφα) και αρνείται κάθε τελεστέο.

  • Τα περισσότερα «άσχημα unless» απέχουν μία μηχανική αντιστροφή De Morgan από ένα καθαρό if.

  • Τα NAND και NOR δεν είναι επιπλέον γεγονότα· είναι οι νόμοι του De Morgan διαβασμένοι ως ζεύγος ισοδύναμων γραφών.

Δείτε επίσης#

  • perlop — Λογικοί — ο σύντροφος αναφοράς τελεστών. Οι μετασχηματισμοί De Morgan εδώ ισχύουν σε κάθε έκφραση Perl που χρησιμοποιεί &&, ||, !, και τη δεσμευμένη λέξη unless.