Haskell學習-高階函數

来源:https://www.cnblogs.com/edwardloveyou/archive/2018/08/11/9446619.html
-Advertisement-
Play Games

原文地址: "Haskell學習 高階函數" 高階函數(higher order function)就是指可以操作函數的函數,即函數可以作為參數,也可以作為返回結果。有了這兩個特性,haskell可以實現許多神奇的效果。 柯里化(Currying) 在haskell中所有的算術運算符都是函數(包括大 ...


原文地址:Haskell學習-高階函數
高階函數(higher-order function)就是指可以操作函數的函數,即函數可以作為參數,也可以作為返回結果。有了這兩個特性,haskell可以實現許多神奇的效果。

柯里化(Currying)

在haskell中所有的算術運算符都是函數(包括大小於等於關係符等),而它們的快捷方式都可以省略操作數(參數)。

(+) 1 2 -- (+) 是需要兩個操作數的函數
> 3

(+1) 2 -- (+1) 是需要左操作數的函數
> 3

(3*) 3 -- (3*) 是需要右操作數的函數
> 6

map (*2) [1,2,3] -- map所有元素 *2 的操作
> [2,4,6]

filter (>3) [2,3,4,5] -- 過濾 >3的元素
> [4,5]

haskell中的函數預設都是首碼模式的,也就是:函數名 參數1 參數2 ... 。但幾乎所有擁有兩個參數的函數都有中綴模式,只需要將函數名反引號包起來就可以了:參數1 `函數名` 參數2。因為在某些情況下中綴函數可讀性更好,更符合人們的理解習慣。

5 `div` 3 -- 求餘數
> 1

9 `mod` 7 -- 求模
> 2

'f' `elem` ['a' .. 'z'] -- 是否包含'f'
> True

本質上,Haskell 的所有函數都只有一個參數,那麼我們多個參數的函數又是怎麼回事? 那是因為所有多個參數的函數都是 Curried functions。其實從上面的算術運算函數例子,我們大概就能猜出來了。接著用實例來進驗證一下:

moreThen4 = max 4 -- 最小為4的函數

:t max -- 需要兩個可比較的參數的函數
max :: Ord a => a -> a -> a

:t moreThen4 -- 需要一個可比較的數字的函數
> moreThen4 :: (Ord a, Num a) => a -> a

通過查看函數的類型可發現,兩個參數的 max 函數其實可以寫成 (max x) ymoreThen4 其實就是 max 函數以不全的參數調用後,再創建了一個新的返回函數,該函數是單個參數形式的。
  這和 JavaScript 里用 閉包 的特性返回函數來實現 柯里化 是一樣一樣的。但在函數式語言當中,函數本來就是一等公民,這事情簡直就是和吃飯睡覺一樣地自然而然。
  我們看起來很怪的函數類型描述 Num a => a -> a -> a ,這下也能理解通了。它表示的是函數取一個數字參數a後,會返回一個需要a類型參數的函數 (Num a) => a -> a ,最後的這個函數再取一個參數a後 ,最終就會回傳a類型的結果。
利用柯里化去掉多餘參數後的函數更加簡潔:

sum' xs = foldl (+) 0 xs
sum' = foldl (+) 0  -- 去掉xs後

maxNum x = foldr max 0 x
maxNum = foldr max 0  -- 去掉x後

Lambda表達式

  lambda 已經不是什麼新鮮事物了, 早在 .NET 4.0時代 C# 就已經引入了 lambdaJavaScript 也在 ES6 中引入。
  編寫匿名的函數,這樣就不需要費力的創建命名函數。因為匿名函數從 lambda 演算而來,所以匿名函數通常也被稱為 lambda 函數。
  在 Haskell 中,匿名函數以反斜杠符號  開始,後跟函數的參數(可以包含模式),而函數體定義在 -> 符號之後。lambda 函數的定義只能有一條語句,同時無法為一個參數設置多個模式,如 [] 和 (x:xs)。

plusOne = \x -> x+1

checkZero = \x -> if x > 0 then "大於0" 
    else if x<0 then "小於0" 
    else "等於0"

摺疊函數

  遍歷列表是一個非常普遍的需求,用摺疊函數代替顯式遞歸進行遍歷明顯更加易於理解和實現。其中 foldl 是左結合,foldr 是右結合,一般右摺疊效率比較高,同時 foldr 也可以用於無限列表,所以應儘量使用 foldr
  摺疊函數調用格式: fold 處理函數 初始值(累加值) 需要摺疊的列表
  另外還提供了和 foldl/foldr 相似的 foldl1/foldr1,它們預設使用列表第一項為初始值,所以可以省略初始值。

map' :: Foldable t1 => (t2 -> a) -> t1 t2 -> [a]
map' f = foldr (\x acc -> f x:acc) []

filter' :: Foldable t => (a -> Bool) -> t a -> [a]
filter' f = foldr (\x acc -> if f x then x:acc else acc) []

elem' :: (Foldable t, Eq a) => a -> t a -> Bool
elem' y = foldl (\acc x -> if y==x then True else acc) False

and' :: Foldable t => t Bool -> Bool
and' = foldr1 (\x y->if not y then False else if not x then False else True)

-- 執行
map' (*2) [1,2]
> [2,4]

filter (>2) [1,2,3,4]
> [3,4]

elem' 1 [1,2,3]
> True

and' [True,False,True]
> False

與 foldl 和 foldr 相似的scanlscanr,它們會記錄下累加值的所有狀態到一個 List。
也有 scanl1scanr1

scanl (+) 0 [3,5,2,1]  
> [0,3,8,10,11]  

scanr (+) 0 [3,5,2,1]  
> [11,8,3,1,0]  

還有 foldl'foldl1' 是它們各自惰性實現的嚴格版本。在用 fold 處理較大的 List 時,經常會遇到堆棧溢出的問題。而這罪魁禍首就是 fold 的惰性: 在執行 fold 時,累加器的值並不會被立即更新,而是做一個"在必要時會取得所需的結果"的承諾。每過一遍累加器,這一行為就重覆一次。而所有的這堆"承諾"最終就會塞滿你的堆棧。嚴格的 fold 就不會有這一問題,它們不會作"承諾",而是直接計算中間值的結果並繼續執行下去。如果用惰性 fold 時經常遇到溢出錯誤,就應換用它們的嚴格版。

函數組合

$) 叫作函數呼叫符,它的優先順序最低。

 f $ g x => f (g x)

-- 取>2的列表長度
length (filter (>2) [1,2,3,4])
length $ filter (>2) [1,2,3,4] -- 降低優先順序消除括弧
> 2

(.) 函數複合運算符,它可以組合函數,並產生新函數,然後傳遞給其它函數。當然我們可以用 lambda 實現,但大多數情況下,使用函數組合無疑更清楚。

(f . g) x => f(g x) 

-- 驗證字元串是否為數字
not ( and ( map isDigit $ "12as"))
not . and . map isDigit $ "12as" -- 使用組合消除括弧
> True

這兩個運算符是消除括弧的神器,有了它們,代碼的可讀性大大提高。
我們再利用haskell強大的模式匹配能力,改變函數運行方向,改造後的效果類似於unix/linux的管道,把上面兩個表達式重寫。現在連 ($) (.) 都不需要了,弔炸天了,有木有

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

-Advertisement-
Play Games
更多相關文章
  • 一、使用文檔自帶的原生API rich-text, nodes屬性直接綁定需要渲染的html內容即可,文檔參見這裡:https://developers.weixin.qq.com/miniprogram/dev/component/rich-text.html 二、使用WxParseData插件, ...
  • 如何實現進度條效果呢 ? 效果:點擊頁面的某個按鈕,彈出一個進度條,然後實時顯示進度,直到任務完成。 思路:頁面裡面有個隱藏的進度條,點擊按鈕後彈出。ajax迴圈請求數據,直到全部完成 難點:ajax的同步請求問題 1、首先引入頁面樣式: 2、頁面 進度條 HTML 元素 3、JS 實現 定義全局的 ...
  • 規則就是,調用函數,放兩個參數,第一個參數,是設計稿的寬度,第二個參數是px與rem的轉換比例,通常會寫100(因為好算);當然了,要把這段js代碼最好封裝在一個單獨的js文件里,並且放在所有的css文件引入之前載入。 其中 var n=t.clientWidth||320;n>720&&(n=72 ...
  • 初學前端js經常搞不清楚null與undefined的區別,他們是js中的原始數據類型 1、undefined數據類型只有一個值undefined,當聲明的變數未初始化時,變數的預設值是undefined. 2、null也只有一個值null,用來表示尚未存在的對象,常用來表示函數企圖返回一個不存在的 ...
  • obj =["34", "3", "34#add"] 數組,id 與樹形的所有id 一致 ...
  • 1、DOM操作: DOM操作分為三類: ●DOM Core:任何一種支持DOM的編程語言都可以使用它,如getElementById()、getElementsByName; ●HTML-DOM:用於處理HTML文檔,如document.forms; ●CSS-DOM:用於操作CSS(獲取和設置st ...
  • MVC框架 介紹: MVC全名Model View Controller Model:模型的意思,代表業務模型 View:視圖的意思,代表用戶界面 Controller:控制器的意思,控制器接受用戶的輸入並調用模型和視圖去完成用戶的需求。 MVC把各個層次需要關註的內容分離了開來。 MVC將負責顯示 ...
  • 在較大規模的業務系統中經常會有這樣的模塊,它按照一定的業務流程調用其它模塊來實現一定的業務邏輯,我們姑且稱之為流程引擎。這裡稱之為引擎有兩層含義,一、突顯其在業務系統的核心重要位置。二、它又是複雜不好維護的,通常由資深程式員把持。這樣的引擎不僅代碼繁多,與各個模塊的介面複雜,並且一定程度對外是不透明... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...