如何開發親戚計算器

這是M小白實驗室第一篇娛樂性科普文章

筆者博客:mwhitelab.com

筆者公衆號:技術雜學鋪

又到了過年的時候,每到這個時候,你總是能見到自己幾乎沒印象但父母就是很熟的親戚。

而且關係凌亂到你自己都說不清。

不信?那就來幾道測試題。

2019年親戚關係測試題正式開始。請完成如下填空。

  1. 爸爸的姐姐的女兒的女兒 = ________
  2. 媽媽的姐姐的弟弟的女兒 = ________
  3. 爺爺的弟弟的女兒的兒子 = ________
  4. 媽媽的媽媽的弟弟的兒子 = ________

參考答案如下:

  1. 表外甥女
  2. 舅表姐/舅表妹
  3. 堂姑表兄/堂姑表弟
  4. 舅表舅父

啥?

由於不知道該如何稱呼,面對陌生的親戚時,我們只能說一句“您好”,附帶一個尷尬又不失禮貌的微笑。

BUT! 作爲21世紀的資深網民(手機重度依賴患者),我們應該使用新時代的技術來解決這個尷尬的局面。那就是使用軟件——親戚關係計算器。

在微信小程序中搜索“親戚計算器”會顯示很多類似的程序。

操作界面和普通的計算器類似,不過數字和加減乘除鍵都變成了“父” “母” “兄” “姐” 這樣的關係。

以後遇見類似有不確定該如何稱呼的親戚,直接這樣用程序就可以了。

再BUT! 作爲21世紀的新型人才(這個詞是我現想的,我也不知道是啥意思),我們不能只能滿足於使用,更應該探求其本質,瞭解親戚關係計算器是如何實現的。

實現方法一:死記硬背法

關係 稱謂
父親的父親 爺爺
父親的母親 奶奶
父親的姐姐 姑媽
父親的妹妹 姑媽
…… ……

該方法嘗試記下所有親戚關係的結果,當用戶想要查詢某一種關係時,直接查表。

假設親戚之間的關係只有父、母、兄、弟、姐、妹、子、女八種。(先不考慮夫妻關係)

那麼像父親的父親,哥哥的女兒這樣簡單的 “XX的XX” 的關係有 8*8 = 64種可能,而 “XX的XX的XX” 有8*8*8 = 512種可能,四個關係的有4096種可能!如此大量的關係,光是把這些可能一個一個地列舉出來就會讓程序員精神崩潰。

然而這樣的死記硬背法是可行的,之前我們看到的微信小程序(其源代碼已公佈在github上)就是用這個方法來實現的:

我們先對關係進行簡化,比如在 ‘我’ 是男性的情況下,那麼‘我的兒子的父親的XX’在正常情況下就可以化簡成 ‘我的XX’。

再比如 母親的丈夫 = 我的父親,兄弟的父母=我的父母,姐姐的姐姐 = 姐姐等等。

接着,我們人爲給出六七百條最常用的關係和其對應的稱謂。任何一條用戶輸入的關係,其化簡後如果在已知的六七百條關係中,我們則返回其對應的稱謂,否則,返回不知道。

實現方法二:網狀圖

在親戚關係中,所有的名詞可以分爲兩類。

一類是稱謂,也就是我們如何稱呼其他人的,如父親、母親、爺爺、姑姑、舅舅等。

另一類是基礎關係,是稱謂的子集,由父親、母親、兒子、女兒、哥哥、弟弟、姐姐、妹妹、丈夫、妻子,這十個詞組成。任何一個稱謂都可以由基礎關係來表達。

如 爺爺 = 父親的父親;姥爺 = 母親的父親;太爺爺 = 父親的父親的父親。

圓角長方形框內爲稱謂,箭頭爲基礎關係(先不考慮丈夫與妻子這兩個關係)。我們可以用如下圖來表示“我”的親戚關係:

我們對圖中的每一個圓角長方形(稱謂)計算其八種關係(父母兒女兄弟姐妹)。

比如,對於上圖,我們接着計算父親和母親的八種關係對應的人,獲得如下圖:

之後對於得到的這張圖,接着計算每個圓角長方形(稱謂)對應的關係, 不斷地畫下去,可以得到一張巨大的網狀圖。

這張網狀圖可以無限延伸(如父親 的 父親 的 父親 的 父親……),我們根據實際情況,將網狀圖擴展到足夠用的地步就行了。

關係網

網狀圖的每一個稱謂和關係都是我們人爲確定了。不過其工作量遠遠小於“死記硬背法”。因爲對於一個經過四個關係的問題,如:父親的兒子的父親的父親=爺爺,母親的母親的女兒的父親=姥爺,一個很小的網狀圖就能確定這個問題的結果。而“死記硬背法”則需要記錄下所有 “XX的XX的XX的XX” 對應的結果才行。

網狀圖如何實現

不懂編程的朋友可跳過本節往下看。

C語言可以使用結構體來表示稱謂,用指針來表示關係(這裏沒有考慮父親的兒子可能是自己,哥哥,弟弟多種可能)。

struct node {
  struct node *father; 
  struct node *mother; 
  struct node *big_bro; 
  struct node *small_bro; 
  struct node *big_sister; 
  struct node *small_sister; 
  struct node *son; 
  struct node *daughter; 
};

python可以使用字典。這裏主要用python寫一個簡單的示意程序,具體代碼見github

1.建立數據庫(該工作量十分龐大,這裏只展示幾個例子):

me = {'f':'父親','m':'母親','bb':'哥哥','sb':'弟弟','bs':'姐姐','ss':'妹妹','son':'兒子','dau':'女兒'}
father = {'f':'爺爺','m':'奶奶','bb':'伯父','sb':'叔叔','bs':'姑媽','ss':'姑媽','son':['我','哥哥','弟弟'],'dau':['我','姐姐','妹妹']}
mother = {'f':'姥爺','m':'姥姥','bb':'大舅','sb':'小舅','bs':'大姨','ss':'小姨','son':['我','哥哥','弟弟'],'dau':['我','姐姐','妹妹']}
…………

2.建立中文名與變量的對應關係:

name2var = {'我':me,'父親':father,'母親':mother,'哥哥':big_bro,\
            '弟弟':small_bro,'姐姐':big_sister,'妹妹':small_sister,\
            '兒子':son,'女兒':daughter}
relation2char =  dict(zip(me.values(), me.keys()))

import numpy as np

# 考慮返回值可能不止一個 如父親的兒子可能爲[‘我’,‘哥哥’,‘弟弟’]
def returnNext(names,relation):
    return_name = []
    for name in names:
        return_name.append(name2var[name][relation2char[relation]])
    return list(set(np.array(return_name).flatten()))

3.使用一個函數,封裝所有操作:

def getName(relation_name):
    relationships = relation_name.split('的')
    name = [relationships[0]]
    for relation in relationships[1:]:
        name = returnNext(name,relation)
    return name

4. 使用:

輸入 輸出
getName(‘我的父親的兒子’) [‘哥哥’, ‘我’, ‘弟弟’]
getName(‘我的母親的女兒’) [‘姐姐’, ‘我’, ‘妹妹’]
getName(‘我的父親的兒子的父親’) [‘父親’]

 

一些細節

首先,是性別:如果‘我’是女性,那麼‘我的父親的兒子’可以爲[‘哥哥’,‘弟弟’],而不可以包含‘我’。(上述代碼沒實現)

另外,關於夫妻關係:在正常情況下,男性稱謂只可以有‘妻子’,女性稱謂只可以有‘丈夫’。(上述代碼沒實現)

第三,多種可能:‘我的父親的兒子’ 可以是[‘我’,‘哥哥’,‘弟弟’],再若是再往後計算,如‘我的父親的兒子的兒子’ ,需要同時考慮‘我的兒子’,‘哥哥的兒子’,‘弟弟的兒子’這三種可能。(上述代碼已實現)

 

最後,給出一個線上的親戚關係計算器。該計算器爲開源代碼,由mumuy開發。

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章