成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

多變量邏輯表達(dá)式化簡原理與應(yīng)用:卡諾圖化簡法

開發(fā) 前端
我們簡要介紹了卡諾圖原理與其化簡方法,這個(gè)工具和思想可以用在任何多參數(shù)有競爭或多種情況的場景下決定某個(gè)操作是否執(zhí)行。這個(gè)方法在參數(shù)較多且狀態(tài)復(fù)雜時(shí)使用,若邏輯狀態(tài)較簡單,則可直接用表達(dá)式寫出,代碼邏輯力求更清晰明了。

1、背景

本文主要介紹使用卡諾圖化簡多變量邏輯表達(dá)式的原理與方法,此方法是一種邏輯計(jì)算思想,在任意技術(shù)平臺(tái)類似的多元化場景中均可適用。

本文以客戶端的一個(gè)業(yè)務(wù)場景為例,從舉例分析到實(shí)際應(yīng)用的步驟,介紹卡諾圖工具的使用,讓我們輕松應(yīng)對(duì)復(fù)雜交互或多條件判斷的編碼。

2、使用場景舉例

開發(fā)中我們有時(shí)會(huì)遇到一些復(fù)雜條件的交互要求,它們共同操作公共控件,使得交互邏輯在流程中變得極為復(fù)雜,如何簡便高效地處理和維護(hù)這些判斷邏輯,是我們可以思考的方向。

先來看一個(gè)這樣的業(yè)務(wù)場景,為方便說明已稍作簡化。在一個(gè)拍攝頁面中有如下幾個(gè)控件:添加音樂按鍵,濾鏡按鍵,拍攝鍵,特效選擇器,特效清除鍵?(圖1.1 所示紅圈處),它們?cè)谂恼站途w狀態(tài)下是都顯示的,然后用戶可能的操作流程有2種:拍照片和拍視頻,而這2種操作可以分別有3個(gè)不同的效果選擇:增加音樂,增加濾鏡,增加特效,則此例中共有 2 * 3 = 6? 種不同操作方式的組合,這些操作又分別涉及不同的控件,需要它們配合完成相應(yīng)操作。即 m 種操作和 n 種效果,共 m*n 種組合方法。

而聰明的你則要處理它們?nèi)康慕换ミ壿嫛4隧撁娓嘣敿?xì)的交互要求可參考發(fā)布工具[拍攝頁] 交互要求 [1]。

圖片

圖 1.1  拍攝頁的不同狀態(tài)UI與交互效果示意

主要交互要求是什么呢?參考看著上圖,我們隨便舉幾個(gè)粟子好了,比如:

?? 已拍了照片或視頻不能顯示音樂鍵

?? 濾鏡浮層打開后要隱藏音樂鍵、拍攝鍵和特效選擇器

?? 關(guān)閉浮層后要把這些控件重新顯示出來

?? 有特效時(shí)要顯示清除鍵

還有其他更多細(xì)節(jié)限于篇幅就不羅列了。多樣化的操作帶來邏輯的復(fù)雜變化,比如視頻拍了一半,暫停來切換特效,而特效控件在有視頻和無視頻時(shí)展示的控件又不同,或刪掉視頻去選濾鏡、選音樂等等,是不是已經(jīng)暈了?這里面還有些其他可能想都想不到的細(xì)節(jié)出現(xiàn),由此足以看出交互要求的復(fù)雜性了。如圖1.1 所示,紅圈處為頁面在不同狀態(tài)下發(fā)生的主要變化。

使用響應(yīng)式編程或許可以解決部分問題,但由于每個(gè)控件狀態(tài)并不是由單一參數(shù)決定的,所以我們?nèi)孕枰谒鼈兏髯缘谋O(jiān)聽方法里寫全部相關(guān)參數(shù)的判斷邏輯,這模式對(duì)解決此場景的根本問題似乎并無太多幫助。主要問題出在哪兒呢?

2.1 痛點(diǎn)

一句話總結(jié)問題:不同的交互方法之間產(chǎn)生了冗余的先后依賴而阻礙了業(yè)務(wù)的狀態(tài)轉(zhuǎn)移,使邏輯不夠清晰,代碼難以維護(hù)。

怎么辦呢?你可能會(huì)說,我們可以按操作的內(nèi)容來分類,把控件按操作類別分裝入不同的父容器中,然后在相應(yīng)操作方法里控制這容器即可。

嗯想法很不錯(cuò),但實(shí)際這么做了然后我們會(huì)發(fā)現(xiàn),例如說在上述場景里,在關(guān)閉浮層時(shí),若已經(jīng)拍了一段視頻,則還需要單獨(dú)判斷音樂鍵的顯示狀態(tài),或是此時(shí)還沒選擇特效,就不能把清除鍵也顯示出來。

于是我們開始意識(shí)到,這個(gè)場景下的業(yè)務(wù)方法不能完全獨(dú)立出來使用。抽象來說,這些操作之間相互有順序的依賴,它們不能嚴(yán)格滿足邏輯表達(dá)式加法交換律,即某些情況 A操作 和 B操作 的順序不同會(huì)導(dǎo)致結(jié)果不同:

圖片

還有許多類似的corner case,我們?nèi)舭讶縞ase分列出來,則它們的控制邏輯會(huì)像星辰一樣散落在各個(gè)處理方法中,作為中介作用的容器也會(huì)層層疊疊地出現(xiàn),然后我們還需要寫一大坨處理各種子狀態(tài)、容器控件的方法,再根據(jù)當(dāng)前用戶操作去相應(yīng)地調(diào)用;當(dāng)頁面里的控件數(shù)量增多,或需求變化了,或控件要增多/減少時(shí),整個(gè)流程就不再具有清晰的層級(jí)關(guān)系或先后邏輯了。

更麻煩的是,我們每次看似簡單的增刪改操作,都要把相應(yīng)業(yè)務(wù)的上下游方法全部梳理一遍,甚至和其他功能有交互的地方,也要關(guān)注到,這樣的邏輯產(chǎn)生過高的復(fù)雜度也容易產(chǎn)生疏漏。要維護(hù)好這么一堆面條代碼將不是一件令人愉快的事;若是對(duì)業(yè)務(wù)不熟悉就做修改,極易踩坑,導(dǎo)致改了A處又壞了B處,修了B處又影響了C處,正是因?yàn)楦髯訕I(yè)務(wù)的方法存在過度耦合,出bug就沒完沒了。

那么我們還有更優(yōu)雅的方式來處理這種場景么?

2.2 邏輯狀態(tài)抽象

不如我們先換個(gè)角度來看。

通過觀察梳理業(yè)務(wù)要求[1]可發(fā)現(xiàn),其實(shí)我們并不需要知道用戶之前做過什么操作,因?yàn)榭丶慕换ブ慌c個(gè)別參數(shù)相關(guān),即交互是依賴于某個(gè)變量而非業(yè)務(wù)流程。我們?nèi)魧⑦@些參數(shù)用bool值來確定,就可以唯一決定當(dāng)前的頁面狀態(tài),并且這些狀態(tài)在一個(gè)交互中是相互獨(dú)立的,即當(dāng)前狀態(tài)與它之前的狀態(tài)無關(guān)。

也就是說:與其說當(dāng)前操作需要什么交互,不如說當(dāng)前操作發(fā)生時(shí),相關(guān)參數(shù)處于什么狀態(tài),再由這些狀態(tài)即可決定頁面所需的交互。

例如,當(dāng)濾鏡浮層dismiss時(shí),哪些控件需要重新顯示出來呢?那得看此時(shí)是否已有錄視頻,是否已有特效,是否已有音樂等等,這些參數(shù)綜合起來,就唯一決定了當(dāng)前的狀態(tài),就可計(jì)算出所需邏輯。

很棒,我們走到這里問題已解決一大半了。即引出本文主題:卡諾圖。

2.3 狀態(tài)化簡工具:卡諾圖

若是滿足上述這些條件,那么我們就可以使用邏輯表達(dá)式來方便地描述它們,而邏輯式的化簡就可以用卡諾圖方便地完成。

至此,我們就將問題由具體業(yè)務(wù)流程轉(zhuǎn)化為:如何使用關(guān)聯(lián)的變量建立邏輯式描述頁面的狀態(tài)。這樣一來就把狀態(tài)從流程中剝離出來了,你會(huì)發(fā)現(xiàn),從這個(gè)角度看問題,無論流程怎么變,所做操作最終都會(huì)落入某個(gè)狀態(tài),這才是我們要找的關(guān)鍵聯(lián)系:

相關(guān)變量 --(確定)--> 邏輯表達(dá)式 --(確定)--> 控件狀態(tài)

這個(gè)過程,也是我們計(jì)算狀態(tài)轉(zhuǎn)移的方法,只用上面3步,掌握之后再遇到類似場景,我們完全可以無腦按套路來處理邏輯狀態(tài)就ok了,讓代碼清爽簡潔,敏感肌也能用。

2.4 舉例使用

卡諾圖具體怎么用呢?

我們還是繼續(xù)看上面的例子,就寫一個(gè)方法 :func refreshCurrentStatusUI(),用于在任意時(shí)刻需要刷新頁面狀態(tài)時(shí)獲得所需狀態(tài),而不用關(guān)心具體當(dāng)前的操作或流程是什么,以便把狀態(tài)從業(yè)務(wù)流程中獨(dú)立出來。

方法結(jié)構(gòu)也是最基本的思路,方法有格式如下:

func refreshCurrentStatusUI()    // 獲取當(dāng)前變量狀態(tài)    let A: Bool = isA()    let B: Bool = isB()    let C: Bool = isC()    let D: Bool = isD()    ...    // 設(shè)置控件交互    view01.isHidden = ABCD表達(dá)式1    view02.isHidden = ABCD表達(dá)式2    view03.isHidden = ABCD表達(dá)式3    ...}

在實(shí)際應(yīng)用的方法體里,把與業(yè)務(wù)場景相關(guān)的參數(shù)狀態(tài)列出來,比方說,獲取與羅列的控件相關(guān)的幾個(gè)必需變量,為了表述方便,這里用符號(hào)ABC來代表參數(shù),當(dāng)然你也可以用其他的符號(hào)。

各參數(shù)說明如下:

A:是否有視頻

B:是否有特效

C:是否有子特效

D:是否已拍照填充了子特效

F:是否有音樂

按上述方式填充方法體,有幾個(gè)參數(shù)你就列出幾個(gè),簡單又直觀:

func refreshCurrentStatusUI()    // 當(dāng)前數(shù)據(jù)狀態(tài)    let hasVideo: Bool = isProgressHasSections()           // A: 是否已錄制視頻    let hasEffect: Bool = hasSpecialEffect()               // B: 是否已選有特效    let hasSubEffect: Bool = hasSubStickers()              // C: 是否帶有子特效    let hasSubEffectPhoto: Bool = hasSubStickersGetPhoto() // D: 是否已拍照填充了子特效 subEffect    let hasMusic: Bool = currentMusicData != nil || musicID > 0 // F: 是否有音樂...}

有一點(diǎn)細(xì)節(jié)需要注意的是:上述變量用到的值一定要在方法體里即用即取,不建議從方法外傳值進(jìn)來,因?yàn)槎嗑€程和并發(fā)的存在,則有的屬性變量值可能在本方法開始調(diào)用后被其他線程或方法修改,即狀態(tài)已發(fā)生了改變,而傳入的值狀態(tài)就已經(jīng)滯后,導(dǎo)致本方法計(jì)算出的控件狀態(tài)不是預(yù)期最新的狀態(tài)了。

然后我們只需要繼續(xù)在這個(gè)方法里,使用上述變量當(dāng)下的值,計(jì)算每個(gè)控件狀態(tài)的邏輯表達(dá)式,根據(jù)結(jié)果來刷新各控件狀態(tài)即可:

// 拍攝鍵takeButton.isHidden          = !(hasVideo || hasSubEffectPhoto)
// [刪除視頻片段]按鍵deleteButton.isHidden = !(hasVideo || hasSubEffectPhoto)
// 特效選擇器specialEffectPicker.isHidden = hasVideo || hasSubEffectPhoto
// 特效浮層鍵stickerSelectionButton.isHidden = !hasVideo && (hasEffect || hasSubEffect) || hasSubEffectPhoto // (!A)(B+C)+D
// 特效清除鍵specialEffectClearButton.isHidden = !hasEffect || (hasSubEffect || hasSubEffectPhoto) // !B || (C || D)

這樣一來,即使后面需要增加更多控件,或增加邏輯參數(shù),都可以在這個(gè)方法里統(tǒng)一處理,然后在需要刷新頁面的地方調(diào)用,而我們不需要關(guān)心具體操作發(fā)生時(shí)頁面的上一個(gè)狀態(tài)。到這里,業(yè)務(wù)方法就大功告成了,我們保證了在任意操作流程中交互的狀態(tài)都是可唯一確定的。

OK 下一步,邏輯表達(dá)式要怎么做呢?

3、卡諾圖簡介

卡諾圖[2]可以用于表示和化簡邏輯函數(shù)。

一個(gè)邏輯函數(shù)的卡諾圖,就是將此函數(shù)的最小項(xiàng)表達(dá)式中的各最小項(xiàng)填入相應(yīng)的特定方格圖內(nèi),這樣的方格圖就是卡諾圖。

舉例,比如 4變量的卡諾圖,記ABCD?為所用4個(gè)變量,行與列頭為4個(gè)變量分別對(duì)應(yīng)的取值(true 或 false,對(duì)應(yīng)記為邏輯 1 或 0 方便計(jì)算),16個(gè)方格中相鄰的數(shù)字0~15依順序代表方格邏輯相鄰:

圖片

圖3.1  卡諾圖邏輯相鄰格式

3.1 代數(shù)含義

一個(gè)邏輯函數(shù),如果有 n 個(gè)變量,則有$$2^n$$個(gè)最小項(xiàng),最小項(xiàng)為所有所有變量的積,也就是所有變量的與門邏輯。

3.2 特點(diǎn)

  • 邏輯函數(shù)有幾個(gè)變量,就有幾個(gè)因子
  • 每個(gè)變量都是它的一個(gè)因子
  • 每一變量或以原變量形式出現(xiàn)(A、B、C)?,或以非變量形式出現(xiàn)(!A、!B、!C)
  • 每個(gè)乘積的組合僅出現(xiàn)一次,且取值為1
  • 任何邏輯函數(shù)都可以轉(zhuǎn)換成最小項(xiàng)的形式

3.3 相鄰組合

任何一對(duì)相鄰最小項(xiàng)可以組合為比原最小項(xiàng)本身少一個(gè)變量的單項(xiàng)。

3.4 邏輯相鄰

  • 最上面的一列和最下面的一列邏輯相鄰,可以相互化簡
  • 最左邊一列和最右邊的一列邏輯相鄰,可以相互化簡
  • 四個(gè)角邏輯相鄰,可以相互化簡
  • 要更好地理解上述規(guī)則和邏輯相鄰,我們需要使用格雷碼。

4、格雷碼編碼規(guī)則

4.1 格雷碼原理

畫卡諾圖的時(shí)候需要先將所有變量可能以格雷碼的形式排列在方格兩側(cè),所有變量有$$2^n$$個(gè),格雷碼的基本特點(diǎn)就是任意兩個(gè)相鄰的代碼只有一位二進(jìn)制數(shù)不同,主要用于在數(shù)字電路中變化時(shí)每次就只有一位發(fā)生變化,用以提高電路的穩(wěn)定性[3]。

十進(jìn)制數(shù)

二進(jìn)制數(shù)

格雷碼


十進(jìn)制數(shù)

二進(jìn)制數(shù)

格雷碼

0

0000

0000


8

1000

1100

1

0001

0001


9

1001

1101

2

0010

0011


10

1010

1111

3

0011

0010


11

1011

1110

4

0100

0110


12

1100

1010

5

0101

0111


13

1101

1011

6

0110

0101


14

1110

1001

7

0111

0100


15

1111

1000

表3.1  格雷碼編碼規(guī)則

4.2 格雷碼規(guī)則

自然二進(jìn)制數(shù)到格雷碼:保留二進(jìn)制碼的最高位作為格雷碼的最高位,而次高位格雷碼為二進(jìn)制碼的高位與次高位相異或,而格雷碼其余各位與次高位的求法相類似。

格雷碼到自然二進(jìn)制數(shù):保留格雷碼的最高位作為自然二進(jìn)制碼的最高位,而次高位自然二進(jìn)制碼為高位自然二進(jìn)制碼與次高位格雷碼做相異或計(jì)算,而自然二進(jìn)制碼的其余各位與次高位自然二進(jìn)制碼的求法相類似。

以一個(gè)四位二進(jìn)制數(shù)來舉例,二進(jìn)制(abcd),依據(jù)規(guī)則轉(zhuǎn)換為格雷碼就是

圖片

即異或計(jì)算【a, (a^b), (b^c), (c^d)】, 依據(jù)規(guī)則繼續(xù)轉(zhuǎn)化二進(jìn)制的話就是 【a, (a^a^b), (a^a^b^b^c), (a^a^b^b^c^c^d)】,化簡之后仍然可以得到(abcd)。

5、卡諾圖簡單邏輯化簡

5.1 化簡方法

邏輯化簡的實(shí)際目標(biāo)是盡可能地減少表達(dá)式中包含的項(xiàng)數(shù)以及各項(xiàng)包含的變量數(shù)[4]。下圖為三變量卡諾圖結(jié)構(gòu),m0~m7表示變量ABC的8種狀態(tài)[7]。

圖片

圖5.1  三變量卡諾圖

此圖即為基本卡諾圖的形式,兩側(cè)變量依據(jù)格雷碼形式,目的就是畫卡諾圈時(shí)要將里面的 1 全都包括在內(nèi)[5]。

類似的,有4變量卡諾圖描述16種狀態(tài),其結(jié)構(gòu)如下所示:

圖片

圖5.2  四變量卡諾圖

卡諾圖填寫完成后,就可以作卡諾圈了,通過圈選與合并狀態(tài),我們可以將邏輯多項(xiàng)式化簡得到其最小項(xiàng)表達(dá)式。

5.2 卡諾圈原則

卡諾圈原則:所作卡諾圈盡量大,卡諾圈的數(shù)量盡量少

  • 卡諾圖上處在相鄰、相對(duì)、相重位置的小方格所代表的最小項(xiàng)為相鄰最小項(xiàng)。
  • 兩個(gè)小方格相鄰, 或處于某行(列)兩端時(shí),所代表的最小項(xiàng)可以合并,合并后可消去一個(gè)變量。
  • 四個(gè)小方格組成一個(gè)大方格、或組成一行(列)、或處于相鄰兩行(列)的兩端、或處于四角時(shí),所的表的最小項(xiàng)可以合并,合并后可消去兩個(gè)變量。
  • 八個(gè)小方格組成一個(gè)大方格、或組成相鄰的兩行(列)、或處于兩個(gè)邊行(列)時(shí),所代表的最小項(xiàng)可以合并,合并后可消去三個(gè)變量。
  • 對(duì)于方格中帶有未知變量x的,是可圈可不圈的,依據(jù)自己實(shí)際情況而定[6]。

5.3 化簡舉例與步驟

用上面拍攝頁的例子,我們?cè)趺从媚菐讉€(gè)變量,得到最終的交互結(jié)果呢?一起按步驟一步步來看。

5.3.1 抽出變量

各變量已有對(duì)應(yīng)的取值方法,為方便使用變量,我們計(jì)算中用ABC符號(hào)來代表各參數(shù),比如 A 代表是否已錄視頻,取0為false,1為true;其他變量同理,可寫出:

let hasVideo          // A: 是否已錄制視頻let hasEffect:        // B: 是否已選有特效let hasSubEffect:     // C: 是否帶有子特效let hasSubEffectPhoto // D: 是否已拍照填充了子特效 subEffectlet hasMusic          // F: 是否有音樂

然后拿出需要計(jì)算的控件,先用一個(gè)邏輯比較簡單的,比如拍攝鍵 takeButton? ,然后把與之相關(guān)的變量找出來,按照業(yè)務(wù)要求得知,它的顯示與隱藏只和“是否有視頻”、“是否填充子特效”這兩個(gè)參數(shù)有關(guān),當(dāng)然你也可以把看似相關(guān)的其他參數(shù)帶進(jìn)去,在化簡完成后,無關(guān)參數(shù)最終會(huì)被消去。

5.3.2 填充卡諾圖

那么,與拍攝鍵相關(guān)的變量就是 hasVideo,hasSubEffectPhoto? ,對(duì)應(yīng)符號(hào)是 A,D ,分別取0和1值。在下圖中,A為行,B為列,填充到2變量的卡諾圖中:

圖片

圖5.3  初始化卡諾圖

其中行代表A的取值,列為B的取值情況,它們相交處共同描述了AB全部4種取值:00,01,10,11。

然后,對(duì)應(yīng)每一種取值情況,我們需要思考 拍攝鍵 takeButton? 的顯示狀態(tài),并在相應(yīng)空格里填入0或1,含義由我們來定,這里取0為隱藏,1為顯示較直觀,那么有:

A = 0,B = 0 ?-->無視頻 & 無填充子特效時(shí),拍攝鍵要隱藏,所以取0,則在坐標(biāo)(0,0)處填0;

A = 0,B = 1 ?-->無視頻 & 有填充子特效時(shí),拍攝鍵要顯示,所以取1,則在坐標(biāo)(0,1)處填1;

A = 1,B = 0 ?-->有視頻 & 無填充子特效時(shí),拍攝鍵要顯示,所以取1,則在坐標(biāo)(1,0)處填1;

A = 1,B = 1 ?-->有視頻 & 有填充子特效時(shí),拍攝鍵要顯示,所以取1,則在坐標(biāo)(1,1)處填1;

圖片

圖5.4  填寫狀態(tài)后的卡諾圖

5.3.3 畫卡諾圈

根據(jù)上面2.4邏輯相鄰的最小項(xiàng)原則和4.1提到的畫圈原則,把相鄰的 1 圈起來,得到全部的卡諾圈:

圖片

圖5.5  畫出卡諾圈

這個(gè)圖里我們得到2個(gè)卡諾圈,全部卡諾圈表達(dá)式相加,對(duì)應(yīng)寫成邏輯式即得:

A + B

已是最簡表達(dá)式,再把符號(hào)AB再換回代碼中的變量,有:

(hasVideo || hasSubEffectPhoto)

因?yàn)榇a中使用的是 .isHidden?屬性,只要把所得表達(dá)式再取反即有 !(A+B),寫為代碼的邏輯式就是:

takeButton.isHidden = !(hasVideo || hasSubEffectPhoto)

至此我們就得到了這個(gè)控件的狀態(tài)表達(dá)式,在任何時(shí)候調(diào)用這個(gè)刷新方法,就只依賴參數(shù)做顯示。

5.3.4 四變量卡諾圖舉例

你可能覺得,上面的變量就兩個(gè),我自己列舉一下也可寫出表達(dá)式,不用走卡諾圖。是的恭喜你已發(fā)現(xiàn)這個(gè)規(guī)律,一般2變量我們可以輕易handle,直接計(jì)算可能會(huì)比畫圖來的簡單。但通過這個(gè)做法我們了解其原理與概念之后,現(xiàn)在一起來看個(gè)稍微復(fù)雜些的,4變量場景的表達(dá)式是如何計(jì)算的。

就用 特效浮層鍵 的交互狀態(tài)來試試吧,對(duì)應(yīng)圖1.1中屏幕左下方那個(gè)半透明白色圓形的按鍵,在就緒狀態(tài)或視頻暫停狀態(tài)下它會(huì)出現(xiàn)在拍攝鍵左側(cè):

圖片

圖5.6  特效浮層按鍵示意

同樣先來看下計(jì)算后的最終結(jié)果:

// 特效浮層鍵stickerSelectionButton.isHidden    = !hasVideo && (hasEffect || hasSubEffect) || hasSubEffectPhoto // (!A)(B+C)+D

思考:它在什么情況下需要顯示或隱藏呢?由業(yè)務(wù)要求[1]可知:是否錄視頻,是否有特效、子特效,是否拍了子特效照片,這幾個(gè)參數(shù)就能決定其狀態(tài)。

于是你問,那音樂參數(shù)F呢?我們通過研究具體需求[1]得知,無論有無音樂,都不會(huì)影響它的顯示,因此F是個(gè)無關(guān)參數(shù),計(jì)算時(shí)不用考慮。當(dāng)然,你非要把它加進(jìn)來也行,因?yàn)檫壿嬍交喓螅瑹o關(guān)參數(shù)都會(huì)被消去,不影響結(jié)果,這也是“最小項(xiàng)”這個(gè)名字的由來:計(jì)算出最少相關(guān)的參數(shù)。要知道,參數(shù)越少就越方便我們化簡邏輯式,所以那些一眼看去就無關(guān)的參數(shù)可以不用加進(jìn)來。若是計(jì)算后發(fā)現(xiàn)我們少帶了某個(gè)參數(shù)或業(yè)務(wù)要求變化了,則再把它加進(jìn)來重新計(jì)算表達(dá)式即可。

  • 確定參數(shù)

綜上,我們所需參數(shù)為ABCD:

let hasVideo      // A: 是否已錄制視頻let hasEffect:    // B: 是否已選有特效let hasSubEffect: // C: 是否帶有子特效let hasSubEffectPhoto // D: 是否已拍照填充了子特效 subEffect

畫出4參數(shù)卡諾圖框架:

圖片

其中AB兩個(gè)變量作為列寫在一起,CD兩個(gè)作為行寫在一起,變量的順序不是一定的,你也可以換著寫,但格雷碼0和1的位置在表頭是固定的,且最終計(jì)算結(jié)果也是一樣的。

AB列中,00代表AB取值 A=false; B=false?,01代表取值 A=false; B=true,依此類推。

于是每個(gè)格子就對(duì)應(yīng)一個(gè)狀態(tài),比如第0行第2列,ABCD取值為0011,代表A=false; B=false; C =true; D=true 這個(gè)狀態(tài)。所以4變量卡諾圖可以把這些變量全排列的16個(gè)狀態(tài)都表示出來,不會(huì)漏掉某個(gè)狀態(tài)。

然后對(duì)于每個(gè)參數(shù)狀態(tài),我們思考目標(biāo)控件的顯示狀態(tài)。這里為方便配合使用屬性 .isHidden?,我們定義邏輯填入1為需要隱藏,0為不隱藏?。注意這里與上面例子2變量里的顯示隱藏定義是反相的。參數(shù)含義與格子對(duì)應(yīng),比如,在0011狀態(tài)時(shí),代表狀態(tài)含義是:

0011 = 無錄視頻,無特效,有子特效,子特效已拍照

本例有個(gè)特殊點(diǎn),因?yàn)樵谛枨筮壿嬌希瑓?shù)B“無特效”比CD有更高優(yōu)先級(jí):即無特效時(shí)不可能有子特效,也不可能給子特效拍照,所以綜合考慮,在狀態(tài)0011時(shí)需要顯示控件,即不隱藏,則在0011對(duì)應(yīng)格子里填入0。

  • 填充卡諾圖

按照上述方法,把全部16個(gè)格子都填上對(duì)應(yīng)(控件需要隱藏)的狀態(tài),AB為列,CD為行:

圖片

  • 按前面 4.2卡諾圈原則 作出全部的卡諾圈,把所有的 1 圈起來:

圖片

  • 化簡

根據(jù)卡諾圈寫出邏輯表達(dá)式并化簡為最小項(xiàng)表達(dá)式(為方便理解,卡諾圈與其表達(dá)式用對(duì)應(yīng)的顏色):

圖片

再把符號(hào)換回代碼中的變量,即有:


// 特效浮層鍵:!A(B+C)+D
P = !hasVideo && (hasEffect || hasSubEffect) || hasSubEffectPhoto

至此,我們就把變量的邏輯狀態(tài)與特效按鍵的狀態(tài)對(duì)應(yīng)起來了。

使用此方法的優(yōu)勢在于:若是需求有變需要更改狀態(tài),則只需要列一遍參數(shù),重新畫卡諾圖計(jì)算表達(dá)式即可,修改的地方也只有這一行的邏輯式,而不會(huì)影響其他控件的邏輯,也不用再走一遍改動(dòng)后的業(yè)務(wù)流程。

6、拓展:五變量卡諾圖

五變量以下最多十六方格,也可用上述方法解出得到。但在實(shí)際開發(fā)中極少需要到5變量,掌握4變量方法已足夠應(yīng)對(duì)大多數(shù)場景。此處僅作拓展了解。六變量以上方格過多,用此方法反倒麻煩[3]。

圖片

圖6.1  五變量卡諾圖

它是由四變量最小項(xiàng)圖構(gòu)成的,將左邊的一個(gè)四變量卡諾圖按軸翻轉(zhuǎn) 180 °而成。左邊的一個(gè)四變量最小項(xiàng)圖對(duì)應(yīng)變量 E =0 ,軸左側(cè)的一個(gè)對(duì)應(yīng) E =1 。

這樣一來除了幾何位置相鄰的小方格滿足鄰接條件外,以軸對(duì)稱的小方格也滿足鄰接條件,這一點(diǎn)需要注意。圖中最小項(xiàng)編號(hào)按變量高低位的順序?yàn)?nbsp;EABCD 排列時(shí),所對(duì)應(yīng)的二進(jìn)制碼確定。

此時(shí)要注意列上變量排列的左右對(duì)稱關(guān)系,對(duì)于既不含 E非也不含 E 的與項(xiàng),可以填入 E非四變量卡諾圖中然后以中間軸翻轉(zhuǎn) 180 °,在 E 四變量卡諾圖中對(duì)稱位置也填上“ 1 ”。

五變量邏輯式化簡的舉例說明如下,每個(gè)步驟原理在上文已作詳盡說明,此處不再贅述:

化簡下式:

圖片

  • 填入卡諾圖:

圖片

  • 作卡諾圈:

圖片

合并與化簡得:

圖片

7、小結(jié)

我們簡要介紹了卡諾圖原理與其化簡方法,這個(gè)工具和思想可以用在任何多參數(shù)有競爭或多種情況的場景下決定某個(gè)操作是否執(zhí)行。這個(gè)方法在參數(shù)較多且狀態(tài)復(fù)雜時(shí)使用,若邏輯狀態(tài)較簡單,則可直接用表達(dá)式寫出,代碼邏輯力求更清晰明了。

在上述場景的操作流程中,任何時(shí)候調(diào)用這個(gè)刷新方法,我們都可獲得符合當(dāng)下參數(shù)的頁面狀態(tài),因?yàn)榻换ゾ椭灰蕾噮?shù)做顯示,而不需要考慮其前置狀態(tài)。

卡諾圖邏輯化簡法優(yōu)點(diǎn)有:簡化了多參數(shù)多控件等復(fù)雜情況下邏輯與方法難以直觀處理的情況,且在后續(xù)維護(hù)迭代中,也可獨(dú)立地修改某個(gè)控件的刷新邏輯,不用擔(dān)心影響其他控件或業(yè)務(wù)流程,每個(gè)交互的控件狀態(tài)修改簡易靈活,方便計(jì)算。

同時(shí),卡諾圖的使用也有其局限性,它比較適合4變量或以下的場景,在5變量及以上用此方法反而復(fù)雜,且在實(shí)際開發(fā)中,4變量場景已然是比較少見,則此法已足夠我們處理大多數(shù)2~4變量的交互邏輯情況了。

Reference

[1] https://blog.shizhuang-inc.com/article/Nzc4Ng==

[2] https://blog.csdn.net/wangqinyi574110/article/details/116573015

[3] https://www.cnblogs.com/uiojhi/p/7517732.html

[4] https://zhuanlan.zhihu.com/p/158535749

[5] https://www.cnblogs.com/iBoundary/p/11303861.html

[6] https://www.pudn.com/news/6367746ba4b7e43a5e898044.html

[7] https://zhuanlan.zhihu.com/p/268750530

責(zé)任編輯:武曉燕 來源: 得物技術(shù)
相關(guān)推薦

2019-08-27 16:23:41

Docker虛擬化虛擬機(jī)

2017-04-11 15:43:39

JavaScript模塊演化

2019-07-17 15:45:47

正則表達(dá)式字符串前端

2024-03-25 13:46:12

C#Lambda編程

2011-06-16 15:28:31

正則表達(dá)式

2024-09-14 09:18:14

Python正則表達(dá)式

2011-07-11 12:33:30

JAVA

2014-01-05 17:41:09

PostgreSQL表達(dá)式

2009-06-10 18:08:14

2009-12-17 10:39:01

Ruby數(shù)學(xué)表達(dá)式

2009-12-15 09:43:50

Ruby case w

2024-09-04 17:35:09

2021-08-31 07:19:41

Lambda表達(dá)式C#

2009-05-05 09:30:01

2009-08-20 16:23:32

C#正則表達(dá)式語法

2009-03-24 08:56:15

正則表達(dá)式格式清理字符串

2009-09-16 16:01:57

PHP正則表達(dá)式正則表達(dá)式的應(yīng)用

2011-11-23 11:04:41

BGPAS_PATH正則表達(dá)式

2012-03-08 13:15:10

JavaStrutsOGNL

2009-08-31 13:32:12

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲国产精品一区 | 人人干人人玩 | 亚洲一区二区精品视频在线观看 | 亚洲高清在线免费观看 | 凹凸日日摸日日碰夜夜 | 在线国产一区二区 | 久操福利| 午夜精品久久久久久久星辰影院 | 一区二区三区精品视频 | 国产精品亚洲精品久久 | 精品九九| 中文字幕国产日韩 | 国产免费一区二区三区 | 欧美一级免费看 | 伊人av在线播放 | 亚洲欧美激情视频 | 成年精品 | 免费网站国产 | 99热这里都是精品 | 精品国产乱码久久久久久影片 | 久久精品亚洲精品国产欧美kt∨ | 久久男女视频 | 色啪网 | 国产精品99视频 | www.奇米| 久久久久久国产免费视网址 | 亚洲精品在线播放 | 欧美a在线| 中国三级黄色录像 | 成人免费毛片片v | 久久天堂 | 亚洲乱码一区二区三区在线观看 | 亚洲电影第1页 | 超碰av免费 | 一区二区三区在线播放 | 欧美综合久久 | 黄色片免费 | 黄色免费av | 久久99精品久久久久久 | 91porn成人精品 | 久久精品二区 |