函式的正確用法

閱讀時間約 7 分鐘

前幾篇文章在討論類型時,只討論了乘法與加法類型,這只是最基礎的類型構造方式,另外還有函式類型和泛型等概念還沒討論。在討論函式的類型之前,必須先討論函式的正確用法。對於程序式編程來說,函式是一段可重複使用的執行代碼,輸入的參數是用來控制執行行為的,因此比起函式(function)更應該稱它為程序(procedure)。或許是在這種觀念下的影響,很多陳舊的函式庫,例如MFC,都會利用參數傳回結果,比較現代的函式庫則比較少這麼做了。對於函數式編程,函式就像是函數,函式的參數代表輸入,回傳值則是輸出。在擁有代數資料類型的支援下,回傳多個值變得非常容易,不再需要透過參數輸出結果。函數式編程也建議少用變數修改(mutation),因此也不應該只為了回傳結果而使用參數傳入參考。


函式最原始的用途是重用程式碼。重用程式碼的目的是將不同地方之中相同的程式邏輯抽取出來,讓我們不用每次都寫一樣的東西。然而在現代應該依據概念定義函式,而非只為了重用程式碼而定義。有時候程式碼一樣不一定代表它們有相同的概念。反之如果它的概念很重要,就算沒有重用的必要,也應該抽取成函式。然而有些人建議程式碼不應超過三層縮排,如果超過了應該考慮用函式抽取出來,我認為這種建議很蠢。把一段程式碼抽取出來會增加程式碼的「深度」,若要知道這個函式是在做什麼,除非它的名稱非常明瞭,否則就必須閱讀它的說明文件,如果沒有就得找原始碼來看。現代編輯器大部分都帶有提示與跳轉功能,因此這麽做並不會造成太多閱讀上的困難。但一般編輯器也會帶有程式碼折疊的功能,只要在複雜的操作前加上註解,再把它折疊起來就能達到相同或更好的效果。縮排層數一般代表流程控制的層數,太多層的流程控制的確會使理解程式邏輯更困難,然而直接拿縮排層數判斷是不準確的,有時我們只是想要控制變數作用範圍而使用區塊,它雖然會增加一層縮排,但反而會減少流程控制複雜度。透過一些技巧,例如及早返回(early return)或是改變條件判斷的順序,可以減少層數,這麽做的確是好的。然而使用物件導向設計時很多人會傾向使用多型把判斷狀態與操作交給多型實現(polymorphism > if/switch),這種做法多了一層抽象,方法不應根據這種狀況而增加。


把程式碼片段抽取成函式時,需要把它使用到的變數轉為參數傳進去,或是實作成物件的方法,讓它自動獲取成員變數的存取權。第一種方法可能會讓參數列表太長,這不是好事;第二種方法依賴於物件導向的支援,而且不適用於關係到區域變數時。如果這些操作牽涉到變數的修改,就更不應該抽取成函式。函式應該要把副作用封裝在內部,否則會增加隱性狀態。另外把它抽取成函式需要把相關的上下文也包含進去,也就是說明這個函式應該在怎樣的情境下使用,否則你的同事可能會誤會它的用途。當你把它抽取成函式之後,如果不是私有函式,就不應該再改變它的定義與使用情境,而這非常不利於修改。如果你沒有遵守這點,當你突然想要修改這部分的一些操作邏輯而修改了這個函式的一點定義,其他使用這個函式的程式碼很有可能就會因此出錯。這種抽取函式的目的本質上就違反了依賴抽象的原則。最好的方法是直接定義在方法內部,如此一來只有這個方法能存取,因此我們也不需說明使用情境或是太過在意是否依賴抽象,這在Haskell很常看到(where clause)。如果你只是為了減少縮排而抽取函式,這種函式的情境通常都非常特定,或是你在寫的時候認為有泛用性,但沒有意識到你利用了當下情境的一些假設。就算你有理解這點,應該也沒有人會給他寫這麽複雜的說明註解,畢竟你只是為了減少縮排層數。


就算他有一個明確的抽象語義,也不一定有抽取成函式的價值,只有當他足夠簡單或是很重要,才應該抽取成函式。過多過於細緻的函式也會增加開發者的負擔,這會讓使用者需要記一堆非常特例的用法與概念,就只為了讀懂一段程式碼。我們不應該擔心縮排層數的問題,我們更應該擔心程式邏輯與概念的可理解性,我們應該盡量避免「概念污染」,過多的概念會增加整個系統的理解難度。設計程式時應該以讓他人能更容易理解而設計(除非你是為了優化效能而設計),因此適度地把一些相近的概念模糊化成一個統一的概念是好的,就像Lua把陣列和字典融合成table一樣(雖然我不認為這麽做是好的)。如果概念就是這麼複雜,應該考慮將這些概念封裝在內部以隱藏複雜度。就像一些排序演算法會用到排序網路的概念,然而使用排序函式時我們不需要理解它是什麼,因為它被封裝在內部了。


函式在函數式編程中是重用程式碼的唯一方法,而物件導向還可以使用繼承「重用」資料結構。一些函數式程式語言也能做到類似繼承資料結構的效果,但一般都是使用組合。繼承重用的是一套運行架構,而不只是一個執行程序。例如Stream類別是一套能進行讀寫的運行架構,我們可以透過它重用讀寫的整套邏輯,例如讀取指定長度的資料,或是Stream的狀態管理。它定義了一套運作規則,覆寫方法時都應遵守這套規則,其他方法會根據你覆寫的方法實現更複雜的操作。這種重用整個架構的方法可以看作是由繼承者提供一套互相關聯的函式,並透過組合這些基礎函式做出更進階的函式,因此那些可覆寫的方法(輸入)跟不可覆寫的方法(輸出)有結構上的不同。這種做法需要以類別作為輸入與輸出函式的載體。在函數式編程中,則是直接提供進階函式,但必須透過trait/typeclass作為輸入基礎函式的載體。它並不像類別把所有輸出函式都搜集起來到一個地方,因此看起來比較鬆散,但你仍然可以自己放到模組或命名空間裡面。這種風格用物件導向的介面也做得到,差別在於它更具彈性,把繼承類別改成實作介面能讓物件可以有更多能力。


定義函式時應該盡量避免帶有副作用(side effect)。在這裡副作用指的是函式會對「環境」造成影響或是被影響,也就是對這個函式的呼叫會執行某種操作,而不只是進行某種計算。常見的副作用包含存取外部變數、印出字串或讀取使用者的輸入、或是與外部設備的互動。副作用會產生隱性的依賴,使得看似無關的程式碼片段隔空互動,這會讓重構程式碼變得困難。就算有辦法明確地判斷函式是否有哪些副作用,也應該盡量避免。除非你明確知道這些函式是怎麼以副作用互動的,一般來說是不可能進一步重構程式碼的。例如當你想要把loop用iterator pattern重構,如果裡面的操作包含大量的變數修改,將會難以將它們分解成個別的map操作。例外機制也是類似的情況。有些程式語言的函式預設會丟出例外,而且還沒有辦法知道它會丟哪些例外。這使得例外處理變得非常麻煩,你必須祈禱說明文件有這部分的描述。就算有也不一定準確,如果它使用你提供的函式(例如comparator),但這個函式丟出了例外,這時會怎麼樣?就算Java要求必須明確地標示函式會丟出哪些例外,也應該少用例外機制。問題在於呼叫函式時預設不會處理例外,從語法上無法區別這時有沒有例外會被拋出,當除錯時發現沒執行到某行程式碼是因為前面呼叫的函式拋出例外,這常常讓人感到被背叛。比較好的做法應該是回傳執行狀態,讓使用者明確地判斷狀態後再進行下一步操作。這種做法比起例外機制繁雜一點,常常需要對每個函式都進行個別例外處理。然而例外機制的「預設正確」的行為就像是null pointer的問題,這只是透過掩蓋問題來簡化邏輯。有些程式語言甚至會濫用例外機制來控制流程,例如Python可以透過StopIteration, GeneratorExit跳出generator的程序,然而這根本不是例外,官方文件也說明它技術上不是例外。例外機制不應被當作能回傳多種狀態的方法而濫用。

4會員
28內容數
這不是教你如何從物件導向到函數式編程的入門教程。我會深入探討物件導向與函數式編程的差異,並討論為什麼你應該使用函數式編程並徹底放棄物件導向。
留言0
查看全部
發表第一個留言支持創作者!
have bear的沙龍 的其他內容
物件導向程式語言的類型系統總是不合理的,這些程式語言承襲至舊的語言不好的特性,而人們並沒有意識到它的問題,或者比起健全(soundness)它們更注重熟悉(familiarity)。多數的物件導向程式語言都源自c/c++,而c++有太多糟糕的設計,然而同一時期出來的Haskell卻很少有類似的問題。
寫上一篇文章時我意識到,類型,類別,型別這幾個詞在物件導向當道的現代變得有些模糊,常常會不小心當成是物件導向的類,但我指的其實是資料類型。在英文中,我常常這樣區分它們:物件導向的類是class,代表的是抽象的物件模型,而類型是type/data type,代表的是實際的資料結構。正如上一篇文章所說,
承接上一篇文章,現代的物件導向已經走偏了,他就像null pointer,很容易出現不好的設計。自從我深入學習函數式編程後,漸漸發現物件導向的不合理的設計,而學習rust之後更讓我開始討厭物件導向,rust幾乎把所有我認為不好的地方都修正了。這個系列的文章我將會一一比較物件導向與rust的差異。這篇
在現代,物件導向雖然仍是主流,函數式慢慢得到關注。物件導向並不適合所有的程式邏輯,但在像是Java的物件導向的程式語言中,幾乎所有值都被當作物件,因此在一些情境下Java寫起來會非常冗余。物件導向流行的原因大概是因為它的思考方式比較符合我們對於世界的認知,但邏輯推理與解決問題的方式卻不一定符合我們的
物件導向程式語言的類型系統總是不合理的,這些程式語言承襲至舊的語言不好的特性,而人們並沒有意識到它的問題,或者比起健全(soundness)它們更注重熟悉(familiarity)。多數的物件導向程式語言都源自c/c++,而c++有太多糟糕的設計,然而同一時期出來的Haskell卻很少有類似的問題。
寫上一篇文章時我意識到,類型,類別,型別這幾個詞在物件導向當道的現代變得有些模糊,常常會不小心當成是物件導向的類,但我指的其實是資料類型。在英文中,我常常這樣區分它們:物件導向的類是class,代表的是抽象的物件模型,而類型是type/data type,代表的是實際的資料結構。正如上一篇文章所說,
承接上一篇文章,現代的物件導向已經走偏了,他就像null pointer,很容易出現不好的設計。自從我深入學習函數式編程後,漸漸發現物件導向的不合理的設計,而學習rust之後更讓我開始討厭物件導向,rust幾乎把所有我認為不好的地方都修正了。這個系列的文章我將會一一比較物件導向與rust的差異。這篇
在現代,物件導向雖然仍是主流,函數式慢慢得到關注。物件導向並不適合所有的程式邏輯,但在像是Java的物件導向的程式語言中,幾乎所有值都被當作物件,因此在一些情境下Java寫起來會非常冗余。物件導向流行的原因大概是因為它的思考方式比較符合我們對於世界的認知,但邏輯推理與解決問題的方式卻不一定符合我們的
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
The Rebel and the Kingdom: The True Story of the Secret Mission to Overthrow the North Korean Regime 我感覺自己唯一跟新聞系還有連結的部分,大約就在於我對報導文學的喜愛,那種「使勁挖」的感覺很吸
Thumbnail
這次韓國音樂劇《拉赫曼尼諾夫》共八名主演,五個組合,陣容十分豪華,演出時間更長達一個禮拜多的時間,我看了10月15日下午14點30的場次,選在有特別謝幕和Photo Day觀賞劇。如果時間跟口袋允許,真的很想每個組合都去看一次。
Thumbnail
對於住在偏鄉地帶的人來說,到北韓邊界可能相對容易。偏遠地區看守人力較為零星。中韓邊界有很大部分沿圖們江和鴨綠江相互毗鄰,冬季會結凍。在更接近城市的地方,邊界兩邊都有人巡守。在北韓這邊,如果被發現越界,會馬上被送到囚營。在中國這邊,則會被立刻送回北韓坐牢。北韓的邊界守衛如果認為哪個人越河太遠,就會朝對
Thumbnail
★財經新聞界最高榮譽羅布獎(Gerald Loeb Award)得獎報導者、普立茲入圍作家最新作 ★暢銷全球《鯨吞億萬》作者,再度跨海追查牽動多國政府的政治內幕 ★橫跨歐、亞、美、非,挖掘蔓延至國際舞台的朝鮮半島政治風雲 ★普立茲得獎作家Kai Bird盛情推薦 一個以擊潰北韓金氏政權為目標的神祕
Thumbnail
《連我都不瞭解自己內心的時候:韓國90萬人的線上心理師,陪你重新理解不安、憂鬱與焦慮,找到痛點,正視內心的求救訊號내 마음을 나도 모를 때》 📝2022年9月的時候,就已經在IG看過@閱讀小姐的書評介紹,直到這個月(都2023年了呢!)才在圖書館的新書區看見它。 這是一本「很好閱讀」的書,就是屬
Thumbnail
這盒融合台東在地盛典的寒單餅,是由台東當地的烘焙坊—歐巴螞研發而成的。歐巴螞秉持選用在地食材的理念,將台東雜糧三寶之一的紅藜、原住民傳統的小米麻糬以及細緻的綠豆沙包裹在香酥的千層酥皮內,打造出獨具特色的寒單餅。
Thumbnail
以法學院殺人案為開端,滾出一整套犯罪網,《Law School》非常優秀!當涉案學生的殺人動機一一被揭露,角色關係逐漸明朗,搭配謎樣且渲染感十足的OST,我整個人像嗑藥(?)一樣興奮到不行!沒想到,《Law School》的主軸不單是「找兇手」這麼簡單而已……
Thumbnail
飾演韓志旼雙胞胎姐姐「英希」的鄭恩惠,是第一個參與韓劇演出的唐氏綜合症演員,在「我們的藍調時光」這部劇中,演的就是唐氏綜合症患者......
Thumbnail
在區廳完成將外國人登錄證號碼添加進婚姻關係證明書以及家庭關係證明書內以後﹐下一個要處理的事情就是﹕申請韓國戶籍謄本內登錄外國人配偶姓名。
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
The Rebel and the Kingdom: The True Story of the Secret Mission to Overthrow the North Korean Regime 我感覺自己唯一跟新聞系還有連結的部分,大約就在於我對報導文學的喜愛,那種「使勁挖」的感覺很吸
Thumbnail
這次韓國音樂劇《拉赫曼尼諾夫》共八名主演,五個組合,陣容十分豪華,演出時間更長達一個禮拜多的時間,我看了10月15日下午14點30的場次,選在有特別謝幕和Photo Day觀賞劇。如果時間跟口袋允許,真的很想每個組合都去看一次。
Thumbnail
對於住在偏鄉地帶的人來說,到北韓邊界可能相對容易。偏遠地區看守人力較為零星。中韓邊界有很大部分沿圖們江和鴨綠江相互毗鄰,冬季會結凍。在更接近城市的地方,邊界兩邊都有人巡守。在北韓這邊,如果被發現越界,會馬上被送到囚營。在中國這邊,則會被立刻送回北韓坐牢。北韓的邊界守衛如果認為哪個人越河太遠,就會朝對
Thumbnail
★財經新聞界最高榮譽羅布獎(Gerald Loeb Award)得獎報導者、普立茲入圍作家最新作 ★暢銷全球《鯨吞億萬》作者,再度跨海追查牽動多國政府的政治內幕 ★橫跨歐、亞、美、非,挖掘蔓延至國際舞台的朝鮮半島政治風雲 ★普立茲得獎作家Kai Bird盛情推薦 一個以擊潰北韓金氏政權為目標的神祕
Thumbnail
《連我都不瞭解自己內心的時候:韓國90萬人的線上心理師,陪你重新理解不安、憂鬱與焦慮,找到痛點,正視內心的求救訊號내 마음을 나도 모를 때》 📝2022年9月的時候,就已經在IG看過@閱讀小姐的書評介紹,直到這個月(都2023年了呢!)才在圖書館的新書區看見它。 這是一本「很好閱讀」的書,就是屬
Thumbnail
這盒融合台東在地盛典的寒單餅,是由台東當地的烘焙坊—歐巴螞研發而成的。歐巴螞秉持選用在地食材的理念,將台東雜糧三寶之一的紅藜、原住民傳統的小米麻糬以及細緻的綠豆沙包裹在香酥的千層酥皮內,打造出獨具特色的寒單餅。
Thumbnail
以法學院殺人案為開端,滾出一整套犯罪網,《Law School》非常優秀!當涉案學生的殺人動機一一被揭露,角色關係逐漸明朗,搭配謎樣且渲染感十足的OST,我整個人像嗑藥(?)一樣興奮到不行!沒想到,《Law School》的主軸不單是「找兇手」這麼簡單而已……
Thumbnail
飾演韓志旼雙胞胎姐姐「英希」的鄭恩惠,是第一個參與韓劇演出的唐氏綜合症演員,在「我們的藍調時光」這部劇中,演的就是唐氏綜合症患者......
Thumbnail
在區廳完成將外國人登錄證號碼添加進婚姻關係證明書以及家庭關係證明書內以後﹐下一個要處理的事情就是﹕申請韓國戶籍謄本內登錄外國人配偶姓名。