CS61A: Priority of Boolean Operators: and, or and not

<a href="http://cs61a.org/lab/lab01/">Lab</a>の問題よりBoolean関数について優先順位があったこと自体知らなかったので備忘録の意味でメモ。

#`and`, `or` and `not`

pythonではこの3つの`Boolean Operator`をサポートしている。

>
_`and` evaluates to True only if both operands evaluate to True. If at least one operand is False, then and evaluates to False. `or` evaluates to True if at least one operand evaluates to True. If all operands are False, then or evaluates to False._

と書いてあるがTrue/Falseの値については[「プログラマの数学」2章メモ#2 論理積、論理和、ド・モルガンの法則、ベン図、カルノー図、3値論理など](http://qiita.com/weedslayer/items/c87ee0956a20ae9975f3)でより詳細に書いたのでそちらを参考にしたほうが分かりやすいかもしれない。

#Priority

結論から言うと優先順位は`not > and > or`という順番らしい。よってたとえば`True and not False or not True and False`をevaluationすると`(True and (not False)) or ((not True) and False)`となるはずだ。ちなみにこれは`True`判定になるはず。詳しくは[ここ](http://stackoverflow.com/questions/16679272/priority-of-the-logical-statements-not-and-or-in-python)を参考に。

#Truthiness

Pythonの大きな特徴として「全てのオブジェクトを`True`もしくは`False` valueとして解釈することが出来る」というものがある。例えば`while`や`if`は`True`判定が出て初めて処理を始める。ということであらゆる種類のデータを扱うときに「どのようにしてTruthiness（=the quality of being felt to be true, even if not necessarily true）がpython内で定義されているのか」を知るのは非常に大切。

以下の関数を使えば簡単に調べることが可能。

```
def truthChecker(some_value):
    if some_value:
        print("YAY")
    else:
        print("NAY")

def noneChecker(some_value):
    if some_value is not None:
        print("YAY")
    else:
        print("NAY")

>>> checks_for_truth("apples"), checks_for_none("apples")
YAY!
YAY!
>>> checks_for_truth([]), checks_for_none([])
BOO!
YAY!
>>> checks_for_truth(""), checks_for_none("")
BOO!
YAY!
>>> checks_for_truth(15), checks_for_none(15)
YAY!
YAY!
>>> checks_for_truth(0), checks_for_none(0) # okay, seeing a pattern
BOO!
YAY!
>>> checks_for_truth(-10), checks_for_none(-10)
YAY!
YAY!
>>> checks_for_truth(None), checks_for_none(None)
BOO!
BOO!
```

要点だけまとめると

| literals | Falthy? | None? |
|:-----------:|:------------:|:------------:|
| `[]`       |        yep |     nope     |
| `""`     |      yep |    nope    |
| `0`       |        yep |     nope     |
| `None`         |          yep |      yep      |

Falsey valueだからといって必ずしもFalseを返すわけではないということ。

```
>>> [] and 1/0
[]

```
最初の`[]`が`False`の時点で`[]`が全体の値となる。`1/0`をevaluateする前に`[]`を返してしまっているのでエラーを吐かない。詳しくは一番下を参考に。

#Short-cuitという名の"Laziness"

```
>>> 'a' == ('a' or 'b')
True
>>> 'b' == ('a' or 'b')
False
>>> 'a' == ('a' and 'b')
False 
>>> 'b' == ('a' and 'b')
True
```

個人的に一見直感に反する答えが出てきたと思ったがよーく考えると納得がいく。

##全体の流れ

ざっくり言うとこんな感じ。

![Screen Shot 2016-02-09 at 9.48.40 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/1643edf7-a3e5-12a7-6e75-a4e0493e8b1c.png)

![Screen Shot 2016-02-09 at 9.49.14 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/6482261f-22d0-a620-7b16-513388d576a2.png)

要点は以下のとおり:

>
_The expression x and y first evaluates x; if x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned._
>
_The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned._
>
_(Note that neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument. This is sometimes useful, e.g., if s is a string that should be replaced by a default value if it is empty, the expression s or 'foo' yields the desired value. Because not has to create a new value, it returns a boolean value regardless of the type of its argument (for example, not 'foo' produces False rather than ''.)_

1. 括弧内の値を`bool()`してTrue/False判定を行う。以下の表に従ってまず括弧内の値がそもそもTrue/Falseかを確かめる。
2. `and`と`or`でそれぞれ処理を行う。
3. 返ってきた値を左側の値と比較して（上の例題に従えば`==`なので）True/Falseで値を出す。

| True | False | 
|:-----------:|:------------:|
| 1       |        0 |
| #s other than 0    |  The string 'None' | 
| Nonempty strings       |        Empty strings | 
| Nonempty lists        |          Empty lists | 
| Nonempty dictionaries        |          Empty dictionaries | 

###`or`expressionの動き

![Screen Shot 2016-02-09 at 8.46.56 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/00f19c6e-ff99-1038-8673-1411a0bfc013.png)


1. 最初の（括弧内の）ひとつ目の値をとりTrue/False判定を行う。`True`の場合は2へ。`False`の場合は3へ。
2. （1つめがTrueの場合）は残りの値をチェックせずに1つ目の値をそのまま返す。理由は`or`expressionの特徴より（片方の値が`True`ならば全体も`True`になるからである）。（例: `'a' == (True or False)`は最初の値`True`で`True`判定を出すので`True`が括弧内全体の値として返される。よって`'a' == True`が`False`となり`False`を返す）
3. （1つめがFalseの場合）は2番目と進みどこかで`True`を見つけた瞬間2に戻る。全て`False`の場合はShort-circuitingにより括弧全体の値がexpressionの中の（true or false関係なく）右の値となる。詳しくは一番下の追記を参考に。


```
>>> False is ('a' == ('' or  None or 0)) # ('' or  None or 0)全体の値がFalseなので'a' == False (which returns False)は究極False is Falseと同じなのでTrueになる
True
>>> False is ('a' == ('' or  None or 'a')) # ('' or  None or 'a')全体の値が'a'なのでFalse is ('a' == 'a')つまりFalse is Trueとなり結果Falseとなる。
False


```

>_if the first value is evaluated as false Python checks the second half and returns that value. That second half determines the truth value of the whole expression since the first half was false._

この振る舞いを`short circuit`といってpythonがブーリアン値に対して行う反応らしい。

>_This "laziness" on the part of the interpreter is called "short circuiting" and is a common way of evaluating boolean expressions in many programming languages._

```
>>> 'a' == (False or 'a')
True
```

###`and`expressionの動き

![Screen Shot 2016-02-09 at 8.43.03 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/79179869-d69e-5583-cd1c-a48a86504184.png)


`and`expressionも似たような動きをする。
1. 括弧内の一つ目の値が`True`判定でも（`or`のようにすぐ`True`判定を出さずに）残りの値全てをチェックする。これは`and`expression特有の動き（`and`は両方共`True`にならないと`True`値を返さないため）。どこかで`false`値を返した瞬間、残りを調べずに全体を`False`として返す。
2. 括弧内の値がすべて`True`判定になった場合は最後の値を全体の値として返す。

```
>>> False is ('a' == ('a' and 1 and 9 and 'b'))
True
>>> False is ('a' == ('a' and 1 and 9 and 'a'))
False

'a' == ('a' and 1 and 2 and 'c' and 'd' and None and 'a') #NoneはFalse valueなので`a`を調べずに括弧内全体の値をFalseとして返す。
False
'a' == ('a' and 1 and 2 and 'c' and 'd' and 'a') #全部True valueなので最後の'a'が括弧内全体の値として返される。
True
```

###`or`と`and`まとめ

1. `or`は括弧内のいづれかが`True`だった場合はその`True`判定を出した値が括弧全体の値として出される。括弧内の値全てが`False`判定を下した場合は自動的に`False`となる（=Short-Circuiting）
2. 
`and`は1番目が`True`ならば2番目に行く...（最後まで確認しに行く）。全て`True`ならば括弧内の最後の値（一番右）を返す。括弧内の内いづれかが`False`の時点で全体の値が（True/False関係なく）一番左の値が全体のexpressionの値となる。（=Short-circuting）。

```
'a' == ('' or None or '' or 0 or [] or '' or 'a')
True
'a' == ('' or None or '' or 0 or [] or '' or 'b' or {}) # 左から順番に見ていき'b'がTrue値だと分かったので{}を判定せずに'b'を全体の値として出す。
False
'a' == ('a' and 'a' and 'a')
True
'a' == ('a' and 'a' and '')
False
'a' == ('a' and 'a' and False)
False
'a' == ('a' and 'b' and True and True) #since 'a' == True returns False
False
'a' == ('a' and 1 and 'a' and 9 and 9)
False
'a' == ('a' and 1 and 'a' and 9 and 'a')
True
```

`and`の場合は括弧の中に`False`値が入ってる時点で速攻で`False`判定を返す。`or`は最初の一個が`True`ならが他関係なくその最初の値が全体の値として持っていかれる

ではこれは何を返すだろうか？

```
>>> 'a' == ('a' or None and 0)
```

上で紹介したPriorityにより`None and 0`を先にTrue/False判定する。結果Falseの値が返ってきて`'a' == ('a' or False)`となり`True`が返ってくる。

```
>>> 'a' == ('a' or None and 0)
True
```

この理論に従えば

```
>>> 'a' == ('b' or None or 0)
False
```
となるはず。

ちなみに

```
>>> 'a' == ('b' and 'a' or 0)
True
```

ということは

```
>>> 'a' == ('a' and 'b' or 0)
False
```

になるのも容易にわかるはず。

#追記

<a href="http://cs61a.org/lab/lab01/" target="_blank">学校のラボの宿題</a>を解いていて疑問に思ったことと、先生からのレスポンスをまとめておく。

```
>> False or 0 
0
how come 0 evaluates to True when bool(0) evaluates to False?
```
「0の値は`bool(0) # False`なので上の問題は一般化すれば`False or False`。よって返ってくる値は`0`ではなくて`False`になるだろう」というのが俺の予想だったのですが、どうやら間違っていたようです。

>_Take this longer expression for example:_
> 
_False or False or False or 0 
Once it gets to the last value - the value of the entire expression is dependent on it. If the last value is "truthy" (meaning bool(value) is True) - then the entire expression is "truthy". If the last value is falsey - then the entire expression is falsey. So with that in mind, python can just use the last value directly._
>
 _Here's another way to think about it: 
Is False Truthy? Nope. 
Is the second False Truthy? Nope. 
Is the third False Truthy? Nope.  I've gotten to the last value - well there's no point figuring out if this is truthy or not. At this point the entire expression is dependent on this last value. Might as well just return that as the value. _

どうやら`or`の時は最後以外全ての値が`False`となると最後の値のTruthiness/Falseinessに関係なく最後の値を出すようになっているらしいです。

例えば

```
>>> print(False or False or False or None)
None
```

`False`判定されてそれがこの`expression`の値になると思っていたのですがどうやら__最後の値__を返り値として出すようです。

```
>>> s = ""
>>> s or "foo"
'foo'
>>> s and "foo"
''
```

#参考にしたリンク

- [Truthiness in Python](https://www.udacity.com/wiki/cs258/truthiness-in-python)
- [Non-Programmer's Tutorial for Python 3/Boolean Expressions](https://en.wikibooks.org/wiki/Non-Programmer%27s_Tutorial_for_Python_3/Boolean_Expressions)
- [Booleans, True or False in Python](http://www.pythonforbeginners.com/basics/boolean)
- [6. Expressions](https://docs.python.org/3/reference/expressions.html)