“ และ” และ“ หรือ” ทำงานกับค่าที่ไม่ใช่บูลีนอย่างไร


105

ฉันกำลังพยายามเรียนรู้ python และเจอโค้ดที่ดีและสั้น แต่ไม่สมเหตุสมผลเลย

บริบทคือ:

def fn(*args):
    return len(args) and max(args)-min(args)

ฉันเข้าใจว่ามันทำอะไร แต่ทำไม python ถึงทำเช่นนี้ - คือส่งคืนค่าแทนที่จะเป็น True / False

10 and 7-2

ผลตอบแทน 5. ในทำนองเดียวกันการเปลี่ยนและเป็นหรือจะส่งผลให้ฟังก์ชันการทำงานเปลี่ยนไป ดังนั้น

10 or 7 - 2

จะกลับมา 10.

นี่เป็นสไตล์ที่ถูกต้อง / เชื่อถือได้หรือมี gotchas เกี่ยวกับเรื่องนี้หรือไม่?


1
and(เช่นเดียวกับor) ไม่ จำกัด เฉพาะการทำงานกับหรือส่งคืนค่าบูลีน
cs95

1
IMNSHO: นั่นเป็นวิธีการเขียนที่ค่อนข้างสับสน ฉันไม่สามารถบอกได้อย่างชัดเจนว่าควรจะส่งคืนบูลีน (มีค่าต่ำสุดและสูงสุดที่แตกต่างกันหรือไม่) หรือตัวเลข (ความแตกต่างของค่าต่ำสุดและสูงสุดคืออะไร) ถ้าเป็นอย่างหลังก็มีคำถามเช่นกันว่าสมเหตุสมผลหรือไม่ที่จะให้ความแตกต่างของรายการที่มีความยาวเป็นศูนย์เป็นตัวเลข (แทนNoneหรือข้อยกเว้น)
ilkkachu

7
มันใช้งานได้ตามที่คนอื่นอธิบายไว้อย่างไรก็ตามปัญหาหนึ่งที่เป็นไปได้คือถ้ามันส่งคืน0คุณไม่สามารถบอกได้ว่าargsว่างหรือไม่ว่าง แต่มีองค์ประกอบทั้งหมดเท่ากัน
โดยเฉพาะ Lime

@EspecialLime: เป๊ะ. ฉันได้กล่าวไว้ในของฉันคำตอบ
Eric Duminil

ที่เกี่ยวข้อง: ตัวอย่างการปฏิบัติของงูใหญ่และผู้ประกอบการ
Wim

คำตอบ:


145

TL; ดร

เราเริ่มต้นด้วยการสรุปพฤติกรรมสองอย่างของตัวดำเนินการตรรกะสองตัวandและor. สำนวนเหล่านี้จะเป็นพื้นฐานของการสนทนาด้านล่าง

and

ส่งคืนค่า Falsy แรกหากมีไม่เช่นนั้นจะส่งคืนค่าสุดท้ายในนิพจน์

or

ส่งคืนค่า Truthy แรกหากมีค่าอื่นส่งคืนค่าสุดท้ายในนิพจน์

นอกจากนี้ยังสรุปพฤติกรรมในเอกสารโดยเฉพาะในตารางนี้:

ป้อนคำอธิบายภาพที่นี่

ตัวดำเนินการเดียวที่ส่งคืนค่าบูลีนโดยไม่คำนึงถึงตัวถูกดำเนินการคือตัวดำเนินnotการ


"ความจริง" และการประเมิน "ความจริง"

คำสั่ง

len(args) and max(args) - min(args)

เป็นมาก pythonicรัดกุม (และเนื้อหาที่อ่านได้น้อยลง) วิธีการพูดว่า "ถ้าargsไม่ว่างกลับผลมาจากmax(args) - min(args)" 0มิฉะนั้นผลตอบแทน โดยทั่วไปแล้วเป็นการif-elseแสดงนิพจน์ที่กระชับมากขึ้น ตัวอย่างเช่น,

exp1 and exp2

ควร (โดยประมาณ) แปลเป็น:

r1 = exp1
if r1:
    r1 = exp2

หรือเทียบเท่า

r1 = exp2 if exp1 else exp1

ในทำนองเดียวกัน

exp1 or exp2

ควร (โดยประมาณ) แปลเป็น:

r1 = exp1
if not r1:
    r1 = exp2

หรือเทียบเท่า

r1 = exp1 if exp1 else exp2

วัตถุ python ที่กำหนดเองexp1และอยู่ที่ไหนexp2หรือนิพจน์ที่ส่งคืนวัตถุ กุญแจสำคัญในการทำความเข้าใจการใช้งานของตรรกะandและorตัวดำเนินการในที่นี้คือการทำความเข้าใจว่าพวกเขาไม่ได้ถูก จำกัด ให้ทำงานบนหรือส่งคืนค่าบูลีน สามารถทดสอบวัตถุใด ๆ ที่มีค่าความจริงได้ที่นี่ ซึ่งรวมถึงint, str, list, dict, tuple, set, NoneTypeวัตถุและผู้ใช้กำหนด กฎการลัดวงจรยังคงมีผลบังคับใช้เช่นกัน

แต่ความจริงคืออะไร?
หมายถึงวิธีการประเมินวัตถุเมื่อใช้ในนิพจน์เงื่อนไข @Patrick Haugh สรุปความจริงไว้ในโพสต์นี้

ค่าทั้งหมดถือเป็น "จริง" ยกเว้นค่าต่อไปนี้ซึ่งเป็น "เท็จ":

  • None
  • False
  • 0
  • 0.0
  • 0j
  • Decimal(0)
  • Fraction(0, 1)
  • [] - ว่างเปล่า list
  • {} - ว่างเปล่า dict
  • () - ว่างเปล่า tuple
  • '' - ว่างเปล่า str
  • b'' - ว่างเปล่า bytes
  • set() - ว่างเปล่า set
  • ว่างเปล่าrangeเช่นrange(0)
  • วัตถุที่
    • obj.__bool__() ผลตอบแทน False
    • obj.__len__() ผลตอบแทน 0

ค่า "จริง" จะตอบสนองการตรวจสอบที่ดำเนินการโดยifหรือwhile คำสั่ง เราใช้ "truthy" และ "falsy" เพื่อให้แตกต่างจาก boolค่าและTrueFalse


วิธีการandทำงาน

เราสร้างคำถามของ OP เพื่อต่อยอดในการอภิปรายเกี่ยวกับวิธีการที่ตัวดำเนินการเหล่านี้ในกรณีเหล่านี้

กำหนดฟังก์ชันพร้อมนิยาม

def foo(*args):
    ...

ฉันจะคืนค่าความแตกต่างระหว่างค่าต่ำสุดและค่าสูงสุดในรายการอาร์กิวเมนต์เป็นศูนย์หรือมากกว่าได้อย่างไร

การหาค่าต่ำสุดและสูงสุดเป็นเรื่องง่าย (ใช้ฟังก์ชัน inbuilt!) อุปสรรคเดียวที่นี่คือการจัดการกรณีมุมอย่างเหมาะสมซึ่งรายการอาร์กิวเมนต์อาจว่างเปล่า (เช่นการโทรfoo()) เราสามารถทำได้ทั้งสองบรรทัดในบรรทัดเดียวด้วยตัวandดำเนินการ:

def foo(*args):
     return len(args) and max(args) - min(args)
foo(1, 2, 3, 4, 5)
# 4

foo()
# 0

นับตั้งแต่มีการใช้การแสดงออกที่สองยังต้องได้รับการประเมินถ้าแรกคือand Trueโปรดทราบว่าถ้าการแสดงออกครั้งแรกที่ได้รับการประเมินให้เป็น truthy ค่าส่งกลับเป็นเสมอผลของการแสดงออกที่สอง หากนิพจน์แรกได้รับการประเมินว่าเป็น Falsy ผลลัพธ์ที่ส่งกลับจะเป็นผลลัพธ์ของนิพจน์แรก

ในฟังก์ชั่นข้างต้นหากfooได้รับหนึ่งหรือมากกว่าหนึ่งข้อโต้แย้งlen(args)ที่มีค่ามากกว่า0(เป็นจำนวนบวก) max(args) - min(args)ดังนั้นผลลัพธ์ที่ปรากฏมี OTOH ถ้าข้อโต้แย้งไม่ได้ผ่านไปlen(args)เป็น0ซึ่งเป็น Falsy และ0ถูกส่งกลับ

โปรดทราบว่าทางเลือกอื่นในการเขียนฟังก์ชันนี้คือ:

def foo(*args):
    if not len(args):
        return 0
    
    return max(args) - min(args)

หรืออย่างรวบรัด

def foo(*args):
    return 0 if not args else max(args) - min(args)

หากแน่นอนไม่มีฟังก์ชันเหล่านี้ทำการตรวจสอบประเภทใด ๆ ดังนั้นหากคุณไม่เชื่อถืออินพุตที่ให้มาอย่างสมบูรณ์อย่าพึ่งพาความเรียบง่ายของโครงสร้างเหล่านี้


วิธีการorทำงาน

ฉันอธิบายการทำงานorในลักษณะที่คล้ายกันพร้อมกับตัวอย่างที่สร้างขึ้น

กำหนดฟังก์ชันพร้อมนิยาม

def foo(*args):
    ...

คุณจะกรอกfooตัวเลขทั้งหมดได้9000อย่างไร?

เราใช้orจับเคสเข้ามุมตรงนี้ เรากำหนดfooเป็น:

def foo(*args):
     return [x for x in args if x > 9000] or 'No number over 9000!'

foo(9004, 1, 2, 500)
# [9004]

foo(1, 2, 3, 4)
# 'No number over 9000!'

fooทำการกรองในรายการเพื่อเก็บตัวเลขทั้งหมด9000ไว้ หากมีตัวเลขดังกล่าวอยู่ผลลัพธ์ของความเข้าใจในรายการจะเป็นรายการที่ไม่ว่างเปล่าซึ่งเป็นความจริงดังนั้นจึงถูกส่งกลับ (การดำเนินการลัดวงจรที่นี่) หากไม่มีตัวเลขดังกล่าวผลลัพธ์ของคอมพ์รายการจะ[]เป็น Falsy ดังนั้นนิพจน์ที่สองจึงได้รับการประเมินแล้ว (สตริงที่ไม่ว่างเปล่า) และถูกส่งกลับ

เมื่อใช้เงื่อนไขเราสามารถเขียนฟังก์ชันนี้ใหม่เป็น

def foo(*args):
    r = [x for x in args if x > 9000]
    if not r:
        return 'No number over 9000!' 
    
    return r

ก่อนหน้านี้โครงสร้างนี้มีความยืดหยุ่นมากขึ้นในแง่ของการจัดการข้อผิดพลาด


33
ไม่ใช่ "pythonic" ที่จะเสียสละความชัดเจนทั้งหมดเพื่อความกะทัดรัดซึ่งฉันคิดว่าเป็นเช่นนั้นที่นี่ ไม่ใช่โครงสร้างที่ตรงไปตรงมา
DBedrenko

11
ฉันคิดว่าเราควรทราบว่านิพจน์เงื่อนไขของ Python ทำให้ไวยากรณ์นี้ไม่ค่อยพบบ่อย แน่นอนฉันชอบ max (args) - min (args) ถ้า len (args) อื่นเป็น 0 เป็นค่าดั้งเดิม
richardb

3
อีกอย่างหนึ่งที่ทำให้สับสนในตอนแรกคือการกำหนดค่าหากไม่มีอยู่: "some_var = arg หรือ 3"
Erik

12
@Baldrickk ก่อนที่ผู้คนจะเริ่มทุบตีไวยากรณ์นี้เพื่อสนับสนุนตัวดำเนินการที่เกี่ยวข้องโปรดจำไว้ว่าเมื่อพูดถึงนิพจน์เงื่อนไข n-ary ตัวดำเนินการที่เกี่ยวข้องสามารถออกจากมือได้อย่างรวดเร็ว ตัวอย่างเช่นif ... else (if ... else (if ... else (if ... else ...)))สามารถเขียนใหม่ได้เช่นกัน... and ... and ... and ... and ...และ ณ จุดนั้นมันยากที่จะโต้แย้งความสามารถในการอ่านสำหรับทั้งสองกรณี
cs95

4
ไม่ใช่ pythonic ที่จะเสียสละความชัดเจนเพื่อความกะทัดรัด แต่สิ่งนี้ไม่ได้ทำเช่นนั้น มันเป็นสำนวนที่รู้จักกันดี มันเป็นสำนวนที่คุณต้องเรียนรู้เช่นเดียวกับสำนวนอื่น ๆ แต่มันแทบจะไม่ 'เสียสละความชัดเจน'
เส้นทางไมล์

18

อ้างจากPython Docs

โปรดทราบว่าค่าandมิได้or จำกัดคุ้มค่าและพิมพ์พวกเขากลับไปFalseและTrueแต่กลับโต้แย้งการประเมินที่ผ่านมา บางครั้งสิ่งนี้มีประโยชน์เช่นถ้าsเป็นสตริงที่ควรถูกแทนที่ด้วยค่าเริ่มต้นหากว่างเปล่านิพจน์s or 'foo'จะให้ค่าที่ต้องการ

ดังนั้นนี่คือวิธีที่ Python ออกแบบมาเพื่อประเมินนิพจน์บูลีนและเอกสารข้างต้นช่วยให้เราเข้าใจถึงสาเหตุที่พวกเขาทำเช่นนั้น

หากต้องการรับค่าบูลีนเพียงพิมพ์แคสต์

return bool(len(args) and max(args)-min(args))

ทำไม?

การลัดวงจร

ตัวอย่างเช่น:

2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all

เช่นเดียวกันorนั่นคือมันจะส่งคืนนิพจน์ซึ่งเป็นTruthyทันทีที่พบทำให้การประเมินนิพจน์ที่เหลือนั้นซ้ำซ้อน

แทนที่จะกลับไม่ยอมใครง่ายๆTrueหรือFalseงูใหญ่ผลตอบแทนTruthyหรือFalseyซึ่งมีอยู่แล้วจะประเมินหรือTrue Falseคุณสามารถใช้นิพจน์ตามที่เป็นอยู่และจะยังใช้งานได้


หากต้องการทราบว่าTruthyและFalseyคืออะไรให้ตรวจสอบคำตอบของ Patrick Haugh


7

และและหรือดำเนินการตรรกะบูลีน แต่จะส่งคืนค่าจริงค่าหนึ่งเมื่อเปรียบเทียบ เมื่อใช้และค่าจะถูกประเมินในบริบทบูลีนจากซ้ายไปขวา 0, '', [], (), {}และไม่มีเป็นเท็จในบริบทบูลีน ทุกสิ่งทุกอย่างเป็นความจริง

หากค่าทั้งหมดเป็นจริงในบริบทบูลีนและส่งกลับค่าสุดท้าย

>>> 2 and 5
5
>>> 2 and 5 and 10
10

หากค่าใด ๆ เป็นเท็จในบริบทบูลีนและส่งกลับค่าเท็จแรก

>>> '' and 5
''
>>> 2 and 0 and 5
0

ดังนั้นรหัส

return len(args) and max(args)-min(args)

ส่งคืนค่าmax(args)-min(args)เมื่อมีargs อื่นจะส่งกลับlen(args)ซึ่งเป็น 0


5

นี่เป็นสไตล์ที่ถูกต้อง / เชื่อถือได้หรือมี gotchas เกี่ยวกับเรื่องนี้หรือไม่?

นี่คือ Legit มันเป็นการประเมินการลัดวงจรโดยที่ค่าสุดท้ายจะถูกส่งกลับ

คุณให้ตัวอย่างที่ดี ฟังก์ชันจะส่งคืน0หากไม่มีการส่งผ่านอาร์กิวเมนต์และโค้ดไม่จำเป็นต้องตรวจสอบกรณีพิเศษที่ไม่มีอาร์กิวเมนต์ผ่าน

อีกวิธีหนึ่งในการใช้สิ่งนี้คือการตั้งค่าเริ่มต้นไม่มีอาร์กิวเมนต์สำหรับอนุพันธ์ที่เปลี่ยนแปลงได้เช่นรายการว่าง:

def fn(alist=None):
    alist = alist or []
    ....

หากค่าที่ไม่เป็นจริงบางค่าถูกส่งผ่านalistค่าเริ่มต้นไปยังรายการว่างวิธีที่สะดวกในการหลีกเลี่ยงifคำสั่งและข้อผิดพลาดของอาร์กิวเมนต์เริ่มต้นที่ไม่แน่นอน


3

Gotchas

ใช่มีไม่กี่ gotchas

fn() == fn(3) == fn(4, 4)

อันดับแรกหากfnส่งคืน0คุณไม่สามารถทราบได้ว่าถูกเรียกโดยไม่มีพารามิเตอร์ใด ๆ โดยมีพารามิเตอร์เดียวหรือหลายพารามิเตอร์ที่เท่ากัน:

>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0

อะไรfnหมายถึง?

จากนั้น Python เป็นภาษาแบบไดนามิก ไม่ได้ระบุไว้ทุกที่ว่าfnทำอะไรสิ่งที่อินพุตควรเป็นอย่างไรและผลลัพธ์ควรมีลักษณะอย่างไร ดังนั้นการตั้งชื่อฟังก์ชันให้ถูกต้องจึงสำคัญมาก argsในทำนองเดียวกันการขัดแย้งจะได้ไม่ต้องถูกเรียกว่า delta(*numbers)หรือcalculate_range(*numbers)อาจอธิบายได้ดีกว่าว่าฟังก์ชันควรทำอย่างไร

ข้อผิดพลาดของอาร์กิวเมนต์

ในที่สุดตัวandดำเนินการเชิงตรรกะควรป้องกันไม่ให้ฟังก์ชันล้มเหลวหากถูกเรียกโดยไม่มีอาร์กิวเมนต์ใด ๆ ยังคงล้มเหลวหากอาร์กิวเมนต์บางอย่างไม่ใช่ตัวเลขแม้ว่า:

>>> fn('1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'

ทางเลือกอื่นที่เป็นไปได้

นี่คือวิธีการเขียนฟังก์ชันตามหัวข้อ"ขอการให้อภัยได้ง่ายกว่าการอนุญาต" หลักการ :

def delta(*numbers):
    try:
        return max(numbers) - min(numbers)
    except TypeError:
        raise ValueError("delta should only be called with numerical arguments") from None
    except ValueError:
        raise ValueError("delta should be called with at least one numerical argument") from None

ตัวอย่างเช่น:

>>> delta()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5

หากคุณไม่ต้องการเพิ่มข้อยกเว้นเมื่อdeltaถูกเรียกโดยไม่มีข้อโต้แย้งใด ๆ คุณสามารถส่งคืนค่าบางอย่างที่ไม่สามารถเป็นไปได้ (เช่น-1หรือNone):

>>> def delta(*numbers):
...     try:
...         return max(numbers) - min(numbers)
...     except TypeError:
...         raise ValueError("delta should only be called with numerical arguments") from None
...     except ValueError:
...         return -1 # or None
... 
>>> 
>>> delta()
-1

0

นี่เป็นสไตล์ที่ถูกต้อง / เชื่อถือได้หรือมี gotchas เกี่ยวกับเรื่องนี้หรือไม่?

ฉันอยากจะเพิ่มคำถามนี้ว่าไม่เพียง แต่ถูกต้องและเชื่อถือได้ แต่ยังใช้งานได้จริงอีกด้วย นี่คือตัวอย่างง่ายๆ:

>>>example_list = []
>>>print example_list or 'empty list'
empty list

ดังนั้นคุณสามารถใช้ประโยชน์ได้อย่างแท้จริง เพื่อให้เป็นที่ยอมรับนี่คือสิ่งที่ฉันเห็น:

Or ตัวดำเนินการ

orตัวดำเนินการของ Python ส่งคืนค่า Truth-y แรกหรือค่าสุดท้ายและหยุด

And ตัวดำเนินการ

andตัวดำเนินการของ Python จะส่งคืนค่า False-y แรกหรือค่าสุดท้ายและหยุด

เบื้องหลัง

ใน python ตัวเลขทั้งหมดจะถูกตีความว่าTrueยกเว้น 0 ดังนั้นการพูดว่า:

0 and 10 

เหมือนกับ:

False and True

ซึ่งเห็นได้ชัดFalseว่า ดังนั้นจึงเป็นตรรกะที่ส่งคืน 0


0

ใช่. นี่คือพฤติกรรมที่ถูกต้องของและการเปรียบเทียบ

อย่างน้อยก็ในหลามA and BผลตอบแทนBถ้าAเป็นหลักTrueรวมทั้งถ้าAไม่ว่างไม่ได้Noneไม่ได้เป็นภาชนะที่ว่างเปล่า (เช่นที่ว่างเปล่าlist, dictฯลฯ ) Aส่งคืน IFF Aเป็นหลักFalseหรือNoneหรือว่างเปล่าหรือ Null

บนมืออื่น ๆ , A or BผลตอบแทนAถ้าAเป็นหลักTrueรวมทั้งถ้าAไม่ว่างไม่ได้Noneไม่ได้เป็นภาชนะที่ว่างเปล่า (เช่นที่ว่างเปล่าlist, dictฯลฯ ) Bมิฉะนั้นผลตอบแทน

เป็นเรื่องง่ายที่จะไม่สังเกตเห็น (หรือมองข้าม) พฤติกรรมนี้เนื่องจากใน Python non-nullอ็อบเจ็กต์ที่ไม่ว่างเปล่าใด ๆที่ประเมินเป็น True จะถือว่าเป็นบูลีน

ตัวอย่างเช่นข้อความต่อไปนี้จะพิมพ์ "True"

if [102]: 
    print "True"
else: 
    print "False"

if "anything that is not empty or None": 
    print "True"
else: 
    print "False"

if {1, 2, 3}: 
    print "True"
else: 
    print "False"

ในทางกลับกันข้อความต่อไปนี้ทั้งหมดจะพิมพ์ "False"

if []: 
    print "True"
else: 
    print "False"

if "": 
    print "True"
else: 
    print "False"

if set ([]): 
    print "True"
else: 
    print "False"

ขอขอบคุณ. ผมอยากจะเขียนเป็นหลักA Trueแก้ไขแล้ว.
emmanuelsa

0

เข้าใจง่ายๆ

และ: if first_val is False return first_val else second_value

เช่น:

1 and 2 # here it will return 2 because 1 is not False

แต่,

0 and 2 # will return 0 because first value is 0 i.e False

และ =>ถ้าใครเท็จก็จะเป็นเท็จ ถ้าทั้งสองเป็นจริงแล้วมันจะกลายเป็นจริงเท่านั้น

หรือ : if first_val is False return second_val else first_value

เหตุผลคือถ้าอันดับแรกเป็นเท็จให้ตรวจสอบว่า 2 เป็นจริงหรือไม่

เช่น:

1 or 2 # here it will return 1 because 1 is not False

แต่,

0 or 2 # will return 2 because first value is 0 i.e False

หรือ =>ถ้าใครเท็จก็จะเป็นจริง ดังนั้นหากค่าแรกเป็นเท็จไม่ว่าค่า 2 จะเป็นเท่าใดก็ตาม ดังนั้นมันจึงคืนค่าที่สองเท่าที่เคยมีมา

ถ้าใครพูดจริงก็จะกลายเป็นจริง ถ้าทั้งสองเป็นเท็จมันจะกลายเป็นเท็จ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.