2011-04-01 63 views
10

對於沒有爲這個問題提出一個好的標題,我表示歉意。我在表達我需要的東西時遇到了一些麻煩。在Haskell中我有一個簡單的問題,我想知道最好的解決方法是什麼。Haskell中的函數設計模式

比方說,我有一個數字列表:[-3,2,1,2]。我想返回絕對值最高的值。也就是說,我想返回-3。所以,我想:

f = maximum . map abs 

的問題,當然,這將返回計算值(3),而不是原來的值(-3)。

我可以找出一種方法做到這一點,也許將原始列表映射到(originalValue,calculatdValue)的元組,找到其函數返回snd的元組(最大值),然後返回該元組的fst。

但是,對於這樣一個簡單的問題,這看起來像很多「管道」,我不知道是否有一些我錯過的抽象解決了這個問題。也就是說,這是我一直在做的一般程序,我想要一些整潔的做法:

  1. 我想要一個項目列表。
  2. 我想將它們映射到某個值(比方說絕對值)
  3. 然後我想根據一些標準(假設我想要最大值或最小值)選擇一個值。
  4. 但是接下來我想返回原來的的值。 (如果列表是[-3,2,1,2],我想返回最高絕對值,那麼我會返回-3)。

是否有這樣的庫函數?有沒有一個函子或monad這個?

我想我要與簽名功能:

f :: ([b] -> b) -> (a -> b) -> [a] -> a 

f maximum abs [-3,2,1,2] 

這種感覺很 「functory」 對我來說也許 「一元」。

回答

20

使用帶比較功能的maximumBy。然後你可以傳遞一些比較你想要的功能。

maximumBy (compare `on` abs) 
+7

或等價:'maximumBy(比較ABS)' – interjay 2011-04-01 19:37:37

+3

我更喜歡使用'on',因爲它可以和其他東西一起工作,例如,nubBy((==)''abs')。 – augustss 2011-04-01 19:44:52

+0

謝謝你的解決方案!有一個maximumBy和一個minimumBy。有沒有findFirstBy?我想有很多函數把一個a變成一個b(abs),然後有很多函數把b變成b(最大)。我試圖找到一種方法來編寫它們,而不需要烹飪它。 – 2011-04-01 20:10:43

1

我相信以下內容應該起作用。

foldl abs_max (head xs) xs 
where abs_max x y = if (abs x) > (abs y) then x else y 

從目前的任務來看,您可以通過抽象出比較函數並在稍後傳遞它來概括它。

1

這是我製作的東西。這是一種MEH的,因爲它要求(公式B)

selectOn :: (Eq b) => ([b] -> b) -> (a -> b) -> [a] -> a 
selectOn reducer f list = head $ filter (\x -> f(x) == k) list 
    where k = reducer $ map f list 

然後:

selectOn maximum abs [1,2,-3] 

或者:

selectOn sum id [-3, 0, 3] 

我想我可以概括比較on並得到完全相同的影響。

3

如果你想擁有的東西通過投影有序,相比往常一樣,而不是隻在一個特定的使用(在這種情況下看到augustss的答案),然後用NEWTYPE包裝:

newtype AbsInt = AbsInt Int 

instance Eq AbsInt where 
    AbsInt x == AbsInt y = abs x == abs y 

instance Ord AbsInt where 
    compare (AbsInt x) (AbsInt y) = compare x y 

現在,例如:

maximum [AbsInt 1, AbsInt 10, AbsInt (-50)] = AbsInt (-50) 

想必你將與AbsInt作爲研究對象的工作,這樣你就不會到處寫那些AbsInt秒。

您需要更多的操作AbsInt,您需要更多的樣板。但是,如果你只是想「通過」一些實例,GHC有一個擴展GeneralizedNewtypeDeriving,允許;例如:

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 

newtype AbsInt = AbsInt Int 
    deriving (Num) 

現在AbsInt表現得像Int關於算術,但相對於比較(給出的實例上文)由絕對值。還要注意的是,Num實例爲您提供了使用文字的能力,因此:

(maximum [1,2,-3] :: AbsInt) = AbsInt (-3) 
8

停止... hoogle時間!

所以你有一個東西列表[a]。而你最終只想到其中的一個a。你也想以某種特殊的方式比較這個列表的元素(而不是它們的自然順序),以確定哪個先到達。這是一個棘手的部分,但你應該能夠看到我所描述的是a -> a -> Ordering的功能。

把它放在一起:

(a -> a -> Ordering) -> [a] -> a

而且hoogle itmaximumByminimumBy是第一次訪問:)當您學習使用它時,Hoogle可以是一個強大的資產。(見augustss的答案如何在這種情況下使用maximumBy細節)

6

另一種方式來做到這一點,如果轉換是貴了一點:

maximumWith :: (Ord b) => (a -> b) -> [a] -> a 
maximumWith f = snd . maximumBy (compare `on` fst) . map (f &&& id) 

這種類型類似於GHC.ExtssortWith,這爲我們提供了另一種方式來做到這一點:

maximumWith :: (Ord b) => (a -> b) -> [a] -> a 
maximumWith f = head . sortWith (Down . f) 

我們同樣可以定義一個minimumWith:

minimumWith :: (Ord b) => (a -> b) -> [a] -> a 
minimumWith f = head . sortWith f 

查看sortWith的來源,發現它的實現爲sortBy,所以它缺少第一個定義爲maximumWith的緩存。

module Main where 
import Control.Arrow ((&&&)) 
import Data.List (sortBy) 
import Data.Function (on) 
import GHC.Exts (sortWith) 
import Criterion.Main 

sortWith :: (Ord b) => (a -> b) -> [a] -> [a] 
sortWith f = map snd . sortBy (compare `on` fst) . map (f &&& id) 

badFib :: Int -> Int 
badFib 0 = 1 
badFib 1 = 1 
badFib n = badFib (n - 1) + badFib (n - 2) 

main = defaultMain [ bench "GHC.Exts.sortWith" $ nf (GHC.Exts.sortWith badFib) [0..20] 
        , bench "Main.sortWith" $ nf (Main.sortWith badFib) [0..20] 
        ] 

在我的筆記本結果:

這顯然對一些基準測試要求

benchmarking GHC.Exts.sortWith 
collecting 100 samples, 12 iterations each, in estimated 1.504415 s 
bootstrapping with 100000 resamples 
mean: 1.264608 ms, lb 1.260519 ms, ub 1.270248 ms, ci 0.950 
std dev: 24.42169 us, lb 19.21734 us, ub 31.50275 us, ci 0.950 
found 8 outliers among 100 samples (8.0%) 
    5 (5.0%) high mild 
    3 (3.0%) high severe 
variance introduced by outliers: 0.996% 
variance is unaffected by outliers 

benchmarking Main.sortWith 
collecting 100 samples, 50 iterations each, in estimated 1.516733 s 
bootstrapping with 100000 resamples 
mean: 305.9089 us, lb 304.0602 us, ub 310.9257 us, ci 0.950 
std dev: 14.41005 us, lb 6.680240 us, ub 30.26940 us, ci 0.950 
found 18 outliers among 100 samples (18.0%) 
    9 (9.0%) high mild 
    9 (9.0%) high severe 
variance introduced by outliers: 0.999% 
variance is unaffected by outliers