Czy istnieje różnica między TAK / NIE, PRAWDA / FAŁSZ i prawda / fałsz w obiektywnym-c?

154

Naprawdę proste pytanie; czy istnieje różnica między tymi wartościami (i czy istnieje różnica między BOOL a bool)? Współpracownik wspomniał, że oceniają różne rzeczy w Objective-C, ale kiedy spojrzałem na kroje czcionek w ich odpowiednich plikach .h, YES / TRUE / true zostały zdefiniowane jako, 1a NO / FALSE / false zostały zdefiniowane jako 0. Czy naprawdę jest jakaś różnica?

Kevlar
źródło
5
Z praktycznego punktu widzenia nie ma różnicy. Prawdopodobnie możesz zrobić różne sztuczki, aby zademonstrować różnicę, ale generalnie błądzisz na terytorium „niezdefiniowanego zachowania”.
Hot Licks

Odpowiedzi:

84

Nie ma praktycznej różnicy pod warunkiem, że używasz BOOLzmiennych jako wartości logicznych. C przetwarza wyrażenia boolowskie w oparciu o to, czy dają 0 czy nie 0. Więc:

if(someVar ) { ... }
if(!someVar) { ... }

oznacza to samo co

if(someVar!=0) { ... }
if(someVar==0) { ... }

dlatego każdy typ lub wyrażenie prymitywne można oceniać jako test logiczny (w tym np. wskaźniki). Pamiętaj, że powinieneś zrobić to pierwsze, a nie drugie.

Zauważ, że nie ma różnicy, jeśli przypisać wartości rozwarte do tzw BOOLzmiennej i test dla określonych wartości, więc zawsze używać ich jako wartości logiczne i przypisać tylko ich z #definewartościami.

Co ważne, nigdy nie testuj wartości logicznych za pomocą porównania znaków - jest to nie tylko ryzykowne, ponieważ someVarmożna by przypisać wartość niezerową, która nie jest TAK, ale, moim zdaniem, co ważniejsze, nie pozwala poprawnie wyrazić intencji:

if(someVar==YES) { ... } // don't do this!
if(someVar==NO ) { ... } // don't do this either!

Innymi słowy, używaj konstruktów zgodnie z ich przeznaczeniem i dokumentacją do użycia, a oszczędzisz sobie świata krzywdy w C.

Lawrence Dol
źródło
100

Wierzę, że jest różnica między booli BOOL, sprawdź tę stronę o wyjaśnienie dlaczego:
http://iosdevelopertips.com/objective-c/of-bool-and-yes.html

Ponieważ BOOLjest unsigned chartypem raczej niż pierwotnym, zmienne typu BOOLmogą zawierać wartości inne niż YESi NO.

Rozważ ten kod:

BOOL b = 42;

if (b) {
    printf("b is not NO!\n");
}

if (b != YES) {
    printf("b is not YES!\n");
}

Wynik to:

b nie jest NIE!
b nie jest TAK!

Dla większości ludzi jest to niepotrzebna obawa, ale jeśli naprawdę chcesz mieć wartość logiczną, lepiej jest użyć bool. Powinienem dodać: SDK iOS zwykle używa BOOLdefinicji interfejsu, więc jest to argument, którego należy się trzymać BOOL.

Dan J
źródło
5
Ale zauważ, że oryginalna implementacja C nie miała bool, i dlatego tradycją jest używanie intlub charjako wartości logicznej, czasami z #define, aby ukryć różnicę, a czasami nie. Właściwie nie jestem pewien, czy nawet obecne standardy wymagają boolimplementacji w sposób uniemożliwiający badanie jego wewnętrznej struktury.
Hot Licks
1
Chociaż pierwsza printfkłamie. Wartość bnie YESjest równa „nie zero”, co sprawdza warunek. Więc powinieneś to zrobić printf("b is not zero"), co niekoniecznie jest tym samym, co YES. W tym przypadku bjest zarówno „niezerowe”, jak i „nie TAK”.
Lawrence Dol
Dzięki Lawrence, dokonałem aktualizacji w tym zakresie.
Dan J
Rzeczywiście, nie otrzymałem drugiego wyjścia w Xcode 8.2. Gdzie zawodzę?
Igor Kislyuk
1
@HotLicks nie ma nieodłącznej różnicy między 0, nie zerem a fałszem i prawdą. Dopóki wartość ma być logiczną wartością logiczną, zawsze będzie mieć ten interfejs, aby zachować zgodność binarną. Problemy zaczynają się, gdy używasz innych niż logiczne, które wyglądają jak logiczne, na przykład funkcja wejścia standardowej biblioteki aplikacji c, main zwraca 0 po sukcesie, wielu kończy się na myśli o tym 0 jako logicznym, podczas gdy w rzeczywistości jest to wyliczenie zdefiniowane przez aplikację lub zdefiniowane przez użytkownika wartość, która wywoływana często oczekuje, że będzie niezerowa w przypadku nieprawidłowego zakończenia.
Dmitry
52

Zrobiłem wyczerpujący test na tym. Moje wyniki powinny mówić same za siebie:

//These will all print "1"
NSLog(@"%d", true == true);
NSLog(@"%d", TRUE == true);
NSLog(@"%d", YES  == true);
NSLog(@"%d", true == TRUE);
NSLog(@"%d", TRUE == TRUE);
NSLog(@"%d", YES  == TRUE);
NSLog(@"%d", true == YES);
NSLog(@"%d", TRUE == YES);
NSLog(@"%d", YES  == YES);

NSLog(@"%d", false == false);
NSLog(@"%d", FALSE == false);
NSLog(@"%d", NO    == false);
NSLog(@"%d", false == FALSE);
NSLog(@"%d", FALSE == FALSE);
NSLog(@"%d", NO    == FALSE);
NSLog(@"%d", false == NO);
NSLog(@"%d", FALSE == NO);
NSLog(@"%d", NO    == NO);


//These will all print "0"
NSLog(@"%d", false == true);
NSLog(@"%d", FALSE == true);
NSLog(@"%d", NO    == true);
NSLog(@"%d", false == TRUE);
NSLog(@"%d", FALSE == TRUE);
NSLog(@"%d", NO    == TRUE);
NSLog(@"%d", false == YES);
NSLog(@"%d", FALSE == YES);
NSLog(@"%d", NO    == YES);

NSLog(@"%d", true == false);
NSLog(@"%d", TRUE == false);
NSLog(@"%d", YES  == false);
NSLog(@"%d", true == FALSE);
NSLog(@"%d", TRUE == FALSE);
NSLog(@"%d", YES  == FALSE);
NSLog(@"%d", true == NO);
NSLog(@"%d", TRUE == NO);
NSLog(@"%d", YES  == NO);

Wynik to:

2013-02-19 20:30:37.061 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.061 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.072 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.073 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.073 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.074 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.074 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.075 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.075 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.076 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.077 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.077 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.078 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.078 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.079 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.079 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.080 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.080 BooleanTests[27433:a0f] 1
2013-02-19 20:30:37.081 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.081 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.082 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.091 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.092 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.093 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.093 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.094 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.094 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.095 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.095 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.096 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.096 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.097 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.098 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.101 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.102 BooleanTests[27433:a0f] 0
2013-02-19 20:30:37.102 BooleanTests[27433:a0f] 0
Supuhstar
źródło
3
[[NSObject] assign] init] nie jest równe TRUE ani YES. Więc testowanie inicjalizacji obiektu z if ([[NSObject] assign] init] == ​​TRUE) zakończy się niepowodzeniem. Nigdy nie czułem się komfortowo z językiem definiującym pojedynczą „prawdziwą” wartość, podczas gdy w rzeczywistości wystarczy każda wartość niezerowa.
DrFloyd5
3
@SamuelRenkert Nigdy nie czułem się komfortowo z językiem przyjmującym wartość inną niż boolowska w rozszerzeniu iflub while. Jak ... while("guitar gently weeps")nie powinno działać ...
Supuhstar
@SamuelRenkert także backdoor dla Linuksa, który został znaleziony w 2003 roku:if (user_id = ROOT_UID)
Supuhstar
14

Możesz przeczytać odpowiedzi na to pytanie . Podsumowując, w Objective-C (z definicji w objc.h):

typedef signed char        BOOL; 
// BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" 
// even if -funsigned-char is used.
#define OBJC_BOOL_DEFINED


#define YES             (BOOL)1
#define NO              (BOOL)0
Barry Wark
źródło
11

Główna (niebezpieczna!) Różnica między truei YESdotyczy serializacji JSON.

Na przykład mamy żądanie serwera typu JSON i musimy wysłać true / false w json sence:

NSDictionary *r1 = @{@"bool" : @(true)};
NSDictionary *r2 = @{@"bool" : @(YES)};
NSDictionary *r3 = @{@"bool" : @((BOOL)true)};

Następnie konwertujemy go na ciąg JSON przed wysłaniem jako

NSData *data = [NSJSONSerialization  dataWithJSONObject:requestParams options:0 error:nil];
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

Wynik to

jsonString1 // {"bool":1}
jsonString2 // {"bool":true}
jsonString3 // {"bool":true}

Ze względu na logikę interfejsu API jsonString1może spowodować błąd.

Dlatego uważaj na wartości logiczne w Objective-C.

Podsumowując, tylko dokładne @YESi rzutowane wartości @((BOOL)expression)__NSCFBooleantypu i konwertowane na truez serializacją JSON. Wszystkie inne wyrażenia, takie jak @(expression1 && expression2)(parzyste @(YES && YES)), są __NSCFNumber (int)typu i są konwertowane na 1format JSON.

PS Możesz po prostu użyć wartości logicznej typu string

@{@"bool" : @"true"}; // in JSON {"bool":true}
malex
źródło
1

Istnieje subtelny błąd, o którym nikt tutaj nie wspomniał, a myślałem, że powinienem zawrzeć ... bardziej logiczny błąd niż cokolwiek innego:

int i = 2;
if(i);        //true
if(i==YES);   // false
if((!!i)==YES); //true

więc problem polega na tym, że (YES==1)w C porównanie nie jest logiczne, ale oparte na wartości.

ponieważ YESjest tylko #define(a nie czymś nieodłącznym dla języka), musi mieć jakąś wartość i 1ma jak największy sens.

Grady Player
źródło
Jest to zasadniczo ta sama odpowiedź, co DanJ, ​​sprzed ponad 2 lat, z mniejszą ilością szczegółów.
Lawrence Dol
@LawrenceDol Nie wiem, wspomina, że ​​TAK jest tylko #definiowane jako 1 i nie jest związane z językiem, tak jak w języku wyższego poziomu ... ktoś mógłby uzyskać wartość z tego ... ale dobrze trolling, z tobą.
Grady Player
0

Myślę, że dodają TAK / NIE, aby w wielu przypadkach były bardziej oczywiste. Na przykład:

[button setHidden:YES];

brzmi lepiej niż

[button setHidden:TRUE];
Marco
źródło
2
Nie zgadzam się; obaj czytają mi to samo. Jednak w interfejsie użytkownika dla laika myślę, że tak / nie wygląda ładniej.
Lawrence Dol
16
Ja też się nie zgadzam. Jeśli już, to źle się czyta, ponieważ nie trzyma się niepisanych standardów, które są używane od lat w innych językach. IE jest najlepszym przykładem tego, co się dzieje, gdy nie przestrzegasz wielu standardów.
FreeAsInBeer,
1
Połowa głosu za 2 nieprecyzyjne odpowiedzi i połowa za podpisaniem odpowiedzi
fpg1503
-2

Najpierw zbadajmy, co jest prawdą, a co fałszem i co nadaje im znaczenie.

możemy skonstruować strukturę o nazwie if a then b else c w rachunku lambda w następujący sposób:

(\ifThenElse. <use if then else>)(\a. \b. \c. a b c)

W JavaScript wygląda to tak:

(function(ifThenElse) {
    // use ifThenElse
})(function(a) {
    return function(b) {
        return function(c) {
            return a(b)(c);
        };
    };
});

aby ifThenElse było użyteczne, potrzebujemy funkcji „true”, która wybiera prawo lub lewo i robi to, ignorując drugą opcję, lub funkcja „false”, która wybiera opcję „true”, nie przyjmuje.

Możemy zdefiniować te funkcje w następujący sposób:

(\true. <use true>)(\a. \b. a) and (\false. <use false>)(\a. \b. b)

w JavaScript wygląda to tak:

(function(True) {
    // use True
})(function(a) {
     return function(b) {
         return a;
     }
});

(function(False) {
    // use True
})(function(a) {
     return function(b) {
         return b;
     }
});

teraz możemy wykonać następujące czynności

(\true. \false. \ifThenElse. \doThis. \doThat. ifThenElse true doThis doThat)
(\a. \b. a)(\a. \b. b)(\a. \b. \c. a b c)(\a. ())(\a. ())

z doThis i doThat jest (\ a. ()) ponieważ rachunek lambda nie oferuje żadnych usług, takich jak drukowanie / matematyka / ciągi znaków, wszystko, co możemy zrobić, to nic nie robić i powiedzieć, że to zrobiliśmy (a później oszukiwać, zastępując to usługami w nasz system, który zapewnia pożądane efekty uboczne)

zobaczmy to w akcji.

(function(True) {
    return (function(False) {
        return (function(ifThenElse) {
            return (function(doThis) {
                return (function(doThat) {
                    return ifThenElse(True)(doThis)(doThat);
                });
            });
        });
    })
})(function(a) {
     return function(b) {
         return a;
     }
})(function(a) {
     return function(b) {
         return b;
     }
})(function(a) {
    return function(b) {
        return function(c) {
            return a(b)(c);
        };
    };
})(function(a) { console.log("you chose LEFT!"); })
(function(a) {console.log("you chose RIGHT");})();

To głębokie środowisko, które można by uprościć, gdyby pozwolono nam używać tablic / map / argumentów / lub więcej niż jednej instrukcji do podziału na wiele funkcji, ale chcę, aby było tak czyste, jak tylko mogę, ograniczając się do funkcji dokładnie jednego argumentu tylko.

zauważ, że nazwa Prawda / Fałsz nie ma żadnego wewnętrznego znaczenia, możemy łatwo zmienić ich nazwę na tak / nie, lewo / prawo, prawo / lewo, zero / jeden, jabłko / pomarańcza. Ma to znaczenie w tym, że dokonany wybór jest spowodowany jedynie rodzajem dokonanego wyboru. Jeśli więc wydrukowano „LEFT”, wiemy, że wybór może być tylko prawdziwy i na podstawie tej wiedzy możemy kierować naszymi dalszymi decyzjami.

Podsumowując

function ChooseRight(left) {
    return function _ChooseRight_inner(right) {
        return right;
    }
}
function ChooseLeft(left) {
    return function _ChooseLeft_inner(right) {
        return left;
    }
}

var env = {
    '0': ChooseLeft,
    '1': ChooseRight,
    'false': ChooseRight,
    'true': ChooseLeft,
    'no': ChooseRight
    'yes': ChooseLeft,
    'snd': ChooseRight,
    'fst': ChooseLeft
};
var _0 = env['0'];
var _1 = env['1'];
var _true = env['true'];
var _false = env['false'];
var yes = env['yes'];
var no = env['no'];

// encodes church zero or one to boolean
function lambda_encodeBoolean(self) {
    return self(false)(true);
}
// decodes a Boolean to church zero or one
function lambda_decodeBoolean(self) {
    console.log(self, self ? env['true'] : env['false']);
    return self ? env['true'] : env['false'];
}

lambda_decodeBoolean('one' === 'two')(function() {
    console.log('one is two');
})(function() {
    console.log('one is not two');
})();

lambda_decodeBoolean('one' === 'one')(function() {
    console.log('one is one');
})(function() {
    console.log('one is not one');
})();
Dmitry
źródło
-7

Nie, TAK / NIE to inny sposób określania PRAWDA / FAŁSZ (1/0)

Marco
źródło