Haskell &引用;“同时”;列表的最小值和最大值
此函数返回一个2元组,其中包含列表的最小值和最大值:Haskell &引用;“同时”;列表的最小值和最大值,haskell,Haskell,此函数返回一个2元组,其中包含列表的最小值和最大值: import Control.Arrow ((***), (>>>), (&&&)) import Data.Semigroup (getMin, getMax, Min(..), Max(..)) bounds :: (Bounded a, Ord a) => [a] -> (a,a) bounds = foldMap (Min &&& Max) >>
import Control.Arrow ((***), (>>>), (&&&))
import Data.Semigroup (getMin, getMax, Min(..), Max(..))
bounds :: (Bounded a, Ord a) => [a] -> (a,a)
bounds = foldMap (Min &&& Max) >>> getMin *** getMax
例如:
> x = [1..10 :: Int]
> bounds x
(1,10)
它是否比(最小x,最大x)
更有效
或者有没有比
(最小x,最大x)
更有效的方法?首先,您的两个函数的行为并不相同<当xs
为空列表时,code>(最小xs,最大xs)失效
它是否比(最小x,最大x)
更有效
他们都是O(n),但回答这样的问题的唯一方法是在竞争中对他们进行基准测试。我想我希望foldMap
解决方案会更快,因为它只对列表进行一次遍历,但让我们来看看
import Control.Arrow ((***), (>>>), (&&&))
import Data.Semigroup (getMin, getMax, Min(..), Max(..))
import System.Random
import Criterion.Main
bounds1, bounds2 :: (Bounded a, Ord a) => [a] -> (a,a)
bounds1 = foldMap (Min &&& Max) >>> getMin *** getMax
bounds2 xs = (minimum xs, maximum xs)
randomList :: Int -> IO [Int]
randomList count = take count <$> randoms <$> newStdGen
mkBench n = env (randomList n) $ \list -> bgroup (show n) [
bench "foldMap" $ nf bounds1 list,
bench "minMax" $ nf bounds2 list
]
main = defaultMain $ map mkBench [100, 1000, 10000, 100000, 1000000]
因此,
(最小xs,最大xs)
比折叠映射
的思想要快得多。折叠映射方法使用一个在整个遍历过程中不强制的盒装对。它将分配多于两次传递的方法,并且不允许对输入进行gc'd。失去/失去。@Carl很有趣,谢谢你。你认为明智地使用吗代码>某个地方可以解决性能问题?可能值得尝试使用严格的元组类型,而不是&&&我认为如果不在足够严格的对类型上使用类似于Endo
的东西,您实际上无法从foldMap
中获得良好的行为。[]
的foldMap
定义将以错误的方向关联。您需要重新关联函数传递以获得良好的空间使用。感谢您的基准测试!我知道criteria
,但它在Windows上运行不太好(至少上次我试过时是这样)。有点奇怪的foldl
包提供了一个很好的框架,可以有效地执行各种类似的操作。对于这个简单的例子,另一个选项是foldl'
函数,第三个选项是高阶折叠:bounds xs=foldr go stop xs minBound maxBound where…
。
100/foldMap 1.411 μs
100/minMax 517.6 ns
1000/foldMap 28.94 μs
1000/minMax 5.078 μs
10000/foldMap 488.4 μs
10000/minMax 51.56 μs
100000/foldMap 21.08 ms
100000/minMax 537.3 μs
1000000/foldMap 268.9 ms
1000000/minMax 8.989 ms