Scala函數式編程(五) 函數式的錯誤處理

来源:https://www.cnblogs.com/listenfwind/archive/2020/02/20/12337437.html
-Advertisement-
Play Games

前情提要 "Scala函數式編程指南(一) 函數式思想介紹" "scala函數式編程(二) scala基礎語法介紹" "Scala函數式編程(三) scala集合和函數" "Scala函數式編程(四)函數式的數據結構 上" "Scala函數式編程(四)函數式的數據結構 下" 1.面向對象的錯誤處理 ...


前情提要

Scala函數式編程指南(一) 函數式思想介紹

scala函數式編程(二) scala基礎語法介紹

Scala函數式編程(三) scala集合和函數

Scala函數式編程(四)函數式的數據結構 上

Scala函數式編程(四)函數式的數據結構 下

1.面向對象的錯誤處理

在介紹scala的函數式的錯誤處理之前,我們要先來介紹一下其他情況下的錯誤處理方式。

以java為例,常見的錯誤處理方式不外乎兩種,一種是及時捕捉到異常,然後當場進行處理。

try{
    ... 
}catch(Exception e){
    ...
    
}finally{
    
}

另一種則是將異常拋出,層層捕獲,然後在最上層對異常進行統一處理,這種通常是在大型項目的時候會使用。

這兩種錯誤處理的方法是,在我們日常的編程中,已經足以應對多種情況。

但在函數式編程中卻不行,函數式編程追求的是無副作用的代碼,無副作用最直接的應用就是可以放心得併發運行,而拋出異常卻會產生副作用。

try catch處理的弊端,在併發編程中其實有較為明顯的體現。

以spark為例,如果spark主節點master詢問worker節點的健康情況,當worker節點出現異常時,顯然讓master節點來捕獲並處理這個異常,有點不符合情理。

更合理的處理,應該是讓master接收到一個表示錯誤情況的消息,然後再決定接下來如何處理。而worker的異常就讓worker自己去解決吧。

而在scala中,有一種特定的類型,它用來表示可能導致異常的一個計算過程,這就是Try。

2.從Option到Try

前面有介紹過Option,相關介紹可以看這裡Scala函數式編程(三) scala集合和函數

這裡簡單介紹一下Option。

Option呢,其實就是薛定諤的值,裡面可能有值,也可能沒有值。只有到要看的時候,才會知道Option裡面到底有沒有值。

Option全程叫Option[A],表示Option裡面存的是A類型的值,這個A可以是Int,String,等等。我們可以通過get這個api來獲取Option[A]裡面的值,當不存在時,get會返回None。

可以通過isEmpty,來確認Option裡面到底是不是有值。也可以通過getOrElse來指定沒有值的時候要返回什麼值。

Try[A]和Option類似,都是表示一個可能有也可能沒有的東西。實際對應過來, Try[A]就表示一個可能成功也可以失敗的計算,如果成功,則返回A類型,如果失敗,則返回Throwable。

先最在互動式環境中直觀看一下怎麼使用吧:

scala> import scala.util.Try
import scala.util.Try

scala> Try(1+1)
res15: scala.util.Try[Int] = Success(2)

scala> Try(1/0)
res16: scala.util.Try[Int] = Failure(java.lang.ArithmeticException: / by zero)

能夠實現這個功能,主要是因為Try的兩個子類型:

  • Success[A]:代表成功的計算。
  • 封裝了 Throwable 的 Failure[A]:代表出了錯的計算。

是不是和Option很像呢?也是薛定諤的錯誤,在沒打開來看之前,Try裡面可能是成功的,也可能是失敗的。

同樣可以通過isSuccess和isFailure來確認到底這個Try是成功還是失敗。

如果一個函數中有一個計算可能會出錯,那麼我們就可以直讓函數返回Try,然後對成功還是錯誤,就全交由調用者來進行處理,比如上面說到的,Spark的那個例子。

3.Try的使用

上面初步介紹了Try的含義和用法,接下來就來看看Try這個東西,還有哪些常規的用法吧。

3.1 map

map是scala裡面非常常用的一種操作,Try裡面也有!

對Try使用Map的話,會將一個是Success[A]的Try[A]映射到Try[B]會得到Success[B]。如果它是Failure[A],就會得到Failure[B],而且包含的異常和Failure[A]一樣。

看看例子吧:

//新建一個Try,註意,這裡是Try[Int]
scala> val tryMap = Try(1+1)
tryMap: scala.util.Try[Int] = Success(2)

//使用Map,讓它變成Try[String]了
scala> tryMap.map(_.toString)
res46: scala.util.Try[String] = Success(2)

//新建一個會失敗的Try[Int]
scala> val tryMapFail = Try(1 / 0)
tryMapFail: scala.util.Try[Int] = Failure(java.lang.ArithmeticException: / by zero)

//轉換成Try[String]了,但Failure的異常類型不變
scala> tryMapFail.map(_.toString)
res47: scala.util.Try[String] = Failure(java.lang.ArithmeticException: / by zero)

Try不止支持map,還支持for,flatMap,filter等常規操作,從這個角度看,Try反而更像一種數據結構。

3.2 錯誤時候的預設值getOrElse

和Option一樣,Try還很方便得提供了getOrElse這個方法。當你想為失敗的時候做些什麼的時候就可以用這個api。

這個我舉個簡單的例子,將字元串轉換為Int類型。在字元串轉Int類型的時候呢,可能會遇到一些不符合規範的數據。這時候你就不得不考慮數據是否可以安全得轉換成Int,但有了Try,可以很方便得用getOrElse,方法。

當遇到不能轉成Int的字元串,給與一個預設值即可。

scala> import scala.util.Try
import scala.util.Try

scala> "12".toInt
res17: Int = 12

scala> "asd".toInt
java.lang.NumberFormatException: For input string: "asd"
  at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
  at java.lang.Integer.parseInt(Integer.java:580)
  at java.lang.Integer.parseInt(Integer.java:615)
  at scala.collection.immutable.StringLike$class.toInt(StringLike.scala:272)
  at scala.collection.immutable.StringOps.toInt(StringOps.scala:29)
  ... 32 elided

scala> Try("asd".toInt).getOrElse(-1)
res19: Int = -1

但這裡還是得多說一句,這種做法會忽略掉原本應該拋出的錯誤,你需要明確知道自己確實是要忽略掉這個錯誤才能這樣用

否則可能因為設置的預設值導致出現問題,而毫無頭緒,因為程式並沒有報任何錯誤!!

3.3 模式匹配

我們可以不必如java的try catch那般去處理Try失敗時返回的異常。因為我們有scala的模式匹配。

不得不說,模式匹配真的是很強大的一個語言特性。前面不是說到嘛,Try有兩個子類,Success和Failure,成功時候返回Success,失敗時返回Failure。

所以我們就能夠這樣做:

import scala.util.Success
import scala.util.Failure
val operation = Try(1 / 0)
operation match {
  case Success(num) => println(num)
  case Failure(ex) => println(s"Problem is ${ex.getMessage}")
}

因為除數為0,所以這個Try是失敗的,所以這裡會輸出:Problem is / by zero

scala強大的模式匹配,可以方便得讓我們處理錯誤和非錯誤的情況。

4. 小結

Scala 的錯誤處理和其他範式的編程語言有很大的不同。 Try 類型可以讓你將可能會出錯的計算封裝在一個容器里,並優雅的去處理計算得到的值。 並且可以像操作集合和 Option 那樣統一的去操作 Try。

同時Try[A]也支持常見數據結構中的操作,諸如Map,Filter等常規的api都支持。

Try這種錯誤處理的方式,明顯更適用於函數式的情況,也就是說更適合在併發編程的時候使用。

但在我看來,Try也是有一些不好的地方,比如說在代碼可讀性方面就比try catch這種方式差。不得不說,雖然寫起來比較啰嗦,但看著這個結構確實是一目瞭然。

但是不管如何,在我看來,函數式的錯誤處理依舊是很有趣的一個東西。如果合適的話,可以多在代碼中嘗試去使用:)

以上~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 開發環境: Windows操作系統開發工具: Myeclipse+Jdk+Tomcat+MySQL資料庫運行效果圖 源碼及原文鏈接:https://javadao.xyz/forum.php?mod=viewthread&tid=44 ...
  • 原創聲明 本文作者:黃小斜 轉載請務必在文章開頭註明出處和作者。 本文思維導圖 什麼是演算法 上回我們有一篇文章,講述了作為一個新人程式員,如何學習數據結構這門課程,其實呢,數據結構和演算法是息息相關的,為什麼這麼說呢,因為數據結構本身只是一個載體,而在數據結構之上產生作用和輸出價值的東西其實是演算法。 ...
  • 關註公眾號:CoderBuff,回覆“redis”獲取《Redis5.x入門教程》完整版PDF。 《Redis5.x入門教程》目錄 "第一章 · 準備工作" "第二章 · 數據類型" 第三章 · ​命令 第四章 ​· 配置 第五章 · Java客戶端(上) 第六章 · 事務 第七章 · 分散式鎖 第 ...
  • Java基礎 多線程 多個線程一起做同一件事情,縮短時間,提升效率 提高資源利用率 加快程式響應,提升用戶體驗 創建線程 1. 繼承Thread類 步驟 繼承Thread類,重寫run方法 調用的時候,直接new一個對象,然後調start()方法啟動線程 特點 由於是繼承方式,所以不建議使用,因為J ...
  • 1.Object類 equals方法 2.Date類 構造方法 成員方法 DateFormat類 Calendar類 3.System類 StringBuilder原理 構造方法 toString方法 4.包裝類 裝箱&拆箱 自動裝箱&自動拆箱 基本類型和字元串類型的 轉換 5.Collection ...
  • 11、Filter (重點) Filter:過濾器 ,用來過濾網站的數據; 處理中文亂碼 登錄驗證…. Filter開發步驟: 1. 導包 2. 編寫過濾器 1. 導包不要錯 實現Filter介面,重寫對應的方法即可 3. 在web.xml中配置 Filter 12、監聽器 實現一個監聽器的介面;( ...
  • 方法一:直接使用已知的cookie訪問 優點: 簡單,但需要先在瀏覽器登錄 原理: 簡單地說,cookie保存在發起請求的客戶端中,伺服器利用cookie來區分不同的客戶端。因為http是一種無狀態的連接,當伺服器一下子收到好幾個請求時,是無法判斷出哪些請求是同一個客戶端發起的。而“訪問登錄後才能看 ...
  • 簡介: JPA(java Persistence API)和SpringData是兩個範疇的概念。spring data jpa是spring公司下的spring data項目的一個模塊。 spring data jpa定義了介面來進行持久層的編寫規範,同時還大大簡化了持久層的CRUD操作。 從此可 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...