# Οι νόμοι του 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. ```perl unless ($a == $x && $b == $y) { ... } ``` Η συνθήκη κάτω από το `unless` είναι `!($a == $x && $b == $y)`. Εφαρμόστε De Morgan: αυτό είναι `!($a == $x) || !($b == $y)`. Κάθε `!=` είναι η άρνηση του αντίστοιχου `==`, επομένως η καθαρή μορφή είναι: ```perl if ($a != $x || $b != $y) { ... } ``` Συνέβησαν τρεις μετασχηματισμοί, καθένας μηχανικός: 1. Το `unless` αντιστράφηκε σε `if` (το εξωτερικό `not`). 2. Το `&&` αντιστράφηκε σε `||` (De Morgan, στο εσωτερικό). 3. Το `==` αντιστράφηκε σε `!=` σε κάθε τελεστέο (οι αρνήσεις που τοποθέτησε ο De Morgan στους τελεστέους ακυρώνονται στην αντίθετη σύγκριση). Αυτό *δεν* είναι στιλιστική προτίμηση. Κάθε προγραμματιστής που έχει αποσφαλματώσει μια λανθασμένη συνθήκη έχει κοιτάξει επίμονα ένα `unless (! ... && ...)` για ένα λεπτό παραπάνω· το επίπεδο `if` διαβάζεται σε ένα πέρασμα. ## Ένα επεξεργασμένο παράδειγμα Μια πραγματική μορφή, ελαφρώς μπερδεμένη: ```perl unless ($x !~ /^\d+$/ && $y !~ /^[a-z]+$/) { print "at least one of \$x and \$y looks valid\n"; } ``` Αυτό λέει: *εκτός αν* το `$x` είναι μη αριθμητικό και το `$y` είναι μη αλφαβητικό, τύπωσε. Πέντε αρνήσεις στοιβαγμένες σε δύο γραμμές. Διατρέξτε το: Η συνθήκη κάτω από το `unless` είναι ```default !( $x !~ /^\d+$/ && $y !~ /^[a-z]+$/ ) ``` Ο De Morgan αντιστρέφει το `&&` σε `||` και αρνείται κάθε πλευρά: ```default !($x !~ /^\d+$/) || !($y !~ /^[a-z]+$/) ``` Το `!($x !~ ...)` είναι απλώς `$x =~ ...` (διπλή άρνηση μιας αντιστοιχίας regex). Το ίδιο για τη δεξιά πλευρά: ```default $x =~ /^\d+$/ || $y =~ /^[a-z]+$/ ``` Έτσι η καθαρισμένη εκδοχή είναι: ```perl if ($x =~ /^\d+$/ || $y =~ /^[a-z]+$/) { print "at least one of \$x and \$y looks valid\n"; } ``` Πέντε αρνήσεις κατέβηκαν στο μηδέν. Η συνθήκη τώρα διαβάζεται ακριβώς όπως το έλεγε το σχόλιο: *αν το `$x` είναι αριθμητικό ή το `$y` είναι αλφαβητικό*. ## Το ίδιο τέχνασμα για το OR Η δεύτερη μορφή του νόμου είναι η συμμετρική περίπτωση: ```perl unless ($a == 0 || $b == 0) { divide($a, $b) } ``` γίνεται ```perl if ($a != 0 && $b != 0) { divide($a, $b) } ``` — ίδια μηχανικά βήματα, μόνο που αυτή τη φορά ο εσωτερικός τελεστής είναι το `||` που αντιστρέφεται σε `&&`. ## NAND, NOR — η ίδια ταυτότητα, διαβασμένη από την άλλη πλευρά Η γραμμή NAND στον πίνακα αληθείας από το προηγούμενο κεφάλαιο έχει δύο καταχωρίσεις στη στήλη «Perl ιδιωματικά»: ```default 14 NAND !($a && $b) / !$a || !$b ``` Αυτές είναι ίσες *λόγω του πρώτου νόμου του De Morgan*. Το NAND είναι ακριβώς η άρνηση του AND, και το πέρασμα της άρνησης προς τα μέσα αντιστρέφει τον τελεστή και αρνείται τους τελεστέους. Η γραμμή NOR λειτουργεί με τον ίδιο τρόπο για τον δεύτερο νόμο: ```default 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`. Το ίδιο για `!~` ↔ `=~`, `!defined` ↔ `defined`, κ.λπ. 4. Σταματήστε όταν δεν υπάρχουν άλλες προπορευόμενες αρνήσεις να πιέσετε ή άλλες διπλές αρνήσεις να ακυρώσετε. Στην πράξη θα κάνετε και τα τέσσερα βήματα στο μυαλό σας σε ένα πέρασμα. Το νόημα της διαδικασίας είναι ότι υπάρχει μία — δεν αναζητάτε τη σωστή μορφή, την παράγετε. ## Τι πρέπει να θυμάστε από αυτό το κεφάλαιο - Δύο ταυτότητες: το πέρασμα του `¬` μέσα από `∧` το αντιστρέφει σε `∨` (και αντίστροφα) και αρνείται κάθε τελεστέο. - Τα περισσότερα «άσχημα `unless`» απέχουν μία μηχανική αντιστροφή De Morgan από ένα καθαρό `if`. - Τα NAND και NOR δεν είναι επιπλέον γεγονότα· είναι οι νόμοι του De Morgan διαβασμένοι ως ζεύγος ισοδύναμων γραφών. ## Δείτε επίσης - [perlop — Λογικοί](../../p5/core/perlop/logical.md) — ο σύντροφος αναφοράς τελεστών. Οι μετασχηματισμοί De Morgan εδώ ισχύουν σε κάθε έκφραση Perl που χρησιμοποιεί `&&`, `||`, `!`, και τη δεσμευμένη λέξη `unless`.