簡體   English   中英

嘗試在Haskell中使用函數組合時出錯

[英]Error when trying to use function composition in Haskell

我剛剛開始學習Haskell,更具體地說是關於函數組合,部分函數,​​映射,過濾器和剖分的主題。 在其中一個練習中,我要求使用函數組合修改twoFilters函數。

我已經閱讀了幾個wiki . 但是我很難讓它正常工作。 據我了解,它通過執行功能b . a b . a按字母順序排列並返回結果。 換句話說, x = foo a ,然后foo b of x 然而,在使用兩個濾波器函數應用了幾個“變化/可能性”之后,由於錯誤,我無法將其編譯。

greaterThanOne :: Int -> Bool
greaterThanOne = (>1)

lessThanTen :: Int -> Bool
lessThanTen = (<10)

twoFilters :: [Int] -> [Int]
twoFilters xs= filter lessThanTen (filter greaterThanOne xs)

這兩個是我最有信心的不成功的嘗試;

twoFilters xs = filter (lessThanTen . greaterThanOne xs)

twoFilters xs = filter (lessThanTen xs . greaterThanOne xs)

我在哪里推理錯了?

你有信心的嘗試是你的邏輯中的一個簡單的失敗:點運算符的工作原理如下:

(fg)(x) = f(g(x))

因此,嘗試計算5的示例給出:

lessThanThen(greaterThanOne(5)) = lessThanTen(True) -- that can't be right, can it???

你想要的是一個lambda和&&:

filter (\\x-> (lessThanThen x) && greaterThanOne(x))

或者,您可以使用兩個過濾器:

filter lessThanTen . filter greaterThanOne $

進入應用函數的精彩世界:

import Control.Applicative

greaterThanOne = (>1)

lessThanTen = (<10)

twoFilters = filter ((&&) <$> greaterThanOne <*> lessThanTen)

twoFilters [1,2,3,4,5,6,7,8,9,10]
-- [2,3,4,5,6,7,8,9]

閱讀了解您的Haskell - Applicative Functors以獲取詳細說明。

你不能像這樣組成這兩個函數。 f . g f . g作用類似於數學中的合成,即相當於f(g(x)) 這意味着外部函數必須采用內部函數返回的類型的參數,在您的情況下,外部函數必須是Bool -> Bool

您可以使用合成運算符編寫twoFilters器,如下所示:

twoFilters = (filter lessThanTen) . (filter greaterThanOne)

(.)期望一個函數接受一個參數並返回一個值,但是你傳遞一個Bool值:

lessThanTen . greaterThanOne xs

這是錯的。

這里:

lessThanTen xs . greaterThanOne xs

你試圖編寫兩個Bool值,但是你應該編寫兩個返回Bool值的函數。

功能應用程序具有最高優先級的一個問題。 所以少了lessThanTen . greaterThanOne xs lessThanTen . greaterThanOne xs嘗試使用greaterThanOne xs的結果組合lessThanTen (這不起作用,該函數適用於整數,而不是其列表)。 同樣, lessThanTen xs. greaterThanOne xs lessThanTen xs. greaterThanOne xs嘗試組合這些函數調用的結果(假設它們首先是有意義的),而不是自己的函數。

另一個問題是誤解了. - (f . g) x等於f (gx) ,即第一個函數的結果是第二個函數的參數。 因此g的類型必須是(a -> b)f的類型必須是(b -> c) (兩個b都是相同的類型變量!)。 您想要將兩個函數應用於同一個參數並使用&&連接結果。 據我所知,目前還沒有現有的功能(至少Hoogle找不到任何東西(a -> Bool) -> (a -> Bool) -> a -> Bool )。 你必須自己做:

both f g x = f x && g x

或者,你只需堅持過濾兩次(由於懶惰的評估,它沒有聽起來那么糟糕) - filter (>1) $ filter (<10) xs

據我了解,它通過執行功能b . a b . a按字母順序排列並返回結果。 換句話說, x = foo a ,然后foo b of x

這可以用Haskell編寫

let x = foo a in 
foo b x

foo來自哪里?)但是正確的

(b . a) x = let y = a x in
            b y

或者,更短:

(b . a) x = b (a x)

現在, filter lessThanTen (filter greaterThanOne xs)具有與此定義右側相似的形狀,如果您還記得可以將其寫為(filter lessThanTen) ((filter greaterThanOne) xs)

((filter lessThanTen) . (filter greaterThanOne)) xs

大概你真正想要的是filter ??? xs filter ??? xs ,但這應該足以繼續下去了。

你幾乎是對的。 我找到了開始學習函數組合的最簡單方法. 是使用$ first代替。

所以你有一個清單

twoFilters xs = xs

您想要通過greaterThanOne進行過濾

twoFilters xs = filter greaterThanOne $ xs

您還想通過lessThanTen過濾

twoFilters xs = filter lessThanTen $ filter greaterThanOne $ xs

現在從左向右移動,替換所有$ s . 除了最后一個$

twoFilters xs = filter lessThanTen . filter greaterThanOne $ xs

您可以使用括號而不是$ now:

twoFilters xs = (filter lessThanTen . filter greaterThanOne) xs

或者只是定義函數pointfree:

twoFilters = filter lessThanTen . filter greaterThanOne

我認為括號內的版本是最重要的。 它表明你融合了兩個部分應用的函數filter lessThanTen並將filter greaterThanOne成一個兆過濾函數,用. 然后將列表應用於它。 你需要將它們括起來,因為. 通過空格綁定比功能應用程序更緊密(空間可以被認為是$的超高固定性版本)。 請記住,當你使用時. ,你將兩個函數融合在一起形成一個巨型函數。

檢查類型簽名是相關的.

(.) :: (b -> c) -> (a -> b) -> a -> c

您提供的功能必須與非常特殊的類型簽名“對齊”(它們必須兼容融合)。 但老實說,關鍵是要學會識別函數應用程序(帶空格)何時綁定比你打算更緊密,並弄亂你想要編寫的函數的類型簽名。 無論如何,這就是我的意思。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM