從陳述式到表達式

更新於 2024/02/26閱讀時間約 5 分鐘

對於程序式編程來說,程式是由一系列的指令組成,例如計算數值、印出訊息、修改變數、呼叫子程序、配置變數的記憶體空間等。定義函式是為了讓一些程序可以重複利用,因此稱為子程序,其中參數為子程序中特別的變數,讓我們能夠透過它們控制子程序的行為。函式的回傳值只是一種方便將結果帶回來的方法,但一般只能回傳一個值,因此早期的c++函式庫常需要預先配置記憶體,再透過參數傳給子程序,讓子程序能夠將結果帶出去。在程式裡面是透過「陳述式」(statement)描述程序應該怎麼執行,其中包含宣告變數、指定變數值、修改變數數值、呼叫子程序等。而為了簡化程式碼,一些計算指令也可以寫成「表達式」(expression),讓多個指令可以縮減成單一陳述式。表達式讓我們不必為每個計算指令的結果設定變數,而是讓表達式本身代表結果。而呼叫函式的指令也可以當作表達式來使用,讓我們可以將更複雜的操作組合起來。但並不是所有的函式都是藉由回傳值帶回結果,很多都是藉由傳入的參考傳回結果,這種函式難以串聯起來,因而仍需要寫成一步一步的陳述式。而且帶有副作用的函式也不適合寫成表達式,因為表達式的執行順序並不明顯,很容易造成奇怪的結果。


對於函數式編程來說,表達式才是描述程式運作的基礎。函數式編程一般只以回傳值帶回結果,如果要回傳多個值就用乘法類型包裝起來,如果要根據狀況回傳不同的值就用加法類型。因此在函數式編程中,大部分的函式都可以透過表達式很容易地串聯起來。函式通常不代表某個程序,它代表的是透過給定的參數計算並組成結果,而且很少會修改參數的值。更抽象地,它還建立了回傳值與參數之間的依賴關係,因此如果我們擔心一個表達式的值受到什麼影響,可以直接找它的參數來判斷。反之,需要寫成陳述式的指令往往會帶有副作用,它會產生隱藏的依賴關係,因此改變陳述式的順序會造成行為的改變。當我們需要共享計算結果時,或是表達式太長需要分段時,也會使用let陳述式定義變數。如果計算過程沒有副作用,這種陳述式可以很容易地重構,例如重新排序、改變變數作用範圍、或是取代此變數成它的定義。因此一個操作有無副作用是很重要的,它會影響我們對於表達式或陳述式的看法,並改變重構程式碼的能力。


對於像是Haskell的純函數式程式語言,變數不可能被修改,也沒有辦法透過呼叫函式執行某種操作,因此根本沒有陳述式的概念,let in並不是陳述式,而且它只能為一個變數綁定值,並不能額外產生其他副作用。通常使用let是為了共用參考,而不是為了執行某種運算。因為沒有副作用,所有的函式都只是通過給定資訊計算出結果,並不能修改變數的值。因為沒有副作用,純函數式程式語言的重構非常輕鬆,這可以讓我們更加專注於內凜的結構,而非實際的語義和目的。非流程控制的陳述式如印出資訊、修改變數等操作則可以用monad實現,Haskell針對monad有特殊的語法糖,使得它寫起來像是陳述式。因為我們必須利用monad才能實現帶有副作用的程序,因此副作用得以明確地與一般的計算分離開來。這種將有無副作用的程序明確分開來看的方法就是純函數式程式語言的特性,同樣的概念也可以帶回一般的程式語言,透過明確說明函式的副作用,可以避免因為未注意的特性產生意外的結果。


因為本來就沒有陳述式,純函數式編程根本沒有一般的跳出流程的方法。它沒有return,函式的身體本身只能是一個代表回傳值的表達式,因此early return的技巧在這裡不管用。如果限定於例外處理的early return,可以用Maybe/Either monad達到相同的效果。return是很特別的陳述式,在這個陳述式之後的程式碼是不會執行的,類似的陳述式是throw等例外處理的機制。另一種說法是它們具有never type,也就是一個永遠不可能回傳的類型。類似throw陳述式的東西在Haskell是throw函式,它會丟出例外並跳出當前的執行程序。這類的函式會回傳Void,代表永遠不可能求值成功。Void可以當作任何類型,畢竟永遠不可能建構這個類型的值,相反的,標榜可以回傳任意類型的函式事實上就是不可能回傳的函式,這是在純函數式程式語言中對於例外的表達方式。


一般的if else判斷式是陳述式,因此這種流程控制方法在Haskell並不存在。Haskell的if then else類似於三元表達式,它會根據狀況切換到其中一個表達式,它們必須都有相同回傳類型。Haskell的條件式流程控制是基於模式匹配,也就是根據加法資料類型的結構控制流程,因此它可以看成是由資料本身決定流程,而非由我們主動決定。例如函式either可以根據Either類型的數值選擇呼叫哪一個函式,這跟case of的作用是一樣的。事實上所有的模式匹配都能寫成這種形式,因此我們根本不需要流程控制的語法。甚至也不需要定義代數資料類型的機制,所有代數特性都可以由特定的函式完整描述,這稱為Scott encoding。這顯示了代數資料類型本身描述的不是資料結構,而是資料的行為,也就是流程的控制。乘法資料結構Tuple可以透過函式uncurry描述,它代表同步的流程;加法資料結構Either可以由either描述,它代表流程的分支;List可由fold描述,它代表迴圈的流程;遞迴的資料結構可由相應的catamorphism描述,它代表遞迴的流程。可以說流程控制在Haskell只是一種特殊的函式,case of/pattern match/if then else只是使用這種特殊函式的語法糖。

avatar-img
4會員
28內容數
這不是教你如何從物件導向到函數式編程的入門教程。我會深入探討物件導向與函數式編程的差異,並討論為什麼你應該使用函數式編程並徹底放棄物件導向。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
have bear的沙龍 的其他內容
函數式編程跟物件導向一個很大的差異在於對資料可變性(mutability)的態度,函數式編程不鼓勵修改原有的資料,有些語言甚至沒有修改的概念;而物件導向專注於狀態的改變,物件作為閉包就已經假設資料是可變的。這種對於可變性的態度注定物件導向比較容易得到關注,因為這個模型比較符合電腦底層的運作邏輯,而我
這個系列的文章主要專注於物件導向到函數式編程的差異與分析,並針對概念與機制上的不同進行比較。很多人說物件導向和函數式編程沒有哪個比較好的問題,只有哪個比較適合的問題,然而我並不這麼認為,我透過這一系列的文章從各個角度討論它們之間的優缺點就是為了闡述我的觀點。物件導向錯在沒有理論基礎,但它贏在熟悉性,
前一篇文章中所提的函數式的三個機制明確說明了它關注的規則與能力具體是什麼。然而這套對於函數式編程的定義主要基於特定的類型系統,作為一個編程範式來說過於狹隘(物件導向的定義也是這樣)。更廣義的,我認為函數式編程主要依循三個原則,它們可以應用於任何程式語言,就算沒有靜態類型系統的支援也可以。例如在不管類
前面談了那麽多函數式編程與物件導向的差異,但我們還沒定義函數式編程。就像物件導向,函數式編程沒有明確的定義,每個人對於什麼是函數式編程都有不同的看法。在這裡我會總結前面的討論,給出我對於函數式編程的觀點。 物件導向注重封裝與延展性,因此一般基於三個機制:繼承、多型、封裝,它們代表了物件導向所重
說到物件導向就必須提五個原則,統稱SOLID,它被認為是物件導向的重要概念。這五個原則並不只適用於物件導向,事實上它很像函數式編程的習慣。它的命名很奇怪且容易讓人混淆,所以我會用我自己的翻譯解釋。 Single Responsibility Principle 是「單一職責原則」,認為一個模組
讓我在這篇文章總結一下前面對物件導向設計的討論,我們討論了物件導向的四個特性:繼承、抽象、多型、封裝,分析了它們的問題,並跟函數式編程的思維做比較。我們引入了與之相對應的特性:泛型、特性系統、模組化,有些特性雖然跟那四個特性很像,但在一些細微的地方有不同的詮釋,使得整體思考方式很不一樣。 「繼
函數式編程跟物件導向一個很大的差異在於對資料可變性(mutability)的態度,函數式編程不鼓勵修改原有的資料,有些語言甚至沒有修改的概念;而物件導向專注於狀態的改變,物件作為閉包就已經假設資料是可變的。這種對於可變性的態度注定物件導向比較容易得到關注,因為這個模型比較符合電腦底層的運作邏輯,而我
這個系列的文章主要專注於物件導向到函數式編程的差異與分析,並針對概念與機制上的不同進行比較。很多人說物件導向和函數式編程沒有哪個比較好的問題,只有哪個比較適合的問題,然而我並不這麼認為,我透過這一系列的文章從各個角度討論它們之間的優缺點就是為了闡述我的觀點。物件導向錯在沒有理論基礎,但它贏在熟悉性,
前一篇文章中所提的函數式的三個機制明確說明了它關注的規則與能力具體是什麼。然而這套對於函數式編程的定義主要基於特定的類型系統,作為一個編程範式來說過於狹隘(物件導向的定義也是這樣)。更廣義的,我認為函數式編程主要依循三個原則,它們可以應用於任何程式語言,就算沒有靜態類型系統的支援也可以。例如在不管類
前面談了那麽多函數式編程與物件導向的差異,但我們還沒定義函數式編程。就像物件導向,函數式編程沒有明確的定義,每個人對於什麼是函數式編程都有不同的看法。在這裡我會總結前面的討論,給出我對於函數式編程的觀點。 物件導向注重封裝與延展性,因此一般基於三個機制:繼承、多型、封裝,它們代表了物件導向所重
說到物件導向就必須提五個原則,統稱SOLID,它被認為是物件導向的重要概念。這五個原則並不只適用於物件導向,事實上它很像函數式編程的習慣。它的命名很奇怪且容易讓人混淆,所以我會用我自己的翻譯解釋。 Single Responsibility Principle 是「單一職責原則」,認為一個模組
讓我在這篇文章總結一下前面對物件導向設計的討論,我們討論了物件導向的四個特性:繼承、抽象、多型、封裝,分析了它們的問題,並跟函數式編程的思維做比較。我們引入了與之相對應的特性:泛型、特性系統、模組化,有些特性雖然跟那四個特性很像,但在一些細微的地方有不同的詮釋,使得整體思考方式很不一樣。 「繼
你可能也想看
Google News 追蹤
Thumbnail
徵的就是你 🫵 超ㄅㄧㄤˋ 獎品搭配超瞎趴的四大主題,等你踹共啦!還有機會獲得經典的「偉士牌樂高」喔!馬上來參加本次的活動吧!
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
這篇內容,將會講解什麼是表達式(Expression),什麼是陳述式(Statement)。有了這些概念,各位會更容易理解,要如何設計程式碼。
Thumbnail
這篇內容,將會講解什麼是函式,以及與函式相關的知識。包括函式的簡介、Runtime Function、自訂函式、Script Function 腳本函式、Method 方法。
主要來講宣告函式跟箭頭函式 : 宣告函式(Function Declaration) 語法: function functionName(parameters) { return result; } 特點: 使用 function 關鍵字 函式名稱是必需的 存在函式
Thumbnail
在這一章中,我們探討了 PHP 中的函數,包括函數的基本結構、不同的函數定義方式(如函數聲明、函數表達式、箭頭函數和匿名函數)以及如何呼叫函數。我們還討論了函數的參數處理方式,包括單個參數、多個參數、預設參數值和剩餘參數。此外,我們還介紹了函數的返回值,包括返回單個值、返回物件和返回函數的情況。
Thumbnail
本章節旨在介紹 C# 中函數的基本結構,包括訪問修飾符、返回類型、方法名稱、參數列表和方法體。同時,也介紹了函數的各種呼叫方式、參數傳遞方式和返回值類型。讀者可以通過本章節,深入理解 C# 中函數的使用和應用。
Thumbnail
C#程式由一或多個檔案組成,包含命名空間、類別、結構、介面、列舉和委派等型別。Main方法是C#應用程式的進入點。在C#中,註解用於在程式碼中添加說明,有單行和多行兩種類型。變數的定義需要指定變數的類型和名稱,可以一次為多個變數賦值。
※ 函式基礎介紹: ※ JavaScript 特殊的函式特性: 函式可以當成值來傳遞 (可以放進變數或放進物件) 函式可以當成函式的參數 callback - 在特定事件中觸發函式 (非同步特性) ※ 函式的基本寫法: ※ 調用 (invoke) 函式: "調用" 意指呼叫或執行
Thumbnail
列出一套完整的程式 程式設計有許多種方法,不過通常會先列出清單的再逐一執行,這樣會加快程式設計的速度。設計通常會採取順推的辦法。所以順推的程式設計方式就是經歷觀念溝通、系統分析、資料統合、權限管理、頻率與時間、後台管理、畫面設計等等階段後,將框架設計完了以後,先列出一套完整的程式,將所有使用者都確
Thumbnail
介紹C++ 語法 資料型態,架構說明 程式語言為人類與電腦溝通的工具 程式設計流程: 定義問題 -> 問題分析 -> 撰寫演算法 ->程式撰寫 -> 程式執行及維護
Thumbnail
徵的就是你 🫵 超ㄅㄧㄤˋ 獎品搭配超瞎趴的四大主題,等你踹共啦!還有機會獲得經典的「偉士牌樂高」喔!馬上來參加本次的活動吧!
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
這篇內容,將會講解什麼是表達式(Expression),什麼是陳述式(Statement)。有了這些概念,各位會更容易理解,要如何設計程式碼。
Thumbnail
這篇內容,將會講解什麼是函式,以及與函式相關的知識。包括函式的簡介、Runtime Function、自訂函式、Script Function 腳本函式、Method 方法。
主要來講宣告函式跟箭頭函式 : 宣告函式(Function Declaration) 語法: function functionName(parameters) { return result; } 特點: 使用 function 關鍵字 函式名稱是必需的 存在函式
Thumbnail
在這一章中,我們探討了 PHP 中的函數,包括函數的基本結構、不同的函數定義方式(如函數聲明、函數表達式、箭頭函數和匿名函數)以及如何呼叫函數。我們還討論了函數的參數處理方式,包括單個參數、多個參數、預設參數值和剩餘參數。此外,我們還介紹了函數的返回值,包括返回單個值、返回物件和返回函數的情況。
Thumbnail
本章節旨在介紹 C# 中函數的基本結構,包括訪問修飾符、返回類型、方法名稱、參數列表和方法體。同時,也介紹了函數的各種呼叫方式、參數傳遞方式和返回值類型。讀者可以通過本章節,深入理解 C# 中函數的使用和應用。
Thumbnail
C#程式由一或多個檔案組成,包含命名空間、類別、結構、介面、列舉和委派等型別。Main方法是C#應用程式的進入點。在C#中,註解用於在程式碼中添加說明,有單行和多行兩種類型。變數的定義需要指定變數的類型和名稱,可以一次為多個變數賦值。
※ 函式基礎介紹: ※ JavaScript 特殊的函式特性: 函式可以當成值來傳遞 (可以放進變數或放進物件) 函式可以當成函式的參數 callback - 在特定事件中觸發函式 (非同步特性) ※ 函式的基本寫法: ※ 調用 (invoke) 函式: "調用" 意指呼叫或執行
Thumbnail
列出一套完整的程式 程式設計有許多種方法,不過通常會先列出清單的再逐一執行,這樣會加快程式設計的速度。設計通常會採取順推的辦法。所以順推的程式設計方式就是經歷觀念溝通、系統分析、資料統合、權限管理、頻率與時間、後台管理、畫面設計等等階段後,將框架設計完了以後,先列出一套完整的程式,將所有使用者都確
Thumbnail
介紹C++ 語法 資料型態,架構說明 程式語言為人類與電腦溝通的工具 程式設計流程: 定義問題 -> 問題分析 -> 撰寫演算法 ->程式撰寫 -> 程式執行及維護