從Java和Haskell一些程式碼對比中認識函數語言程式設計 - morgenthum.dev

banq發表於2019-09-20

很多朋友和同事問我為什麼談論Haskell。在我學習Haskell之前,我總是使用Java,C和C ++等主流語言 - 並且仍然喜歡它們。那麼一個命令式的開發人員怎麼會變成Haskell粉絲?在本文中,我想解釋一下 - 特別是對於函式程式設計經驗較少的開發人員。

控制流
控制流程描述瞭如何告訴程式要做什麼 - 制定演算法。有三個基本控制元素:

  • 序列 - 按順序執行程式碼
  • 重複 - 重複執行程式碼
  • 條件 - 按條件將程式碼拆分為分支

物件導向程式設計:
  • 序列是使用逐行逐句的語句實現
  • 重複是使用迴圈實現,如for或while語句或遞迴
  • 條件是使用if ... else或switch陳述

這是一個使用Java的簡單示例,它以文字為中心。文字作為字串陣列傳遞。每一行都是該陣列的一個元素:

void alignCenter(String[] text)
{
    int maxLength = 0;
    for (String line : text) {
        if (line.length() > maxLength) {
            maxLength = line.length();
        }
    }

    for (int i = 0; i < text.length; ++i)
    {
        int spaceCount = (maxLength - text[i].length()) / 2;

        StringBuilder builder = new StringBuilder();
        for (int j = 0; j < spaceCount; ++j)
        {
            builder.append(' ');
        }
        builder.append(text[i]);

        text[i] = builder.toString();
    }
}


函數語言程式設計
  • 序列是使用一鏈串的呼叫實現chained calls
  • 重複是使用遞迴實現
  • 條件是使用模式匹配實現,如case ... of或if ... else表示式

下面是Haskell中的相同示例,它顯示了模式匹配和遞迴的用法:

alignCenter :: [String] -> [String]
alignCenter xs = alignCenter' maxLength xs
    where maxLength = maximum (map length xs)

alignCenter' :: Int -> [String] -> [String]
alignCenter' _ [] = []
alignCenter' n (x:xs) = (replicate spaceCount ' ' ++ x) : alignCenter' n xs
    where spaceCount = div (n - length x) 2


下面是一個簡短的版本,透過使用map和lambda函式避免遞迴:

alignCenter :: [String] -> [String]
alignCenter xs = map (\x -> replicate (div (n - length x) 2) ' ' ++ x) xs
    where n = maximum (map length xs)



函式的第一行是函式簽名:
alignCenter :: [String] -> [String]
這告訴我們,我們有一個名為alignCenter的函式,它將一個字串列表作為輸入,並返回一個新的字串列表作為輸出(從左到右理解)。
然後第二行程式碼中是函式體的內容,第一個函式確定字串列表中的最長行並呼叫第二個函式。我們透過一個簡單的表示式maximum (map length xs)終止了我們的oop程式碼的第一個迴圈。
那麼它是怎樣工作的?我們來看看所有相關maximum (map length xs)函式的簽名:

length :: [a] -> Int
map :: (a -> b) -> [a] -> [b]
maximum :: [a] -> a


length函式獲取任何型別的列表並返回一個Int。所有小寫型別的型別簽名是型別變數,類似於在List<T>Java中的型別T。

map函式需要獲取兩個輸入引數:第一個是型別a -> b;第二個獲取[a]並返回
那麼它是什麼意思“它需要一個函式作為引數”?對,是真的!您可以將函式作為引數傳遞,既不像C中那樣的函式指標也不像Java中那樣引用方法引用 - 實際函式作為第一類值。
將函式作為引數使用或將新函式作為結果返回的函式稱為高階函式。那麼這個功能有什麼作用呢?它將傳遞[a]中每個元素給a -> b函式,在這個函式中,將a轉為b,並且收集collect形成一個新的list

現在讓我們解決型別變數map length xs,其中xs的型別為[String]:

map :: (String -> Int) -> [String] -> [Int]


您需要知道這String是一個型別的同義詞[Char],表示字元列表。這就是為什麼它與length函式相容的原因。表示式map length ["Hello", "World!"]將解析為[5, 6]。(計算這兩個字串的長度),我們感興趣的是列表裡面最長的字串的長度,所以我們傳遞給結果列表maximum返回列表,這是最大的數字6。

更多介紹點選標題見原文。

 

相關文章