[英]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.