Haskell中的Functor设计模式

Ara*_*ian 10 haskell

我为没有提出这个问题的好标题而道歉.我在表达我需要的东西时遇到了一些麻烦.我在Haskell中有一个简单的问题,我想知道解决它的最佳方法是什么.

假设我有一个数字列表:[-3,2,1,2].我想返回具有最高绝对值的值.也就是说,我想返回-3.所以我想:

f = maximum . map abs
Run Code Online (Sandbox Code Playgroud)

当然,问题是返回计算值(3)而不是原始值(-3).

我可以想办法做到这一点,也许将原始列表映射到(originalValue,calculatdValue)元组,找到snd由我的函数返回的元组(最大值),然后返回该元组的fst.

但对于像这样的简单问题,这似乎有很多"管道",我想知道是否有一些我想念的抽象解决了这个问题.也就是说,我一直都会这样做,我想要一些巧妙的方法:

  1. 我想拿一份物品清单.
  2. 我想将它们映射到某个值(让我们说绝对值)
  3. 然后我想根据一些标准选择一个(假设我想要最大值或者最小值).
  4. 但后来我想返回原始值.(如果列表是[-3,2,1,2],我想返回具有最高abs的值,那么我将返回-3).

这有库函数吗?这有一个仿函数或一个monad吗?

我想我想要一个带签名的功能:

f :: ([b] -> b) -> (a -> b) -> [a] -> a
Run Code Online (Sandbox Code Playgroud)

f maximum abs [-3,2,1,2]
Run Code Online (Sandbox Code Playgroud)

这对我来说非常"乐观",或者可能是"monadic".

aug*_*tss 20

使用带有比较功能的maximumBy.然后,您可以传递一些比较您想要的功能.

maximumBy (compare `on` abs)
Run Code Online (Sandbox Code Playgroud)

  • 或者等价地:`maximumBy(比较abs)` (7认同)
  • 我更喜欢使用`on`,因为它适用于其他东西,例如,nubBy((==)`on`abs). (3认同)
  • @Ara:`Data.List`模块中的`elemIndex`函数产生列表中第一个满足给定谓词的元素的索引.如果没有匹配的元素,则返回`Nothing`.这可能是你在谈论`findFirstBy`时所想的. (2认同)

Dan*_*ton 8

停止... hoogle时间!

所以你有一份东西清单[a].而你想最终只有其中一个a.您还希望以某种特殊方式(而不是它们的自然顺序)比较此列表的元素,以确定哪个是第一个.这是棘手的部分,但你应该能够看到我所描述的是形式的一个功能a -> a -> Ordering.

把它们放在一起:

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

它一样.maximumBy并且minimumBy是第一次点击:)当你学习使用它时,Hoogle可以成为一个强大的资产.(有关如何maximumBy在这种情况下使用的详细信息,请参阅augustss的答案)


ram*_*ion 6

另一种方法,如果转换有点贵:

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

这种类型与GHC.Exts's 类似sortWith,这为我们提供了另一种方法:

maximumWith :: (Ord b) => (a -> b) -> [a] -> a
maximumWith f = head . sortWith (Down . f)
Run Code Online (Sandbox Code Playgroud)

我们可以类似地定义一个最小值:

minimumWith :: (Ord b) => (a -> b) -> [a] -> a
minimumWith f = head . sortWith f
Run Code Online (Sandbox Code Playgroud)

看一下源代码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]
                   ]
Run Code Online (Sandbox Code Playgroud)

我的笔记本电脑上的结果:

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
Run Code Online (Sandbox Code Playgroud)