跟著 Ramda 學 FP - 邏輯運算(3)
ramda and or both either not complement xor -不管是前面提到的 ifElse
或處理陣列的 takeWhile
,都有一個判斷函式。
Ramda
提供一系列對邏輯運算操作的函式。整理如下,
value |
func |
|
&& |
and |
both |
|| |
or |
either |
! |
not |
complement |
^ |
xor |
How
在函式風格的程式撰寫中,通常盡量使用表達式(Expression)而不是陳述式(Statement)來達成目的。
當然,沒必要強迫自己一定要全部使用表達式。
表達式最大的一個特徵就是它代表一個值。而這對 FP
特別有用。
函式的所有參數都必須是表達式,這也就是說每一個輸入參數都必須代表一個值。一個函式也是一個值。
因為 Ramda
提供邏輯運算的函式,所以我們能將邏輯運算的陳述式改為表達式來撰寫。
and
比較 and
和 both
簽名,
and :: a -> b -> a | b both :: (*… -> Boolean) -> (*… -> Boolean) -> (*… -> Boolean)
從上可以看出 and
直接返回結果,但 both
返回的是函式。
and
其實就是 &&
的封裝。可以像下面這樣寫,
const and = curryN(2, (a, b) => a && b)
簡單比較兩種寫法,如下
const a = true const b = true // Statement if (a && b) { result = true } else { result false } // Expression result = and(a, b)
可以看出表達式地寫法因為直接傳回值的關係,該值可以直接提供給下一個函式使用。 而陳述式地寫法則必須有一個可變的變數。
而優點不只是得到一個乾淨的不可變的變數。
Ramda
提供的函式大部分都是支援 curry
的,所以可以像下面這樣來使用,
const father = true const parents = and(father) // ... parents(mother)
不需要同時給兩個參數,而是先使用一個參數將他變成一個函式,等待之後需要時在使用。
or
or
跟 and
是一樣的簽名。等效如下,
const or = curryN(2, (a, b) => a || b)
both
同樣的,and
判斷的是兩個值, both
卻是要求要吻合兩個判斷式。
從 both
延伸有一個更通用的函式 allPass
,比較兩者的簽名如下,
both :: (*… -> Boolean) -> (*… -> Boolean) -> (*… -> Boolean) allPass:: [(*… -> Boolean)] → (*… -> Boolean)
兩者都傳回函式 (*… -> Boolean)
,both
要求兩個判斷式都為 true
才傳回 true
,
而 allPass
接受一串判斷式的陣列,必須陣列內的所有判斷式都為 true
才得到 true
。
either
either
的簽名與 both
相同。要求是任一判斷式為 true
就傳回 true
。
同時也有更通用的函式 anyPass
,只要陣列內的任一判斷式為 true
就傳回 true
。
所以你可以基於日後修改的方便性使用 allPass
替代 both
或用 anyPass
替代 either
。
當然也可以因為效能上的考量(其實差異不大)使用 both
或 either
。
這兩組的差異,除上述外,更重要的是 both
或 either
支援 functor
。
這代表你可以使用他們來比較兩個陣列,因為陣列也是一個 functor
。
both([false, false, 'a'], [11])
//=> [false, false, 11]
either([false, false, 'a'], [11])
// => [11, 11, "a"]
上面的例子,等效於
const both = lift(and)
// lift(and)([false, false, 'a'], [11])
// => [and(false, 11), and(false, 11), and('a', 11)]
// => [false, false, 11]
const either = lift(or)
// lift(or)([false, false, 'a'], [11])
// => [or(false, 11), or(false, 11), or('a', 11)]
// => [11, 11, "a"]
and
和 or
都是短路的。他們會傳回最後的結果。
透過 lift
最後能得到一個二維的結果。示意如下,
both:
both |
false |
false |
'a' |
11 |
false |
false |
11 |
either:
either |
false |
false |
'a' |
11 |
11 |
11 |
'a' |
如果你給的是一個 3 元素陣列跟 2 元素陣列,最後得到就是一個 6 元素陣列。
not, complement
not
等效方法如下,
// not :: * -> Boolean
const not = (a) => !a
比較出人意外的 complement
的等效方法是這樣寫的,
const complement = lift(not)
這是因為函式也是一個 functor
。
not
接受一個參數,透過 lift
就等效將函式的結果進行 not
。
xor
這在 Ramda
與 js
的對照中是比較特殊的,因為 Ramda
提供的 xor
函式,並無法進行位元運算。
由他的簽名可以很清楚的看到和 and
或 or
不同。
and :: a -> b -> a | b xor :: a -> b -> Boolean
xor
最後是傳回一個 Boolean
值,而不是 a | b
。
相對的 js
中並未提供做純邏輯判斷的 xor
算子,而 ^
其實是一個位元算子,只是在某些情狀下會被用來當邏輯算子用。