2017-07-26 42 views
1
let f = map tail.lines 
f "fsdaf\nfdsf\n" 

爲什麼它的工作原理?混淆了haskell中的複合函數與地圖

let f = map tail.tail.lines 
f "fasdf\nfasdfdsfd\n" 

我得到的結果:

["asdfdsfd"] 
let f = map (tail.tail).lines 
f "fasdf\nfasdfdsfd\n" 

我得到的結果:

["sdf","sdfdsfd"] 

我想知道哈斯克爾如何解析上面的代碼。

回答

6

讓我們看看你的第一個例子:

let f = map tail.tail.lines 
f "fasdf\nfasdfdsfd\n" 

首先,lines傷了你的輸入字符串轉換成字符串數組:["fasdf","fasdfdsfd"]。現在

,工作從右到左,tail滴列表的「頭」:["fasdfdsfd"]

最後,map tail適用的「尾巴」到列表中的每個元素:["asdfdsfd"]

你的第二個例子工程類似地:

let f = map (tail.tail).lines 
f "fasdf\nfasdfdsfd\n" 

同樣,你打破輸入字符串轉換成一個字符串數組:["fasdf","fasdfdsfd"]

但是,現在您正在創建一個複合函數(tail.tail)(放下列表的「頭部」兩次),並將其映射到列表中的每個元素。

因此,您刪除每個字符串的前兩個字符。

["sdf","sdfdsfd"] 

這兩個示例都按預期工作。閱讀關於haskell的關聯性和複合函數以更多地瞭解它。

編輯:差別基本上是這樣的:

map tail (tail lines) VS map (tail.tail) lines

請記住,在Haskell中,函數是一等公民 - 你可以創建複合功能(例如:(+1).(+1)),並進行其他操作(例如作爲map函數到列表)在其他語言中不常見。

+3

我不得不克服學習Haskell的第一個障礙之一是將空間視爲非常緊密的綁定。在空間是詞分隔符的自然語言環境中,它似乎在做'map(tail.tail.lines)'(因爲函數組合之間沒有空間,它必須更緊密地綁定,對嗎?)但當然這在Haskell中並不是這樣。 –

+0

對不起,我仍然不明白爲什麼兩個輸出不同,我想知道haskell解析代碼的方式。 –

+0

感謝您的幫助。中國有句老話:醍醐灌頂 –

1

第一次發帖是完全正確的,但是因爲你縫有更多的問題,這是我嘗試...

當你試着去了解這段代碼做什麼,瞭解什麼樣的功能做的很重要以及以何種順序它們被應用,所以讓我們來看看:

lines :: String -> [String] --takes a String and returns a list of Strings 
tail :: [a] -> [a] --takes a (nonempty) list and drops the head (a list containing the rest) 
(.) :: (b -> c) -> (a -> b) -> a -> c --function composition 

現在這是很重要...函數組合是右關聯的,具有9的優先級(最高級!)

map :: (a -> b) -> [a] -> [b] --applies (a -> b) to every element of [a] 

現在你的代碼:

let f = map tail.tail.lines 

相當於

let f x = map tail ((tail.lines) $ x) 

這意味着你將映射tail超過lines結果的tail的結果。這種行爲是因爲(.)以及函數應用程序在Haskell中是正確關聯的。值得注意的是,(tail.lines)是部分函數應用的結果(如您從(。)的類型簽名可以看出a缺失...因此它將返回一個函數,該函數需要一個a並返回一個c

在你以後的例子:

let f = map (tail.tail).lines 

應用程序的順序由parenthesizes改變......這個版本相當於:

let f x = map (tail.tail) (lines $ x) 

所以將間對lines的結果執行ap tail.tail(將頭部放下兩次)。

關鍵是理解關聯性。它確定哪個函數首先在沒有加括號的情況下被應用,以及函數是否具有相同的優先級。

我希望這可以幫助您瞭解差異。

+1

小問題:'(。)'是正確的聯想並不重要:即使它是左聯想也沒關係。 – chi