Ostatnio natknąłem się na problem, który wymagał ode mnie programowego zdefiniowania logicznego operatora „OR”, ale bez użycia samego operatora.
Wymyśliłem to:
OR(arg1, arg2)
if arg1 = True and arg2 = True
return True
else if arg1 = True and arg2 = False
return True
else if arg1 = False and arg2 = True
return True
else:
return False
Czy ta logika jest poprawna, czy coś przeoczyłem?
programming-logic
logicNoob
źródło
źródło
return arg1 ? arg1 : arg2;
or
operatora.Odpowiedzi:
Powiedziałbym, że to prawda, ale czy nie możesz tego skondensować do czegoś takiego?
Ponieważ robisz porównanie lub nie sądzę, że naprawdę musisz sprawdzać kombinację. Liczy się tylko to, czy jeden z nich jest prawdziwy, aby zwrócić prawdę. W przeciwnym razie chcemy zwrócić wartość false.
Jeśli szukasz krótszej wersji, która jest mniej szczegółowa, zadziała to również:
źródło
if arg2 == true
).or(a, b): a ? a : b
Oto rozwiązanie bez lub bez porównań i literałów boolowskich:
Prawdopodobnie nie ma to bardziej fundamentalnego znaczenia;)
źródło
true
lubfalse
.||
Operator JavaScript w pigułce (gdy jest implementowany w dynamicznie pisanym języku).Jedna linia kodu:
Bez rozgałęzień, bez OR.
W języku C byłoby to:
Jest to po prostu zastosowanie praw De Morgana :
(A || B) == !(!A && !B)
źródło
if/else
konstrukcja jest taka sama jak przy użyciu OR, tylko z inną nazwą.if
jest równoważne z równością. Zwykle w kodzie maszynowymif
zaimplementowana jest arytmetyka, po której następuje porównanie do zera ze skokiem.and
, zapewniając tym samym spójność między operatorami.if (a) return true; else if (b) return true;
wydaje się to mniej więcej równoważne moralnieif (a OR b) return true;
, ale pogląd ten może być otwarty na spory.Jeśli masz tylko
and
inot
, możesz użyć prawa DeMorgan do odwracaniaand
:... lub (jeszcze prościej)
...
A ponieważ najwyraźniej wszyscy jesteśmy skupieni na optymalizacji czegoś, co i tak prawie zawsze jest dostępne jako instrukcja maszyny, sprowadza się to do:
itd. itd. itd.
Ponieważ większość języków zapewnia warunki warunkowe, a szanse są operatorem „, a” oznacza i tak oddział.
...
Jeśli wszystko co masz to
nand
(patrz wikipedia ):return nand (nand (arg1, arg1), nand (arg2, arg2))
źródło
return not (not arg1 and not arg2)
nand
rozwiązaniem.Funkcje (ECMAScript)
Potrzebujesz tylko definicji funkcji i wywołań funkcji. Nie potrzebujesz żadnych rozgałęzień, warunków, operatorów ani wbudowanych funkcji. Zademonstruję implementację przy użyciu ECMAScript.
Najpierw zdefiniujmy dwie funkcje o nazwie
true
ifalse
. Możemy zdefiniować je w dowolny sposób, są one całkowicie arbitralne, ale zdefiniujemy je w bardzo specjalny sposób, który ma pewne zalety, co zobaczymy później:tru
jest funkcją z dwoma parametrami, która po prostu ignoruje swój drugi argument i zwraca pierwszy.fls
jest także funkcją z dwoma parametrami, która po prostu ignoruje swój pierwszy argument i zwraca drugi.Dlaczego kodować
tru
ifls
w ten sposób? Cóż, w ten sposób dwie funkcje reprezentują nie tylko dwie koncepcjetrue
ifalse
, nie, jednocześnie reprezentują także koncepcję „wyboru”, innymi słowy, są również wyrażeniemif
/then
/else
! Oceniamyif
warunek i przekazujemythen
blok ielse
blok jako argumenty. Jeśli warunek oceni się natru
, zwrócithen
blok, jeśli ocenifls
, to zwrócielse
blok. Oto przykład:Zwraca
23
i to:zwraca
42
, tak jak można się spodziewać.Jest jednak zmarszczka:
To drukuje zarówno
then branch
ielse branch
! Czemu?Cóż, to zwraca wartość zwracaną pierwszego argumentu, ale ocenia oba argumenty, ponieważ ECMAScript jest surowe i zawsze ocenia wszystkie argumenty do funkcji przed wywołaniem funkcji. IOW: ocenia pierwszy argument
console.log("then branch")
, który po prostu zwracaundefined
i wywołuje efekt uboczny drukowaniathen branch
na konsoli, i ocenia drugi argument, który również zwracaundefined
i drukuje na konsoli jako efekt uboczny. Następnie zwraca pierwszyundefined
.W rachunku λ, w którym wymyślono to kodowanie, nie stanowi to problemu: rachunek λ jest czysty , co oznacza, że nie ma żadnych skutków ubocznych; dlatego nigdy nie zauważysz, że drugi argument również jest oceniany. Ponadto rachunek λ jest leniwy (a przynajmniej często jest oceniany w normalnej kolejności), co oznacza, że tak naprawdę nie ocenia argumentów, które nie są potrzebne. IOW: w rachunku λ drugi argument nigdy nie byłby oceniany, a gdyby tak było, nie zauważylibyśmy.
ECMAScript jest jednak ścisły , tzn. Zawsze ocenia wszystkie argumenty. Właściwie nie zawsze: na przykład
if
/then
/else
oceniathen
gałąź tylko wtedy, gdy jest spełniony warunektrue
i oceniaelse
gałąź tylko wtedy, gdy jest spełniony warunekfalse
. I chcemy powtórzyć to zachowanie z naszymiff
. Na szczęście, mimo że ECMAScript nie jest leniwy, ma sposób na opóźnienie oceny fragmentu kodu, tak samo jak prawie każdy inny język: zawija go w funkcję, a jeśli nigdy nie wywołasz tej funkcji, kod będzie nigdy nie zostanie stracony.Tak więc zawijamy oba bloki w funkcję, a na końcu wywołuje zwracaną funkcję:
wydruki
then branch
iodbitki
else branch
.Możemy wdrożyć tradycyjny
if
/then
/ welse
ten sposób:Ponownie potrzebujemy dodatkowego zawijania funkcji podczas wywoływania
iff
funkcji i dodatkowych nawiasów wywoływania funkcji w definicji ziff
tego samego powodu, co powyżej:Teraz, gdy mamy te dwie definicje, możemy je wdrożyć
or
. Najpierw przyjrzymy się tabeli prawdy dlaor
: jeśli pierwszy operand jest prawdziwy, wynik wyrażenia jest taki sam jak pierwszy operand. W przeciwnym razie wynik wyrażenia jest wynikiem drugiego operandu. W skrócie: jeśli pierwszym operandem jesttrue
, zwracamy pierwszy operand, w przeciwnym razie zwracamy drugi operand:Sprawdźmy, czy to działa:
Świetny! Jednak ta definicja wygląda trochę brzydko. Pamiętajcie,
tru
ifls
już sami zachowują się jak warunek, więc naprawdę nie ma takiej potrzebyiff
, a zatem wszystkie te funkcje są w ogóle opakowane:or
Oto on: (plus inne operatory logiczne) zdefiniowane za pomocą definicji funkcji i wywołań funkcji w zaledwie kilku liniach:Niestety, ta implementacja jest raczej bezużyteczna: w ECMAScript nie ma żadnych funkcji ani operatorów, które zwracają
tru
lubfls
wszystkie zwracajątrue
lubfalse
, więc nie możemy ich używać z naszymi funkcjami. Ale wciąż możemy wiele zrobić. Na przykład jest to implementacja pojedynczo połączonej listy:Obiekty (Scala)
Być może zauważyliście coś dziwnego:
tru
ifls
odgrywają podwójną rolę, działają zarówno jako wartości danychtrue
ifalse
, ale w tym samym czasie, ale także działać jako wyrażenie warunkowe. Są to dane i zachowanie , połączone w jeden… uhm… „rzecz”… lub (ośmielę się powiedzieć) obiekt !Rzeczywiście,
tru
ifls
są obiektami. A jeśli kiedykolwiek używałeś Smalltalk, Self, Newspeak lub innych języków zorientowanych obiektowo, zauważysz, że implementują booleany w dokładnie taki sam sposób. Zademonstruję taką implementację tutaj w Scali:To BTW jest powodem, dla którego funkcja Zastąp warunkowe refaktoryzacją polimorfizmu zawsze działa: zawsze możesz zastąpić dowolny warunek w twoim programie polimorficzną wysyłką wiadomości, ponieważ, jak właśnie pokazaliśmy, polimorficzna wysyłka wiadomości może zastąpić warunki warunkowe, po prostu je wdrażając. Języki takie jak Smalltalk, Self i Newspeak są tego dowodem na istnienie, ponieważ te języki nie mają nawet warunkowych. (Nie mają też pętli, BTW ani żadnych struktur kontrolnych wbudowanych w język, z wyjątkiem polimorficznego wysyłania komunikatów, czyli wirtualnych wywołań metod.)
Dopasowywanie wzorów (Haskell)
Można również zdefiniować
or
za pomocą dopasowania wzorca lub czegoś w rodzaju częściowych definicji funkcji Haskella:Oczywiście dopasowanie wzorca jest formą wykonania warunkowego, ale z drugiej strony, podobnie jak obiektowe wysyłanie wiadomości.
źródło
False ||| False = False
, a_ ||| _ = True
zamiast tego? :)True ||| undefined
się w ghci, aby zobaczyć!Oto inny sposób zdefiniowania OR, a nawet dowolnego operatora logicznego, przy użyciu najbardziej tradycyjnego sposobu jego zdefiniowania: użyj tabeli prawdy.
Jest to oczywiście dość trywialne w językach wyższego poziomu, takich jak Javascript lub Perl, ale piszę ten przykład w C, aby pokazać, że technika nie zależy od funkcji języka wysokiego poziomu:
Możesz zrobić to samo z AND, NOR, NAND, NOT i XOR. Kod jest wystarczająco czysty, aby wyglądać jak składnia, dzięki czemu możesz robić takie rzeczy:
źródło
BinaryOperator or = new TruthTableBasedBinaryOperator(new TruthTable(false, true, true, true));
Kolejny sposób wyrażenia operatorów logicznych jako liczb całkowitych wyrażeń arytmetycznych (tam, gdzie jest to możliwe). W ten sposób można uniknąć wielu rozgałęzień, aby uzyskać większą ekspresję wielu predykatów.
Niech prawda będzie 1 Niech fałszem będzie 0
jeśli suma obu jest większa niż 1, zwracane jest prawda lub fałsz.
źródło
booleanExpression ? true : false
jest trywialnie równybooleanExpression
.return (arga+argb)>0
return (((arg1 ? 1 : 0)+(arg2 ? 1 : 0)) > 0);
:)arg1 ? 1 : 0;
. Są to niezawodne wyrażenia służące do przekształcania wartości logicznej w liczbę. To tylko wyrażenie zwrotne, które można w prosty sposób refaktoryzować.Dwie formy:
LUB
Mają, podobnie jak golfową przewagę, bycie nieco mniejszym niż inne dotychczasowe sugestie, o jedną gałąź mniej. Mikrosopt nie jest nawet tak głupi, aby zmniejszyć liczbę rozgałęzień, jeśli rozważamy stworzenie prymitywu, który byłby w związku z tym bardzo intensywnie wykorzystywany.
Definicja JavaScript
||
jest podobna do tego, co w połączeniu z luźnym pisaniem oznacza, że wyrażeniefalse || "abc"
ma wartość"abc"
i42 || "abc"
ma wartość42
.Chociaż jeśli masz już inne operatory logiczne, osoby takie
nand(not(arg1), not(arg2))
mogą mieć tę zaletę, że w ogóle nie mają rozgałęzień.źródło
Oprócz wszystkich zaprogramowanych rozwiązań wykorzystujących konstrukcję if, możliwe jest zbudowanie bramki OR poprzez połączenie trzech bramek NAND. Jeśli chcesz zobaczyć, jak to się robi w wikipedii, kliknij tutaj .
Z tego wyrażenia
który używa NOT i AND daje taką samą odpowiedź jak OR. Zauważ, że użycie zarówno NOT, jak i AND jest niejasnym sposobem wyrażania NAND.
źródło
Wszystkie dobre odpowiedzi zostały już podane. Ale nie pozwolę, żeby mnie to powstrzymało.
Alternatywnie:
Mam nadzieję, że nikt nigdy nie użyłby takich podejść. Są tutaj tylko po to, aby promować świadomość alternatyw.
Aktualizacja:
Ponieważ liczby ujemne mogą złamać oba powyższe podejścia, oto kolejna okropna sugestia:
To po prostu wykorzystuje prawa DeMorgan i narusza fakt, który
*
jest podobny do tego,&&
kiedytrue
ifalse
są traktowane odpowiednio1
i0
. (Czekaj, mówisz, że to nie jest golf golfowy?)Oto przyzwoita odpowiedź:
Ale to zasadniczo identyczne z innymi już udzielonymi odpowiedziami.
źródło
arg1+arg2
, -1 i 0 dlamax(arg1,arg2)
itd.Jednym ze sposobów zdefiniowania
or
jest użycie tabeli odnośników. Możemy to wyraźnie zaznaczyć:tworzymy tablicę z wartościami, które powinna zwracać wartość w zależności od tego, co
a
jest. Następnie sprawdzamy. W językach podobnych do C ++,bool
promuje się do wartości, która może być używana jako indeks tablicowy ztrue
istnieniem1
ifalse
bytem0
.Następnie możemy rozszerzyć to na inne operacje logiczne:
Ich wadą jest to, że wymaga notacji przedrostkowej.
a teraz możesz pisać
true *OR* false
i to działa.Powyższa technika wymaga języka obsługującego wyszukiwanie zależne od argumentów i szablony. Prawdopodobnie można to zrobić w języku zawierającym generyczne i ADL.
Poza tym możesz rozszerzyć
*OR*
powyższe, aby pracować z zestawami. Wystarczy utworzyć bezpłatną funkcjęinvoke
w tej samej przestrzeni nazw, coor_tag
:a teraz
set *OR* set
zwraca związek dwóch.źródło
Ten pamięta mi funkcje charasteristyczne:
Dotyczy to tylko języków, które mogą traktować logiczne jako (1, 0). Nie dotyczy Smalltalk ani Python, ponieważ wartość logiczna jest klasą. W smalltalk idą jeszcze dalej (zostanie to zapisane w formie pseudokodu):
Istnieją podwójne metody dla:
Zatem „logika” jest całkowicie poprawna w instrukcji OP, chociaż jest pełna. Uwaga, nie jest źle. Jest idealny, jeśli potrzebujesz funkcji, która działa jak operator matematyczny oparty na powiedzmy pewnego rodzaju macierzy. Inni zaimplementowaliby rzeczywistą kostkę (jak instrukcja Quine-McCluskey):
I ocenisz lub [a] [b]
Tak więc, każda logika tutaj jest poprawna (oprócz tej opublikowanej jako przy użyciu w języku operatora OR xDDDDDDDD).
Ale moim ulubionym jest prawo DeMorgan:
!(!a && !b)
źródło
Spójrz na standardową bibliotekę Swift i sprawdź ich implementację skrótu OR i skrótu AND, które nie oceniają drugich operandów, jeśli nie są potrzebne / dozwolone.
źródło
Logika jest całkowicie poprawna, ale można ją uprościć:
I prawdopodobnie w twoim języku jest operator OR, więc - o ile nie jest to sprzeczne z duchem pytania - dlaczego nie
źródło
if arg1 = True or arg2 = True { return true } else { return false }
Jeszcze lepiejreturn arg1 = True or arg2 = True
.if condition then true else false
jest zbędny.